pacemaker  2.1.4-dc6eb4362
Scalable High-Availability cluster resource manager
utils.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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 
31 #include <qb/qbdefs.h>
32 
33 #include <crm/crm.h>
34 #include <crm/services.h>
35 #include <crm/msg_xml.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 #ifndef PW_BUFFER_LEN
47 # define PW_BUFFER_LEN 500
48 #endif
49 
50 CRM_TRACE_INIT_DATA(common);
51 
52 gboolean crm_config_error = FALSE;
53 gboolean crm_config_warning = FALSE;
54 char *crm_system_name = NULL;
55 
56 int
57 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
58 {
59  int rc = pcmk_ok;
60  char *buffer = NULL;
61  struct passwd pwd;
62  struct passwd *pwentry = NULL;
63 
64  buffer = calloc(1, PW_BUFFER_LEN);
65  if (buffer == NULL) {
66  return -ENOMEM;
67  }
68 
69  rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
70  if (pwentry) {
71  if (uid) {
72  *uid = pwentry->pw_uid;
73  }
74  if (gid) {
75  *gid = pwentry->pw_gid;
76  }
77  crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
78 
79  } else {
80  rc = rc? -rc : -EINVAL;
81  crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
82  }
83 
84  free(buffer);
85  return rc;
86 }
87 
96 int
97 pcmk_daemon_user(uid_t *uid, gid_t *gid)
98 {
99  static uid_t daemon_uid;
100  static gid_t daemon_gid;
101  static bool found = false;
102  int rc = pcmk_ok;
103 
104  if (!found) {
105  rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
106  if (rc == pcmk_ok) {
107  found = true;
108  }
109  }
110  if (found) {
111  if (uid) {
112  *uid = daemon_uid;
113  }
114  if (gid) {
115  *gid = daemon_gid;
116  }
117  }
118  return rc;
119 }
120 
128 static int
129 version_helper(const char *text, const char **end_text)
130 {
131  int atoi_result = -1;
132 
133  CRM_ASSERT(end_text != NULL);
134 
135  errno = 0;
136 
137  if (text != NULL && text[0] != 0) {
138  /* seemingly sacrificing const-correctness -- because while strtol
139  doesn't modify the input, it doesn't want to artificially taint the
140  "end_text" pointer-to-pointer-to-first-char-in-string with constness
141  in case the input wasn't actually constant -- by semantic definition
142  not a single character will get modified so it shall be perfectly
143  safe to make compiler happy with dropping "const" qualifier here */
144  atoi_result = (int) strtol(text, (char **) end_text, 10);
145 
146  if (errno == EINVAL) {
147  crm_err("Conversion of '%s' %c failed", text, text[0]);
148  atoi_result = -1;
149  }
150  }
151  return atoi_result;
152 }
153 
154 /*
155  * version1 < version2 : -1
156  * version1 = version2 : 0
157  * version1 > version2 : 1
158  */
159 int
160 compare_version(const char *version1, const char *version2)
161 {
162  int rc = 0;
163  int lpc = 0;
164  const char *ver1_iter, *ver2_iter;
165 
166  if (version1 == NULL && version2 == NULL) {
167  return 0;
168  } else if (version1 == NULL) {
169  return -1;
170  } else if (version2 == NULL) {
171  return 1;
172  }
173 
174  ver1_iter = version1;
175  ver2_iter = version2;
176 
177  while (1) {
178  int digit1 = 0;
179  int digit2 = 0;
180 
181  lpc++;
182 
183  if (ver1_iter == ver2_iter) {
184  break;
185  }
186 
187  if (ver1_iter != NULL) {
188  digit1 = version_helper(ver1_iter, &ver1_iter);
189  }
190 
191  if (ver2_iter != NULL) {
192  digit2 = version_helper(ver2_iter, &ver2_iter);
193  }
194 
195  if (digit1 < digit2) {
196  rc = -1;
197  break;
198 
199  } else if (digit1 > digit2) {
200  rc = 1;
201  break;
202  }
203 
204  if (ver1_iter != NULL && *ver1_iter == '.') {
205  ver1_iter++;
206  }
207  if (ver1_iter != NULL && *ver1_iter == '\0') {
208  ver1_iter = NULL;
209  }
210 
211  if (ver2_iter != NULL && *ver2_iter == '.') {
212  ver2_iter++;
213  }
214  if (ver2_iter != NULL && *ver2_iter == 0) {
215  ver2_iter = NULL;
216  }
217  }
218 
219  if (rc == 0) {
220  crm_trace("%s == %s (%d)", version1, version2, lpc);
221  } else if (rc < 0) {
222  crm_trace("%s < %s (%d)", version1, version2, lpc);
223  } else if (rc > 0) {
224  crm_trace("%s > %s (%d)", version1, version2, lpc);
225  }
226 
227  return rc;
228 }
229 
241 guint
243 {
244  long long msec = -1;
245 
246  errno = 0;
247  if (input == NULL) {
248  return 0;
249 
250  } else if (input[0] == 'P') {
252 
253  if (period_s) {
254  msec = 1000 * crm_time_get_seconds(period_s);
255  crm_time_free(period_s);
256  }
257 
258  } else {
259  msec = crm_get_msec(input);
260  }
261 
262  if (msec < 0) {
263  crm_warn("Using 0 instead of '%s'", input);
264  errno = EINVAL;
265  return 0;
266  }
267  return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
268 }
269 
279 static void
280 log_assertion_as(const char *file, const char *function, int line,
281  const char *assert_condition)
282 {
283  if (!pcmk__is_daemon) {
284  crm_enable_stderr(TRUE); // Make sure command-line user sees message
285  }
286  crm_err("%s: Triggered fatal assertion at %s:%d : %s",
287  function, file, line, assert_condition);
288 }
289 
290 /* coverity[+kill] */
302 static _Noreturn void
303 abort_as(const char *file, const char *function, int line,
304  const char *assert_condition)
305 {
306  log_assertion_as(file, function, line, assert_condition);
307  abort();
308 }
309 
310 /* coverity[+kill] */
323 static void
324 fail_assert_as(const char *file, const char *function, int line,
325  const char *assert_condition)
326 {
327  int status = 0;
328  pid_t pid = 0;
329 
330  if (!pcmk__is_daemon) {
331  abort_as(file, function, line, assert_condition); // does not return
332  }
333 
334  pid = fork();
335  switch (pid) {
336  case -1: // Fork failed
337  crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
338  ": %s", function, file, line, assert_condition);
339  break;
340 
341  case 0: // Child process: just abort to dump core
342  abort();
343  break;
344 
345  default: // Parent process: wait for child
346  crm_err("%s: Forked child [%d] to record non-fatal assertion at "
347  "%s:%d : %s", function, pid, file, line, assert_condition);
348  crm_write_blackbox(SIGTRAP, NULL);
349  do {
350  if (waitpid(pid, &status, 0) == pid) {
351  return; // Child finished dumping core
352  }
353  } while (errno == EINTR);
354  if (errno == ECHILD) {
355  // crm_mon ignores SIGCHLD
356  crm_trace("Cannot wait on forked child [%d] "
357  "(SIGCHLD is probably ignored)", pid);
358  } else {
359  crm_err("Cannot wait on forked child [%d]: %s",
360  pid, pcmk_rc_str(errno));
361  }
362  break;
363  }
364 }
365 
366 /* coverity[+kill] */
367 void
368 crm_abort(const char *file, const char *function, int line,
369  const char *assert_condition, gboolean do_core, gboolean do_fork)
370 {
371  if (!do_fork) {
372  abort_as(file, function, line, assert_condition);
373  } else if (do_core) {
374  fail_assert_as(file, function, line, assert_condition);
375  } else {
376  log_assertion_as(file, function, line, assert_condition);
377  }
378 }
379 
391 void
392 pcmk__daemonize(const char *name, const char *pidfile)
393 {
394  int rc;
395  pid_t pid;
396 
397  /* Check before we even try... */
398  rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
399  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
400  crm_err("%s: already running [pid %lld in %s]",
401  name, (long long) pid, pidfile);
402  printf("%s: already running [pid %lld in %s]\n",
403  name, (long long) pid, pidfile);
405  }
406 
407  pid = fork();
408  if (pid < 0) {
409  fprintf(stderr, "%s: could not start daemon\n", name);
410  crm_perror(LOG_ERR, "fork");
412 
413  } else if (pid > 0) {
415  }
416 
417  rc = pcmk__lock_pidfile(pidfile, name);
418  if (rc != pcmk_rc_ok) {
419  crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
420  pidfile, name, pcmk_rc_str(rc), rc);
421  printf("Could not lock '%s' for %s: %s (%d)\n",
422  pidfile, name, pcmk_rc_str(rc), rc);
424  }
425 
426  umask(S_IWGRP | S_IWOTH | S_IROTH);
427 
428  close(STDIN_FILENO);
429  pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
430 
431  close(STDOUT_FILENO);
432  pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
433 
434  close(STDERR_FILENO);
435  pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
436 }
437 
438 char *
439 crm_meta_name(const char *field)
440 {
441  int lpc = 0;
442  int max = 0;
443  char *crm_name = NULL;
444 
445  CRM_CHECK(field != NULL, return NULL);
446  crm_name = crm_strdup_printf(CRM_META "_%s", field);
447 
448  /* Massage the names so they can be used as shell variables */
449  max = strlen(crm_name);
450  for (; lpc < max; lpc++) {
451  switch (crm_name[lpc]) {
452  case '-':
453  crm_name[lpc] = '_';
454  break;
455  }
456  }
457  return crm_name;
458 }
459 
460 const char *
461 crm_meta_value(GHashTable * hash, const char *field)
462 {
463  char *key = NULL;
464  const char *value = NULL;
465 
466  key = crm_meta_name(field);
467  if (key) {
468  value = g_hash_table_lookup(hash, key);
469  free(key);
470  }
471 
472  return value;
473 }
474 
475 #ifdef HAVE_UUID_UUID_H
476 # include <uuid/uuid.h>
477 #endif
478 
479 char *
481 {
482  unsigned char uuid[16];
483  char *buffer = malloc(37); /* Including NUL byte */
484 
485  uuid_generate(uuid);
486  uuid_unparse(uuid, buffer);
487  return buffer;
488 }
489 
490 #ifdef HAVE_GNUTLS_GNUTLS_H
491 void
492 crm_gnutls_global_init(void)
493 {
494  signal(SIGPIPE, SIG_IGN);
495  gnutls_global_init();
496 }
497 #endif
498 
504 char *
506 {
507  struct utsname hostinfo;
508 
509  return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
510 }
511 
512 bool
513 pcmk_str_is_infinity(const char *s) {
515 }
516 
517 bool
519  return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
520 }
521 
530 void
531 pcmk__sleep_ms(unsigned int ms)
532 {
533  // @TODO Impose a sane maximum sleep to avoid hanging a process for long
534  //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
535 
536  // Use sleep() for any whole seconds
537  if (ms >= 1000) {
538  sleep(ms / 1000);
539  ms -= ms / 1000;
540  }
541 
542  if (ms == 0) {
543  return;
544  }
545 
546 #if defined(HAVE_NANOSLEEP)
547  // nanosleep() is POSIX-2008, so prefer that
548  {
549  struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
550 
551  nanosleep(&req, NULL);
552  }
553 #elif defined(HAVE_USLEEP)
554  // usleep() is widely available, though considered obsolete
555  usleep((useconds_t) ms);
556 #else
557  // Otherwise use a trick with select() timeout
558  {
559  struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
560 
561  select(0, NULL, NULL, NULL, &tv);
562  }
563 #endif
564 }
Services API.
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
char * pcmk_hostname()
Get the local hostname.
Definition: utils.c:505
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:474
A dumping ground.
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:57
const char * pcmk_strerror(int rc)
Definition: results.c:58
void crm_enable_stderr(int enable)
Definition: logging.c:998
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:461
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:775
const char * name
Definition: cib.c:24
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:518
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:364
gboolean crm_config_warning
Definition: utils.c:53
Unspecified error.
Definition: results.h:240
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:513
crm_time_t * crm_time_parse_duration(const char *duration_str)
Parse a time duration from an ISO 8601 duration specification.
Definition: iso8601.c:982
void pcmk__daemonize(const char *name, const char *pidfile)
Definition: utils.c:392
char * crm_system_name
Definition: utils.c:54
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:370
#define _Noreturn
Definition: config.h:683
Wrappers for and extensions to glib mainloop.
char * crm_meta_name(const char *field)
Definition: utils.c:439
#define PW_BUFFER_LEN
Definition: utils.c:47
#define crm_warn(fmt, args...)
Definition: logging.h:359
uint32_t pid
Definition: cpg.c:46
guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:242
Utility functions.
void pcmk__sleep_ms(unsigned int ms)
Definition: utils.c:531
External (OS/environmental) problem.
Definition: results.h:258
#define crm_trace(fmt, args...)
Definition: logging.h:364
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define CRM_MINUS_INFINITY_S
Definition: crm.h:88
Wrappers for and extensions to libxml2.
ISO_8601 Date handling.
#define CRM_DAEMON_USER
Definition: config.h:30
Success.
Definition: results.h:239
char * crm_generate_uuid(void)
Definition: utils.c:480
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:955
CRM_TRACE_INIT_DATA(common)
long long int crm_time_get_seconds(crm_time_t *dt)
Definition: iso8601.c:307
int compare_version(const char *version1, const char *version2)
Definition: utils.c:160
#define CRM_XS
Definition: logging.h:55
void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:368
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition: pid.c:228
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:309
#define CRM_META
Definition: crm.h:78
#define crm_err(fmt, args...)
Definition: logging.h:358
#define CRM_ASSERT(expr)
Definition: results.h:42
#define CRM_INFINITY_S
Definition: crm.h:86
xmlNode * input
char uname[MAX_NAME]
Definition: cpg.c:50
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition: pid.c:186
#define pcmk_ok
Definition: results.h:68
IPC interface to Pacemaker daemons.
bool pcmk__is_daemon
Definition: logging.c:47
#define CRM_PLUS_INFINITY_S
Definition: crm.h:87
gboolean crm_config_error
Definition: utils.c:52
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition: utils.c:97
#define crm_info(fmt, args...)
Definition: logging.h:361
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140