pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
utils.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #ifndef _GNU_SOURCE
13 # define _GNU_SOURCE
14 #endif
15 
16 #include <sys/stat.h>
17 #include <sys/utsname.h>
18 
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include <pwd.h>
25 #include <time.h>
26 #include <libgen.h>
27 #include <signal.h>
28 #include <grp.h>
29 
30 #include <qb/qbdefs.h>
31 
32 #include <crm/crm.h>
33 #include <crm/services.h>
34 #include <crm/cib/internal.h>
35 #include <crm/common/xml.h>
36 #include <crm/common/util.h>
37 #include <crm/common/ipc.h>
38 #include <crm/common/iso8601.h>
39 #include <crm/common/mainloop.h>
40 #include <libxml2/libxml/relaxng.h>
41 
42 #include "crmcommon_private.h"
43 
44 CRM_TRACE_INIT_DATA(common);
45 
46 gboolean crm_config_error = FALSE;
47 gboolean crm_config_warning = FALSE;
48 char *crm_system_name = NULL;
49 
50 bool
51 pcmk__is_user_in_group(const char *user, const char *group)
52 {
53  struct group *grent;
54  char **gr_mem;
55 
56  if (user == NULL || group == NULL) {
57  return false;
58  }
59 
60  setgrent();
61  while ((grent = getgrent()) != NULL) {
62  if (grent->gr_mem == NULL) {
63  continue;
64  }
65 
66  if(strcmp(group, grent->gr_name) != 0) {
67  continue;
68  }
69 
70  gr_mem = grent->gr_mem;
71  while (*gr_mem != NULL) {
72  if (!strcmp(user, *gr_mem++)) {
73  endgrent();
74  return true;
75  }
76  }
77  }
78  endgrent();
79  return false;
80 }
81 
82 int
83 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
84 {
85  int rc = pcmk_ok;
86  char *buffer = NULL;
87  struct passwd pwd;
88  struct passwd *pwentry = NULL;
89 
90  buffer = calloc(1, PCMK__PW_BUFFER_LEN);
91  if (buffer == NULL) {
92  return -ENOMEM;
93  }
94 
95  rc = getpwnam_r(name, &pwd, buffer, PCMK__PW_BUFFER_LEN, &pwentry);
96  if (pwentry) {
97  if (uid) {
98  *uid = pwentry->pw_uid;
99  }
100  if (gid) {
101  *gid = pwentry->pw_gid;
102  }
103  crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
104 
105  } else {
106  rc = rc? -rc : -EINVAL;
107  crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
108  }
109 
110  free(buffer);
111  return rc;
112 }
113 
122 int
123 pcmk_daemon_user(uid_t *uid, gid_t *gid)
124 {
125  static uid_t daemon_uid;
126  static gid_t daemon_gid;
127  static bool found = false;
128  int rc = pcmk_ok;
129 
130  if (!found) {
131  rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
132  if (rc == pcmk_ok) {
133  found = true;
134  }
135  }
136  if (found) {
137  if (uid) {
138  *uid = daemon_uid;
139  }
140  if (gid) {
141  *gid = daemon_gid;
142  }
143  }
144  return rc;
145 }
146 
154 static int
155 version_helper(const char *text, const char **end_text)
156 {
157  int atoi_result = -1;
158 
159  pcmk__assert(end_text != NULL);
160 
161  errno = 0;
162 
163  if (text != NULL && text[0] != 0) {
164  /* seemingly sacrificing const-correctness -- because while strtol
165  doesn't modify the input, it doesn't want to artificially taint the
166  "end_text" pointer-to-pointer-to-first-char-in-string with constness
167  in case the input wasn't actually constant -- by semantic definition
168  not a single character will get modified so it shall be perfectly
169  safe to make compiler happy with dropping "const" qualifier here */
170  atoi_result = (int) strtol(text, (char **) end_text, 10);
171 
172  if (errno == EINVAL) {
173  crm_err("Conversion of '%s' %c failed", text, text[0]);
174  atoi_result = -1;
175  }
176  }
177  return atoi_result;
178 }
179 
180 /*
181  * version1 < version2 : -1
182  * version1 = version2 : 0
183  * version1 > version2 : 1
184  */
185 int
186 compare_version(const char *version1, const char *version2)
187 {
188  int rc = 0;
189  int lpc = 0;
190  const char *ver1_iter, *ver2_iter;
191 
192  if (version1 == NULL && version2 == NULL) {
193  return 0;
194  } else if (version1 == NULL) {
195  return -1;
196  } else if (version2 == NULL) {
197  return 1;
198  }
199 
200  ver1_iter = version1;
201  ver2_iter = version2;
202 
203  while (1) {
204  int digit1 = 0;
205  int digit2 = 0;
206 
207  lpc++;
208 
209  if (ver1_iter == ver2_iter) {
210  break;
211  }
212 
213  if (ver1_iter != NULL) {
214  digit1 = version_helper(ver1_iter, &ver1_iter);
215  }
216 
217  if (ver2_iter != NULL) {
218  digit2 = version_helper(ver2_iter, &ver2_iter);
219  }
220 
221  if (digit1 < digit2) {
222  rc = -1;
223  break;
224 
225  } else if (digit1 > digit2) {
226  rc = 1;
227  break;
228  }
229 
230  if (ver1_iter != NULL && *ver1_iter == '.') {
231  ver1_iter++;
232  }
233  if (ver1_iter != NULL && *ver1_iter == '\0') {
234  ver1_iter = NULL;
235  }
236 
237  if (ver2_iter != NULL && *ver2_iter == '.') {
238  ver2_iter++;
239  }
240  if (ver2_iter != NULL && *ver2_iter == 0) {
241  ver2_iter = NULL;
242  }
243  }
244 
245  if (rc == 0) {
246  crm_trace("%s == %s (%d)", version1, version2, lpc);
247  } else if (rc < 0) {
248  crm_trace("%s < %s (%d)", version1, version2, lpc);
249  } else if (rc > 0) {
250  crm_trace("%s > %s (%d)", version1, version2, lpc);
251  }
252 
253  return rc;
254 }
255 
267 void
268 pcmk__daemonize(const char *name, const char *pidfile)
269 {
270  int rc;
271  pid_t pid;
272 
273  /* Check before we even try... */
274  rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
275  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
276  crm_err("%s: already running [pid %lld in %s]",
277  name, (long long) pid, pidfile);
278  printf("%s: already running [pid %lld in %s]\n",
279  name, (long long) pid, pidfile);
281  }
282 
283  pid = fork();
284  if (pid < 0) {
285  fprintf(stderr, "%s: could not start daemon\n", name);
286  crm_perror(LOG_ERR, "fork");
288 
289  } else if (pid > 0) {
291  }
292 
293  rc = pcmk__lock_pidfile(pidfile, name);
294  if (rc != pcmk_rc_ok) {
295  crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
296  pidfile, name, pcmk_rc_str(rc), rc);
297  printf("Could not lock '%s' for %s: %s (%d)\n",
298  pidfile, name, pcmk_rc_str(rc), rc);
300  }
301 
302  umask(S_IWGRP | S_IWOTH | S_IROTH);
303 
304  close(STDIN_FILENO);
305  pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
306 
307  close(STDOUT_FILENO);
308  pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
309 
310  close(STDERR_FILENO);
311  pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
312 }
313 
314 #ifdef HAVE_UUID_UUID_H
315 # include <uuid/uuid.h>
316 #endif
317 
318 char *
320 {
321  unsigned char uuid[16];
322  char *buffer = malloc(37); /* Including NUL byte */
323 
324  pcmk__mem_assert(buffer);
325  uuid_generate(uuid);
326  uuid_unparse(uuid, buffer);
327  return buffer;
328 }
329 
330 #ifdef HAVE_GNUTLS_GNUTLS_H
331 void
332 crm_gnutls_global_init(void)
333 {
334  signal(SIGPIPE, SIG_IGN);
335  gnutls_global_init();
336 }
337 #endif
338 
339 bool
340 pcmk_str_is_infinity(const char *s) {
342  NULL);
343 }
344 
345 bool
347  return pcmk__str_eq(s, PCMK_VALUE_MINUS_INFINITY, pcmk__str_none);
348 }
349 
358 void
359 pcmk__sleep_ms(unsigned int ms)
360 {
361  // @TODO Impose a sane maximum sleep to avoid hanging a process for long
362  //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
363 
364  // Use sleep() for any whole seconds
365  if (ms >= 1000) {
366  sleep(ms / 1000);
367  ms -= ms / 1000;
368  }
369 
370  if (ms == 0) {
371  return;
372  }
373 
374 #if defined(HAVE_NANOSLEEP)
375  // nanosleep() is POSIX-2008, so prefer that
376  {
377  struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
378 
379  nanosleep(&req, NULL);
380  }
381 #elif defined(HAVE_USLEEP)
382  // usleep() is widely available, though considered obsolete
383  usleep((useconds_t) ms);
384 #else
385  // Otherwise use a trick with select() timeout
386  {
387  struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
388 
389  select(0, NULL, NULL, NULL, &tv);
390  }
391 #endif
392 }
393 
394 // Deprecated functions kept only for backward API compatibility
395 // LCOV_EXCL_START
396 
397 #include <crm/common/util_compat.h>
398 
399 guint
401 {
402  long long msec = -1;
403 
404  errno = 0;
405  if (input == NULL) {
406  return 0;
407 
408  } else if (input[0] == 'P') {
410 
411  if (period_s) {
412  msec = 1000 * crm_time_get_seconds(period_s);
413  crm_time_free(period_s);
414  }
415 
416  } else {
417  msec = crm_get_msec(input);
418  }
419 
420  if (msec < 0) {
421  crm_warn("Using 0 instead of '%s'", input);
422  errno = EINVAL;
423  return 0;
424  }
425  return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
426 }
427 
428 char *
430 {
431  struct utsname hostinfo;
432 
433  return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
434 }
435 
436 // LCOV_EXCL_STOP
437 // End deprecated API
Services API.
A dumping ground.
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:83
const char * pcmk_strerror(int rc)
Definition: results.c:151
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:938
#define PCMK_VALUE_MINUS_INFINITY
Definition: options.h:173
#define PCMK_VALUE_INFINITY
Definition: options.h:163
const char * name
Definition: cib.c:26
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:346
struct crm_time_s crm_time_t
Definition: iso8601.h:32
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:361
gboolean crm_config_warning
Definition: utils.c:47
Unspecified error.
Definition: results.h:252
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:340
crm_time_t * crm_time_parse_duration(const char *duration_str)
Parse a time duration from an ISO 8601 duration specification.
Definition: iso8601.c:1121
#define PCMK_VALUE_PLUS_INFINITY
Definition: options.h:192
void pcmk__daemonize(const char *name, const char *pidfile)
Definition: utils.c:268
char * crm_system_name
Definition: utils.c:48
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:503
Wrappers for and extensions to glib mainloop.
#define crm_warn(fmt, args...)
Definition: logging.h:394
uint32_t pid
Definition: cpg.c:49
guint crm_parse_interval_spec(const char *input)
Definition: utils.c:400
Utility functions.
void pcmk__sleep_ms(unsigned int ms)
Definition: utils.c:359
External (OS/environmental) problem.
Definition: results.h:272
#define crm_trace(fmt, args...)
Definition: logging.h:404
Wrappers for and extensions to libxml2.
ISO_8601 Date handling.
#define CRM_DAEMON_USER
Definition: config.h:30
Success.
Definition: results.h:251
char * crm_generate_uuid(void)
Definition: utils.c:319
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1062
long long crm_time_get_seconds(const crm_time_t *dt)
Definition: iso8601.c:331
CRM_TRACE_INIT_DATA(common)
char * pcmk_hostname(void)
Definition: utils.c:429
int compare_version(const char *version1, const char *version2)
Definition: utils.c:186
#define pcmk__assert(expr)
#define CRM_XS
Definition: logging.h:56
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition: pid.c:214
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:331
#define crm_err(fmt, args...)
Definition: logging.h:391
Deprecated Pacemaker utilities.
xmlNode * input
#define pcmk__mem_assert(ptr)
char uname[MAX_NAME]
Definition: cpg.c:53
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition: pid.c:172
#define PCMK__PW_BUFFER_LEN
#define pcmk_ok
Definition: results.h:65
IPC interface to Pacemaker daemons.
gboolean crm_config_error
Definition: utils.c:46
bool pcmk__is_user_in_group(const char *user, const char *group)
Definition: utils.c:51
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition: utils.c:123
#define crm_info(fmt, args...)
Definition: logging.h:399
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150