pacemaker  2.1.8-3980678f03
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/types.h>
17 #include <sys/wait.h>
18 #include <sys/stat.h>
19 #include <sys/utsname.h>
20 
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <limits.h>
26 #include <pwd.h>
27 #include <time.h>
28 #include <libgen.h>
29 #include <signal.h>
30 #include <grp.h>
31 
32 #include <qb/qbdefs.h>
33 
34 #include <crm/crm.h>
35 #include <crm/services.h>
36 #include <crm/cib/internal.h>
37 #include <crm/common/xml.h>
38 #include <crm/common/util.h>
39 #include <crm/common/ipc.h>
40 #include <crm/common/iso8601.h>
41 #include <crm/common/mainloop.h>
42 #include <libxml2/libxml/relaxng.h>
43 
44 #include "crmcommon_private.h"
45 
46 CRM_TRACE_INIT_DATA(common);
47 
48 gboolean crm_config_error = FALSE;
49 gboolean crm_config_warning = FALSE;
50 char *crm_system_name = NULL;
51 
52 bool
53 pcmk__is_user_in_group(const char *user, const char *group)
54 {
55  struct group *grent;
56  char **gr_mem;
57 
58  if (user == NULL || group == NULL) {
59  return false;
60  }
61 
62  setgrent();
63  while ((grent = getgrent()) != NULL) {
64  if (grent->gr_mem == NULL) {
65  continue;
66  }
67 
68  if(strcmp(group, grent->gr_name) != 0) {
69  continue;
70  }
71 
72  gr_mem = grent->gr_mem;
73  while (*gr_mem != NULL) {
74  if (!strcmp(user, *gr_mem++)) {
75  endgrent();
76  return true;
77  }
78  }
79  }
80  endgrent();
81  return false;
82 }
83 
84 int
85 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
86 {
87  int rc = pcmk_ok;
88  char *buffer = NULL;
89  struct passwd pwd;
90  struct passwd *pwentry = NULL;
91 
92  buffer = calloc(1, PCMK__PW_BUFFER_LEN);
93  if (buffer == NULL) {
94  return -ENOMEM;
95  }
96 
97  rc = getpwnam_r(name, &pwd, buffer, PCMK__PW_BUFFER_LEN, &pwentry);
98  if (pwentry) {
99  if (uid) {
100  *uid = pwentry->pw_uid;
101  }
102  if (gid) {
103  *gid = pwentry->pw_gid;
104  }
105  crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
106 
107  } else {
108  rc = rc? -rc : -EINVAL;
109  crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
110  }
111 
112  free(buffer);
113  return rc;
114 }
115 
124 int
125 pcmk_daemon_user(uid_t *uid, gid_t *gid)
126 {
127  static uid_t daemon_uid;
128  static gid_t daemon_gid;
129  static bool found = false;
130  int rc = pcmk_ok;
131 
132  if (!found) {
133  rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
134  if (rc == pcmk_ok) {
135  found = true;
136  }
137  }
138  if (found) {
139  if (uid) {
140  *uid = daemon_uid;
141  }
142  if (gid) {
143  *gid = daemon_gid;
144  }
145  }
146  return rc;
147 }
148 
156 static int
157 version_helper(const char *text, const char **end_text)
158 {
159  int atoi_result = -1;
160 
161  CRM_ASSERT(end_text != NULL);
162 
163  errno = 0;
164 
165  if (text != NULL && text[0] != 0) {
166  /* seemingly sacrificing const-correctness -- because while strtol
167  doesn't modify the input, it doesn't want to artificially taint the
168  "end_text" pointer-to-pointer-to-first-char-in-string with constness
169  in case the input wasn't actually constant -- by semantic definition
170  not a single character will get modified so it shall be perfectly
171  safe to make compiler happy with dropping "const" qualifier here */
172  atoi_result = (int) strtol(text, (char **) end_text, 10);
173 
174  if (errno == EINVAL) {
175  crm_err("Conversion of '%s' %c failed", text, text[0]);
176  atoi_result = -1;
177  }
178  }
179  return atoi_result;
180 }
181 
182 /*
183  * version1 < version2 : -1
184  * version1 = version2 : 0
185  * version1 > version2 : 1
186  */
187 int
188 compare_version(const char *version1, const char *version2)
189 {
190  int rc = 0;
191  int lpc = 0;
192  const char *ver1_iter, *ver2_iter;
193 
194  if (version1 == NULL && version2 == NULL) {
195  return 0;
196  } else if (version1 == NULL) {
197  return -1;
198  } else if (version2 == NULL) {
199  return 1;
200  }
201 
202  ver1_iter = version1;
203  ver2_iter = version2;
204 
205  while (1) {
206  int digit1 = 0;
207  int digit2 = 0;
208 
209  lpc++;
210 
211  if (ver1_iter == ver2_iter) {
212  break;
213  }
214 
215  if (ver1_iter != NULL) {
216  digit1 = version_helper(ver1_iter, &ver1_iter);
217  }
218 
219  if (ver2_iter != NULL) {
220  digit2 = version_helper(ver2_iter, &ver2_iter);
221  }
222 
223  if (digit1 < digit2) {
224  rc = -1;
225  break;
226 
227  } else if (digit1 > digit2) {
228  rc = 1;
229  break;
230  }
231 
232  if (ver1_iter != NULL && *ver1_iter == '.') {
233  ver1_iter++;
234  }
235  if (ver1_iter != NULL && *ver1_iter == '\0') {
236  ver1_iter = NULL;
237  }
238 
239  if (ver2_iter != NULL && *ver2_iter == '.') {
240  ver2_iter++;
241  }
242  if (ver2_iter != NULL && *ver2_iter == 0) {
243  ver2_iter = NULL;
244  }
245  }
246 
247  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  } else if (rc > 0) {
252  crm_trace("%s > %s (%d)", version1, version2, lpc);
253  }
254 
255  return rc;
256 }
257 
267 static void
268 log_assertion_as(const char *file, const char *function, int line,
269  const char *assert_condition)
270 {
271  if (!pcmk__is_daemon) {
272  crm_enable_stderr(TRUE); // Make sure command-line user sees message
273  }
274  crm_err("%s: Triggered fatal assertion at %s:%d : %s",
275  function, file, line, assert_condition);
276 }
277 
278 /* coverity[+kill] */
290 static _Noreturn void
291 abort_as(const char *file, const char *function, int line,
292  const char *assert_condition)
293 {
294  log_assertion_as(file, function, line, assert_condition);
295  abort();
296 }
297 
298 /* coverity[+kill] */
311 static void
312 fail_assert_as(const char *file, const char *function, int line,
313  const char *assert_condition)
314 {
315  int status = 0;
316  pid_t pid = 0;
317 
318  if (!pcmk__is_daemon) {
319  abort_as(file, function, line, assert_condition); // does not return
320  }
321 
322  pid = fork();
323  switch (pid) {
324  case -1: // Fork failed
325  crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
326  ": %s", function, file, line, assert_condition);
327  break;
328 
329  case 0: // Child process: just abort to dump core
330  abort();
331  break;
332 
333  default: // Parent process: wait for child
334  crm_err("%s: Forked child [%d] to record non-fatal assertion at "
335  "%s:%d : %s", function, pid, file, line, assert_condition);
336  crm_write_blackbox(SIGTRAP, NULL);
337  do {
338  if (waitpid(pid, &status, 0) == pid) {
339  return; // Child finished dumping core
340  }
341  } while (errno == EINTR);
342  if (errno == ECHILD) {
343  // crm_mon ignores SIGCHLD
344  crm_trace("Cannot wait on forked child [%d] "
345  "(SIGCHLD is probably ignored)", pid);
346  } else {
347  crm_err("Cannot wait on forked child [%d]: %s",
348  pid, pcmk_rc_str(errno));
349  }
350  break;
351  }
352 }
353 
354 /* coverity[+kill] */
355 void
356 crm_abort(const char *file, const char *function, int line,
357  const char *assert_condition, gboolean do_core, gboolean do_fork)
358 {
359  if (!do_fork) {
360  abort_as(file, function, line, assert_condition);
361  } else if (do_core) {
362  fail_assert_as(file, function, line, assert_condition);
363  } else {
364  log_assertion_as(file, function, line, assert_condition);
365  }
366 }
367 
379 void
380 pcmk__daemonize(const char *name, const char *pidfile)
381 {
382  int rc;
383  pid_t pid;
384 
385  /* Check before we even try... */
386  rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
387  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
388  crm_err("%s: already running [pid %lld in %s]",
389  name, (long long) pid, pidfile);
390  printf("%s: already running [pid %lld in %s]\n",
391  name, (long long) pid, pidfile);
393  }
394 
395  pid = fork();
396  if (pid < 0) {
397  fprintf(stderr, "%s: could not start daemon\n", name);
398  crm_perror(LOG_ERR, "fork");
400 
401  } else if (pid > 0) {
403  }
404 
405  rc = pcmk__lock_pidfile(pidfile, name);
406  if (rc != pcmk_rc_ok) {
407  crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
408  pidfile, name, pcmk_rc_str(rc), rc);
409  printf("Could not lock '%s' for %s: %s (%d)\n",
410  pidfile, name, pcmk_rc_str(rc), rc);
412  }
413 
414  umask(S_IWGRP | S_IWOTH | S_IROTH);
415 
416  close(STDIN_FILENO);
417  pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
418 
419  close(STDOUT_FILENO);
420  pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
421 
422  close(STDERR_FILENO);
423  pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
424 }
425 
426 #ifdef HAVE_UUID_UUID_H
427 # include <uuid/uuid.h>
428 #endif
429 
430 char *
432 {
433  unsigned char uuid[16];
434  char *buffer = malloc(37); /* Including NUL byte */
435 
436  pcmk__mem_assert(buffer);
437  uuid_generate(uuid);
438  uuid_unparse(uuid, buffer);
439  return buffer;
440 }
441 
442 #ifdef HAVE_GNUTLS_GNUTLS_H
443 void
444 crm_gnutls_global_init(void)
445 {
446  signal(SIGPIPE, SIG_IGN);
447  gnutls_global_init();
448 }
449 #endif
450 
451 bool
452 pcmk_str_is_infinity(const char *s) {
454  NULL);
455 }
456 
457 bool
459  return pcmk__str_eq(s, PCMK_VALUE_MINUS_INFINITY, pcmk__str_none);
460 }
461 
470 void
471 pcmk__sleep_ms(unsigned int ms)
472 {
473  // @TODO Impose a sane maximum sleep to avoid hanging a process for long
474  //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
475 
476  // Use sleep() for any whole seconds
477  if (ms >= 1000) {
478  sleep(ms / 1000);
479  ms -= ms / 1000;
480  }
481 
482  if (ms == 0) {
483  return;
484  }
485 
486 #if defined(HAVE_NANOSLEEP)
487  // nanosleep() is POSIX-2008, so prefer that
488  {
489  struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
490 
491  nanosleep(&req, NULL);
492  }
493 #elif defined(HAVE_USLEEP)
494  // usleep() is widely available, though considered obsolete
495  usleep((useconds_t) ms);
496 #else
497  // Otherwise use a trick with select() timeout
498  {
499  struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
500 
501  select(0, NULL, NULL, NULL, &tv);
502  }
503 #endif
504 }
505 
506 // Deprecated functions kept only for backward API compatibility
507 // LCOV_EXCL_START
508 
509 #include <crm/common/util_compat.h>
510 
511 guint
513 {
514  long long msec = -1;
515 
516  errno = 0;
517  if (input == NULL) {
518  return 0;
519 
520  } else if (input[0] == 'P') {
522 
523  if (period_s) {
524  msec = 1000 * crm_time_get_seconds(period_s);
525  crm_time_free(period_s);
526  }
527 
528  } else {
529  msec = crm_get_msec(input);
530  }
531 
532  if (msec < 0) {
533  crm_warn("Using 0 instead of '%s'", input);
534  errno = EINVAL;
535  return 0;
536  }
537  return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
538 }
539 
540 char *
542 {
543  struct utsname hostinfo;
544 
545  return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
546 }
547 
548 // LCOV_EXCL_STOP
549 // End deprecated API
Services API.
A dumping ground.
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:85
const char * pcmk_strerror(int rc)
Definition: results.c:149
void crm_enable_stderr(int enable)
Definition: logging.c:1042
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:936
#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:458
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:356
gboolean crm_config_warning
Definition: utils.c:49
Unspecified error.
Definition: results.h:256
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:452
crm_time_t * crm_time_parse_duration(const char *duration_str)
Parse a time duration from an ISO 8601 duration specification.
Definition: iso8601.c:1086
#define PCMK_VALUE_PLUS_INFINITY
Definition: options.h:192
void pcmk__daemonize(const char *name, const char *pidfile)
Definition: utils.c:380
char * crm_system_name
Definition: utils.c:50
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
#define _Noreturn
Definition: config.h:661
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:512
Utility functions.
void pcmk__sleep_ms(unsigned int ms)
Definition: utils.c:471
External (OS/environmental) problem.
Definition: results.h:276
#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:255
char * crm_generate_uuid(void)
Definition: utils.c:431
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1050
long long crm_time_get_seconds(const crm_time_t *dt)
Definition: iso8601.c:316
CRM_TRACE_INIT_DATA(common)
char * pcmk_hostname(void)
Definition: utils.c:541
void crm_write_blackbox(int nsig, const struct qb_log_callsite *callsite)
Definition: logging.c:505
int compare_version(const char *version1, const char *version2)
Definition: utils.c:188
#define CRM_XS
Definition: logging.h:56
void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:356
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
#define CRM_ASSERT(expr)
Definition: results.h:42
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:69
IPC interface to Pacemaker daemons.
bool pcmk__is_daemon
Definition: logging.c:47
gboolean crm_config_error
Definition: utils.c:48
bool pcmk__is_user_in_group(const char *user, const char *group)
Definition: utils.c:53
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition: utils.c:125
#define crm_info(fmt, args...)
Definition: logging.h:399
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150