pacemaker  2.1.1-52dc28db4
Scalable High-Availability cluster resource manager
utils.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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 
59 
60 int
61 char2score(const char *score)
62 {
63  int score_f = 0;
64 
65  if (score == NULL) {
66 
67  } else if (pcmk_str_is_minus_infinity(score)) {
68  score_f = -CRM_SCORE_INFINITY;
69 
70  } else if (pcmk_str_is_infinity(score)) {
71  score_f = CRM_SCORE_INFINITY;
72 
73  } else if (pcmk__str_eq(score, "red", pcmk__str_casei)) {
74  score_f = pcmk__score_red;
75 
76  } else if (pcmk__str_eq(score, "yellow", pcmk__str_casei)) {
77  score_f = pcmk__score_yellow;
78 
79  } else if (pcmk__str_eq(score, "green", pcmk__str_casei)) {
80  score_f = pcmk__score_green;
81 
82  } else {
83  long long score_ll;
84 
85  pcmk__scan_ll(score, &score_ll, 0LL);
86  if (score_ll > CRM_SCORE_INFINITY) {
87  score_f = CRM_SCORE_INFINITY;
88 
89  } else if (score_ll < -CRM_SCORE_INFINITY) {
90  score_f = -CRM_SCORE_INFINITY;
91 
92  } else {
93  score_f = (int) score_ll;
94  }
95  }
96 
97  return score_f;
98 }
99 
100 char *
101 score2char_stack(int score, char *buf, size_t len)
102 {
103  CRM_CHECK((buf != NULL) && (len >= sizeof(CRM_MINUS_INFINITY_S)),
104  return NULL);
105 
106  if (score >= CRM_SCORE_INFINITY) {
107  strncpy(buf, CRM_INFINITY_S, 9);
108  } else if (score <= -CRM_SCORE_INFINITY) {
109  strncpy(buf, CRM_MINUS_INFINITY_S , 10);
110  } else {
111  snprintf(buf, len, "%d", score);
112  }
113  return buf;
114 }
115 
116 char *
117 score2char(int score)
118 {
119  if (score >= CRM_SCORE_INFINITY) {
120  return strdup(CRM_INFINITY_S);
121 
122  } else if (score <= -CRM_SCORE_INFINITY) {
123  return strdup(CRM_MINUS_INFINITY_S);
124  }
125  return pcmk__itoa(score);
126 }
127 
128 int
129 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
130 {
131  int rc = pcmk_ok;
132  char *buffer = NULL;
133  struct passwd pwd;
134  struct passwd *pwentry = NULL;
135 
136  buffer = calloc(1, PW_BUFFER_LEN);
137  if (buffer == NULL) {
138  return -ENOMEM;
139  }
140 
141  rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
142  if (pwentry) {
143  if (uid) {
144  *uid = pwentry->pw_uid;
145  }
146  if (gid) {
147  *gid = pwentry->pw_gid;
148  }
149  crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
150 
151  } else {
152  rc = rc? -rc : -EINVAL;
153  crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
154  }
155 
156  free(buffer);
157  return rc;
158 }
159 
168 int
169 pcmk_daemon_user(uid_t *uid, gid_t *gid)
170 {
171  static uid_t daemon_uid;
172  static gid_t daemon_gid;
173  static bool found = false;
174  int rc = pcmk_ok;
175 
176  if (!found) {
177  rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
178  if (rc == pcmk_ok) {
179  found = true;
180  }
181  }
182  if (found) {
183  if (uid) {
184  *uid = daemon_uid;
185  }
186  if (gid) {
187  *gid = daemon_gid;
188  }
189  }
190  return rc;
191 }
192 
200 static int
201 version_helper(const char *text, const char **end_text)
202 {
203  int atoi_result = -1;
204 
205  CRM_ASSERT(end_text != NULL);
206 
207  errno = 0;
208 
209  if (text != NULL && text[0] != 0) {
210  /* seemingly sacrificing const-correctness -- because while strtol
211  doesn't modify the input, it doesn't want to artificially taint the
212  "end_text" pointer-to-pointer-to-first-char-in-string with constness
213  in case the input wasn't actually constant -- by semantic definition
214  not a single character will get modified so it shall be perfectly
215  safe to make compiler happy with dropping "const" qualifier here */
216  atoi_result = (int) strtol(text, (char **) end_text, 10);
217 
218  if (errno == EINVAL) {
219  crm_err("Conversion of '%s' %c failed", text, text[0]);
220  atoi_result = -1;
221  }
222  }
223  return atoi_result;
224 }
225 
226 /*
227  * version1 < version2 : -1
228  * version1 = version2 : 0
229  * version1 > version2 : 1
230  */
231 int
232 compare_version(const char *version1, const char *version2)
233 {
234  int rc = 0;
235  int lpc = 0;
236  const char *ver1_iter, *ver2_iter;
237 
238  if (version1 == NULL && version2 == NULL) {
239  return 0;
240  } else if (version1 == NULL) {
241  return -1;
242  } else if (version2 == NULL) {
243  return 1;
244  }
245 
246  ver1_iter = version1;
247  ver2_iter = version2;
248 
249  while (1) {
250  int digit1 = 0;
251  int digit2 = 0;
252 
253  lpc++;
254 
255  if (ver1_iter == ver2_iter) {
256  break;
257  }
258 
259  if (ver1_iter != NULL) {
260  digit1 = version_helper(ver1_iter, &ver1_iter);
261  }
262 
263  if (ver2_iter != NULL) {
264  digit2 = version_helper(ver2_iter, &ver2_iter);
265  }
266 
267  if (digit1 < digit2) {
268  rc = -1;
269  break;
270 
271  } else if (digit1 > digit2) {
272  rc = 1;
273  break;
274  }
275 
276  if (ver1_iter != NULL && *ver1_iter == '.') {
277  ver1_iter++;
278  }
279  if (ver1_iter != NULL && *ver1_iter == '\0') {
280  ver1_iter = NULL;
281  }
282 
283  if (ver2_iter != NULL && *ver2_iter == '.') {
284  ver2_iter++;
285  }
286  if (ver2_iter != NULL && *ver2_iter == 0) {
287  ver2_iter = NULL;
288  }
289  }
290 
291  if (rc == 0) {
292  crm_trace("%s == %s (%d)", version1, version2, lpc);
293  } else if (rc < 0) {
294  crm_trace("%s < %s (%d)", version1, version2, lpc);
295  } else if (rc > 0) {
296  crm_trace("%s > %s (%d)", version1, version2, lpc);
297  }
298 
299  return rc;
300 }
301 
313 guint
314 crm_parse_interval_spec(const char *input)
315 {
316  long long msec = -1;
317 
318  errno = 0;
319  if (input == NULL) {
320  return 0;
321 
322  } else if (input[0] == 'P') {
323  crm_time_t *period_s = crm_time_parse_duration(input);
324 
325  if (period_s) {
326  msec = 1000 * crm_time_get_seconds(period_s);
327  crm_time_free(period_s);
328  }
329 
330  } else {
331  msec = crm_get_msec(input);
332  }
333 
334  if (msec < 0) {
335  crm_warn("Using 0 instead of '%s'", input);
336  errno = EINVAL;
337  return 0;
338  }
339  return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
340 }
341 
351 static void
352 log_assertion_as(const char *file, const char *function, int line,
353  const char *assert_condition)
354 {
355  if (!pcmk__is_daemon) {
356  crm_enable_stderr(TRUE); // Make sure command-line user sees message
357  }
358  crm_err("%s: Triggered fatal assertion at %s:%d : %s",
359  function, file, line, assert_condition);
360 }
361 
362 /* coverity[+kill] */
374 static _Noreturn void
375 abort_as(const char *file, const char *function, int line,
376  const char *assert_condition)
377 {
378  log_assertion_as(file, function, line, assert_condition);
379  abort();
380 }
381 
382 /* coverity[+kill] */
395 static void
396 fail_assert_as(const char *file, const char *function, int line,
397  const char *assert_condition)
398 {
399  int status = 0;
400  pid_t pid = 0;
401 
402  if (!pcmk__is_daemon) {
403  abort_as(file, function, line, assert_condition); // does not return
404  }
405 
406  pid = fork();
407  switch (pid) {
408  case -1: // Fork failed
409  crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
410  ": %s", function, file, line, assert_condition);
411  break;
412 
413  case 0: // Child process: just abort to dump core
414  abort();
415  break;
416 
417  default: // Parent process: wait for child
418  crm_err("%s: Forked child [%d] to record non-fatal assertion at "
419  "%s:%d : %s", function, pid, file, line, assert_condition);
420  crm_write_blackbox(SIGTRAP, NULL);
421  do {
422  if (waitpid(pid, &status, 0) == pid) {
423  return; // Child finished dumping core
424  }
425  } while (errno == EINTR);
426  if (errno == ECHILD) {
427  // crm_mon ignores SIGCHLD
428  crm_trace("Cannot wait on forked child [%d] "
429  "(SIGCHLD is probably ignored)", pid);
430  } else {
431  crm_err("Cannot wait on forked child [%d]: %s",
432  pid, pcmk_rc_str(errno));
433  }
434  break;
435  }
436 }
437 
438 /* coverity[+kill] */
439 void
440 crm_abort(const char *file, const char *function, int line,
441  const char *assert_condition, gboolean do_core, gboolean do_fork)
442 {
443  if (!do_fork) {
444  abort_as(file, function, line, assert_condition);
445  } else if (do_core) {
446  fail_assert_as(file, function, line, assert_condition);
447  } else {
448  log_assertion_as(file, function, line, assert_condition);
449  }
450 }
451 
463 void
464 pcmk__daemonize(const char *name, const char *pidfile)
465 {
466  int rc;
467  pid_t pid;
468 
469  /* Check before we even try... */
470  rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
471  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
472  crm_err("%s: already running [pid %lld in %s]",
473  name, (long long) pid, pidfile);
474  printf("%s: already running [pid %lld in %s]\n",
475  name, (long long) pid, pidfile);
477  }
478 
479  pid = fork();
480  if (pid < 0) {
481  fprintf(stderr, "%s: could not start daemon\n", name);
482  crm_perror(LOG_ERR, "fork");
484 
485  } else if (pid > 0) {
487  }
488 
489  rc = pcmk__lock_pidfile(pidfile, name);
490  if (rc != pcmk_rc_ok) {
491  crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
492  pidfile, name, pcmk_rc_str(rc), rc);
493  printf("Could not lock '%s' for %s: %s (%d)\n",
494  pidfile, name, pcmk_rc_str(rc), rc);
496  }
497 
498  umask(S_IWGRP | S_IWOTH | S_IROTH);
499 
500  close(STDIN_FILENO);
501  pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
502 
503  close(STDOUT_FILENO);
504  pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
505 
506  close(STDERR_FILENO);
507  pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
508 }
509 
510 char *
511 crm_meta_name(const char *field)
512 {
513  int lpc = 0;
514  int max = 0;
515  char *crm_name = NULL;
516 
517  CRM_CHECK(field != NULL, return NULL);
518  crm_name = crm_strdup_printf(CRM_META "_%s", field);
519 
520  /* Massage the names so they can be used as shell variables */
521  max = strlen(crm_name);
522  for (; lpc < max; lpc++) {
523  switch (crm_name[lpc]) {
524  case '-':
525  crm_name[lpc] = '_';
526  break;
527  }
528  }
529  return crm_name;
530 }
531 
532 const char *
533 crm_meta_value(GHashTable * hash, const char *field)
534 {
535  char *key = NULL;
536  const char *value = NULL;
537 
538  key = crm_meta_name(field);
539  if (key) {
540  value = g_hash_table_lookup(hash, key);
541  free(key);
542  }
543 
544  return value;
545 }
546 
547 #ifdef HAVE_UUID_UUID_H
548 # include <uuid/uuid.h>
549 #endif
550 
551 char *
553 {
554  unsigned char uuid[16];
555  char *buffer = malloc(37); /* Including NUL byte */
556 
557  uuid_generate(uuid);
558  uuid_unparse(uuid, buffer);
559  return buffer;
560 }
561 
562 #ifdef HAVE_GNUTLS_GNUTLS_H
563 void
564 crm_gnutls_global_init(void)
565 {
566  signal(SIGPIPE, SIG_IGN);
567  gnutls_global_init();
568 }
569 #endif
570 
576 char *
578 {
579  struct utsname hostinfo;
580 
581  return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
582 }
583 
584 bool
585 pcmk_str_is_infinity(const char *s) {
587 }
588 
589 bool
591  return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
592 }
593 
602 void
603 pcmk__sleep_ms(unsigned int ms)
604 {
605  // @TODO Impose a sane maximum sleep to avoid hanging a process for long
606  //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
607 
608  // Use sleep() for any whole seconds
609  if (ms >= 1000) {
610  sleep(ms / 1000);
611  ms -= ms / 1000;
612  }
613 
614  if (ms == 0) {
615  return;
616  }
617 
618 #if defined(HAVE_NANOSLEEP)
619  // nanosleep() is POSIX-2008, so prefer that
620  {
621  struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
622 
623  nanosleep(&req, NULL);
624  }
625 #elif defined(HAVE_USLEEP)
626  // usleep() is widely available, though considered obsolete
627  usleep((useconds_t) ms);
628 #else
629  // Otherwise use a trick with select() timeout
630  {
631  struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
632 
633  select(0, NULL, NULL, NULL, &tv);
634  }
635 #endif
636 }
Services API.
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:218
int pcmk__score_green
Definition: utils.c:57
char * pcmk_hostname()
Get the local hostname.
Definition: utils.c:577
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:468
A dumping ground.
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:129
const char * pcmk_strerror(int rc)
Definition: results.c:58
void crm_enable_stderr(int enable)
Definition: logging.c:960
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:533
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:759
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:590
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:363
gboolean crm_config_warning
Definition: utils.c:53
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:585
crm_time_t * crm_time_parse_duration(const char *duration_str)
Parse a time duration from an ISO 8601 duration specification.
Definition: iso8601.c:983
void pcmk__daemonize(const char *name, const char *pidfile)
Definition: utils.c:464
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:420
#define _Noreturn
Definition: config.h:653
#define CRM_SCORE_INFINITY
Definition: crm.h:85
Wrappers for and extensions to glib mainloop.
char * crm_meta_name(const char *field)
Definition: utils.c:511
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
#define PW_BUFFER_LEN
Definition: utils.c:47
#define crm_warn(fmt, args...)
Definition: logging.h:351
int rc
Definition: pcmk_fence.c:35
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:314
int pcmk__score_yellow
Definition: utils.c:58
Utility functions.
void pcmk__sleep_ms(unsigned int ms)
Definition: utils.c:603
char * score2char(int score)
Definition: utils.c:117
#define crm_trace(fmt, args...)
Definition: logging.h:356
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:32
char * crm_generate_uuid(void)
Definition: utils.c:552
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:979
CRM_TRACE_INIT_DATA(common)
long long int crm_time_get_seconds(crm_time_t *dt)
Definition: iso8601.c:308
int compare_version(const char *version1, const char *version2)
Definition: utils.c:232
#define CRM_XS
Definition: logging.h:54
void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:440
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition: pid.c:219
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:301
int pcmk__score_red
Definition: utils.c:56
#define CRM_META
Definition: crm.h:78
int char2score(const char *score)
Definition: utils.c:61
#define crm_err(fmt, args...)
Definition: logging.h:350
#define CRM_ASSERT(expr)
Definition: results.h:42
#define CRM_INFINITY_S
Definition: crm.h:86
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:177
#define pcmk_ok
Definition: results.h:67
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:169
char * name
Definition: pcmk_fence.c:31
#define crm_info(fmt, args...)
Definition: logging.h:353
char * score2char_stack(int score, char *buf, size_t len)
Definition: utils.c:101
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:141