pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
utils.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2020 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 #ifndef PW_BUFFER_LEN
45 # define PW_BUFFER_LEN 500
46 #endif
47 
48 CRM_TRACE_INIT_DATA(common);
49 
50 gboolean crm_config_error = FALSE;
51 gboolean crm_config_warning = FALSE;
52 char *crm_system_name = NULL;
53 
57 
58 int
59 char2score(const char *score)
60 {
61  int score_f = 0;
62 
63  if (score == NULL) {
64 
65  } else if (pcmk_str_is_minus_infinity(score)) {
66  score_f = -CRM_SCORE_INFINITY;
67 
68  } else if (pcmk_str_is_infinity(score)) {
69  score_f = CRM_SCORE_INFINITY;
70 
71  } else if (safe_str_eq(score, "red")) {
72  score_f = pcmk__score_red;
73 
74  } else if (safe_str_eq(score, "yellow")) {
75  score_f = pcmk__score_yellow;
76 
77  } else if (safe_str_eq(score, "green")) {
78  score_f = pcmk__score_green;
79 
80  } else {
81  score_f = crm_parse_int(score, NULL);
82  if (score_f > 0 && score_f > CRM_SCORE_INFINITY) {
83  score_f = CRM_SCORE_INFINITY;
84 
85  } else if (score_f < 0 && score_f < -CRM_SCORE_INFINITY) {
86  score_f = -CRM_SCORE_INFINITY;
87  }
88  }
89 
90  return score_f;
91 }
92 
93 char *
94 score2char_stack(int score, char *buf, size_t len)
95 {
96  if (score >= CRM_SCORE_INFINITY) {
97  strncpy(buf, CRM_INFINITY_S, 9);
98  } else if (score <= -CRM_SCORE_INFINITY) {
99  strncpy(buf, CRM_MINUS_INFINITY_S , 10);
100  } else {
101  return crm_itoa_stack(score, buf, len);
102  }
103 
104  return buf;
105 }
106 
107 char *
108 score2char(int score)
109 {
110  if (score >= CRM_SCORE_INFINITY) {
111  return strdup(CRM_INFINITY_S);
112 
113  } else if (score <= -CRM_SCORE_INFINITY) {
114  return strdup(CRM_MINUS_INFINITY_S);
115  }
116  return crm_itoa(score);
117 }
118 
119 char *
120 generate_hash_key(const char *crm_msg_reference, const char *sys)
121 {
122  char *hash_key = crm_strdup_printf("%s_%s", (sys? sys : "none"),
123  crm_msg_reference);
124 
125  crm_trace("created hash key: (%s)", hash_key);
126  return hash_key;
127 }
128 
129 
130 int
131 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
132 {
133  int rc = pcmk_ok;
134  char *buffer = NULL;
135  struct passwd pwd;
136  struct passwd *pwentry = NULL;
137 
138  buffer = calloc(1, PW_BUFFER_LEN);
139  if (buffer == NULL) {
140  return -ENOMEM;
141  }
142 
143  rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
144  if (pwentry) {
145  if (uid) {
146  *uid = pwentry->pw_uid;
147  }
148  if (gid) {
149  *gid = pwentry->pw_gid;
150  }
151  crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
152 
153  } else {
154  rc = rc? -rc : -EINVAL;
155  crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
156  }
157 
158  free(buffer);
159  return rc;
160 }
161 
170 int
171 pcmk_daemon_user(uid_t *uid, gid_t *gid)
172 {
173  static uid_t daemon_uid;
174  static gid_t daemon_gid;
175  static bool found = false;
176  int rc = pcmk_err_generic;
177 
178  if (!found) {
179  rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
180  if (rc == pcmk_ok) {
181  found = true;
182  }
183  }
184  if (found) {
185  if (uid) {
186  *uid = daemon_uid;
187  }
188  if (gid) {
189  *gid = daemon_gid;
190  }
191  }
192  return rc;
193 }
194 
195 static int
196 crm_version_helper(const char *text, const char **end_text)
197 {
198  int atoi_result = -1;
199 
200  CRM_ASSERT(end_text != NULL);
201 
202  errno = 0;
203 
204  if (text != NULL && text[0] != 0) {
205  /* seemingly sacrificing const-correctness -- because while strtol
206  doesn't modify the input, it doesn't want to artificially taint the
207  "end_text" pointer-to-pointer-to-first-char-in-string with constness
208  in case the input wasn't actually constant -- by semantic definition
209  not a single character will get modified so it shall be perfectly
210  safe to make compiler happy with dropping "const" qualifier here */
211  atoi_result = (int) strtol(text, (char **) end_text, 10);
212 
213  if (errno == EINVAL) {
214  crm_err("Conversion of '%s' %c failed", text, text[0]);
215  atoi_result = -1;
216  }
217  }
218  return atoi_result;
219 }
220 
221 /*
222  * version1 < version2 : -1
223  * version1 = version2 : 0
224  * version1 > version2 : 1
225  */
226 int
227 compare_version(const char *version1, const char *version2)
228 {
229  int rc = 0;
230  int lpc = 0;
231  const char *ver1_iter, *ver2_iter;
232 
233  if (version1 == NULL && version2 == NULL) {
234  return 0;
235  } else if (version1 == NULL) {
236  return -1;
237  } else if (version2 == NULL) {
238  return 1;
239  }
240 
241  ver1_iter = version1;
242  ver2_iter = version2;
243 
244  while (1) {
245  int digit1 = 0;
246  int digit2 = 0;
247 
248  lpc++;
249 
250  if (ver1_iter == ver2_iter) {
251  break;
252  }
253 
254  if (ver1_iter != NULL) {
255  digit1 = crm_version_helper(ver1_iter, &ver1_iter);
256  }
257 
258  if (ver2_iter != NULL) {
259  digit2 = crm_version_helper(ver2_iter, &ver2_iter);
260  }
261 
262  if (digit1 < digit2) {
263  rc = -1;
264  break;
265 
266  } else if (digit1 > digit2) {
267  rc = 1;
268  break;
269  }
270 
271  if (ver1_iter != NULL && *ver1_iter == '.') {
272  ver1_iter++;
273  }
274  if (ver1_iter != NULL && *ver1_iter == '\0') {
275  ver1_iter = NULL;
276  }
277 
278  if (ver2_iter != NULL && *ver2_iter == '.') {
279  ver2_iter++;
280  }
281  if (ver2_iter != NULL && *ver2_iter == 0) {
282  ver2_iter = NULL;
283  }
284  }
285 
286  if (rc == 0) {
287  crm_trace("%s == %s (%d)", version1, version2, lpc);
288  } else if (rc < 0) {
289  crm_trace("%s < %s (%d)", version1, version2, lpc);
290  } else if (rc > 0) {
291  crm_trace("%s > %s (%d)", version1, version2, lpc);
292  }
293 
294  return rc;
295 }
296 
308 guint
309 crm_parse_interval_spec(const char *input)
310 {
311  long long msec = -1;
312 
313  errno = 0;
314  if (input == NULL) {
315  return 0;
316 
317  } else if (input[0] == 'P') {
318  crm_time_t *period_s = crm_time_parse_duration(input);
319 
320  if (period_s) {
321  msec = 1000 * crm_time_get_seconds(period_s);
322  crm_time_free(period_s);
323  }
324 
325  } else {
326  msec = crm_get_msec(input);
327  }
328 
329  if (msec < 0) {
330  crm_warn("Using 0 instead of '%s'", input);
331  errno = EINVAL;
332  return 0;
333  }
334  return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
335 }
336 
337 extern bool crm_is_daemon;
338 
339 /* coverity[+kill] */
340 void
341 crm_abort(const char *file, const char *function, int line,
342  const char *assert_condition, gboolean do_core, gboolean do_fork)
343 {
344  int rc = 0;
345  int pid = 0;
346  int status = 0;
347 
348  /* Implied by the parent's error logging below */
349  /* crm_write_blackbox(0); */
350 
351  if(crm_is_daemon == FALSE) {
352  /* This is a command line tool - do not fork */
353 
354  /* crm_add_logfile(NULL); * Record it to a file? */
355  crm_enable_stderr(TRUE); /* Make sure stderr is enabled so we can tell the caller */
356  do_fork = FALSE; /* Just crash if needed */
357  }
358 
359  if (do_core == FALSE) {
360  crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition);
361  return;
362 
363  } else if (do_fork) {
364  pid = fork();
365 
366  } else {
367  crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition);
368  }
369 
370  if (pid == -1) {
371  crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s",
372  function, file, line, assert_condition);
373  return;
374 
375  } else if(pid == 0) {
376  /* Child process */
377  abort();
378  return;
379  }
380 
381  /* Parent process */
382  crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s",
383  function, pid, file, line, assert_condition);
384  crm_write_blackbox(SIGTRAP, NULL);
385 
386  do {
387  rc = waitpid(pid, &status, 0);
388  if(rc == pid) {
389  return; /* Job done */
390  }
391 
392  } while(errno == EINTR);
393 
394  if (errno == ECHILD) {
395  /* crm_mon does this */
396  crm_trace("Cannot wait on forked child %d - SIGCHLD is probably set to SIG_IGN", pid);
397  return;
398  }
399  crm_perror(LOG_ERR, "Cannot wait on forked child %d", pid);
400 }
401 
402 void
403 crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
404 {
405  int rc;
406  pid_t pid;
407 
408  if (daemonize == FALSE) {
409  return;
410  }
411 
412  /* Check before we even try... */
413  rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
414  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
415  crm_err("%s: already running [pid %lld in %s]",
416  name, (long long) pid, pidfile);
417  printf("%s: already running [pid %lld in %s]\n",
418  name, (long long) pid, pidfile);
420  }
421 
422  pid = fork();
423  if (pid < 0) {
424  fprintf(stderr, "%s: could not start daemon\n", name);
425  crm_perror(LOG_ERR, "fork");
427 
428  } else if (pid > 0) {
430  }
431 
432  rc = pcmk__lock_pidfile(pidfile, name);
433  if (rc != pcmk_rc_ok) {
434  crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
435  pidfile, name, pcmk_rc_str(rc), rc);
436  printf("Could not lock '%s' for %s: %s (%d)\n",
437  pidfile, name, pcmk_rc_str(rc), rc);
439  }
440 
441  umask(S_IWGRP | S_IWOTH | S_IROTH);
442 
443  close(STDIN_FILENO);
444  pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
445 
446  close(STDOUT_FILENO);
447  pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
448 
449  close(STDERR_FILENO);
450  pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
451 }
452 
453 char *
454 crm_meta_name(const char *field)
455 {
456  int lpc = 0;
457  int max = 0;
458  char *crm_name = NULL;
459 
460  CRM_CHECK(field != NULL, return NULL);
461  crm_name = crm_strdup_printf(CRM_META "_%s", field);
462 
463  /* Massage the names so they can be used as shell variables */
464  max = strlen(crm_name);
465  for (; lpc < max; lpc++) {
466  switch (crm_name[lpc]) {
467  case '-':
468  crm_name[lpc] = '_';
469  break;
470  }
471  }
472  return crm_name;
473 }
474 
475 const char *
476 crm_meta_value(GHashTable * hash, const char *field)
477 {
478  char *key = NULL;
479  const char *value = NULL;
480 
481  key = crm_meta_name(field);
482  if (key) {
483  value = g_hash_table_lookup(hash, key);
484  free(key);
485  }
486 
487  return value;
488 }
489 
490 #ifdef HAVE_UUID_UUID_H
491 # include <uuid/uuid.h>
492 #endif
493 
494 char *
496 {
497  unsigned char uuid[16];
498  char *buffer = malloc(37); /* Including NUL byte */
499 
500  uuid_generate(uuid);
501  uuid_unparse(uuid, buffer);
502  return buffer;
503 }
504 
516 const char *
518 {
519  if (name == NULL) {
520  return "unknown";
521 
522  } else if (!strcmp(name, "pacemaker-attrd")) {
523  return "attrd";
524 
525  } else if (!strcmp(name, "pacemaker-based")) {
526  return CRM_SYSTEM_CIB;
527 
528  } else if (!strcmp(name, "pacemaker-controld")) {
529  return CRM_SYSTEM_CRMD;
530 
531  } else if (!strcmp(name, "pacemaker-execd")) {
532  return CRM_SYSTEM_LRMD;
533 
534  } else if (!strcmp(name, "pacemaker-fenced")) {
535  return "stonith-ng";
536 
537  } else if (!strcmp(name, "pacemaker-schedulerd")) {
538  return CRM_SYSTEM_PENGINE;
539 
540  } else {
541  return name;
542  }
543 }
544 
552 bool
554 {
555  name = pcmk_message_name(name);
556  return (!strcmp(name, CRM_SYSTEM_CRMD)
557  || !strcmp(name, CRM_SYSTEM_STONITHD)
558  || !strcmp(name, "stonith-ng")
559  || !strcmp(name, "attrd")
560  || !strcmp(name, CRM_SYSTEM_CIB)
561  || !strcmp(name, CRM_SYSTEM_MCP)
562  || !strcmp(name, CRM_SYSTEM_DC)
563  || !strcmp(name, CRM_SYSTEM_TENGINE)
564  || !strcmp(name, CRM_SYSTEM_LRMD));
565 }
566 
567 #include <md5.h>
568 
569 char *
570 crm_md5sum(const char *buffer)
571 {
572  int lpc = 0, len = 0;
573  char *digest = NULL;
574  unsigned char raw_digest[MD5_DIGEST_SIZE];
575 
576  if (buffer == NULL) {
577  buffer = "";
578  }
579  len = strlen(buffer);
580 
581  crm_trace("Beginning digest of %d bytes", len);
582  digest = malloc(2 * MD5_DIGEST_SIZE + 1);
583  if(digest) {
584  md5_buffer(buffer, len, raw_digest);
585  for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
586  sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
587  }
588  digest[(2 * MD5_DIGEST_SIZE)] = 0;
589  crm_trace("Digest %s.", digest);
590 
591  } else {
592  crm_err("Could not create digest");
593  }
594  return digest;
595 }
596 
597 #ifdef HAVE_GNUTLS_GNUTLS_H
598 void
599 crm_gnutls_global_init(void)
600 {
601  signal(SIGPIPE, SIG_IGN);
602  gnutls_global_init();
603 }
604 #endif
605 
611 char *
613 {
614  struct utsname hostinfo;
615 
616  return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
617 }
618 
619 bool
620 pcmk_str_is_infinity(const char *s) {
621  return crm_str_eq(s, CRM_INFINITY_S, TRUE) || crm_str_eq(s, CRM_PLUS_INFINITY_S, TRUE);
622 }
623 
624 bool
626  return crm_str_eq(s, CRM_MINUS_INFINITY_S, TRUE);
627 }
Services API.
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
char uname[MAX_NAME]
Definition: internal.h:85
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:394
A dumping ground.
const char * pcmk_strerror(int rc)
Definition: results.c:55
void crm_enable_stderr(int enable)
Definition: logging.c:892
bool crm_is_daemon_name(const char *name)
Check whether a string represents a cluster daemon name.
Definition: utils.c:553
#define crm_crit(fmt, args...)
Definition: logging.h:362
char * crm_generate_uuid(void)
Definition: utils.c:495
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:751
struct crm_time_s crm_time_t
Definition: iso8601.h:32
int char2score(const char *score)
Definition: utils.c:59
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:211
void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
Definition: utils.c:403
char * score2char_stack(int score, char *buf, size_t len)
Definition: utils.c:94
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:476
#define pcmk_err_generic
Definition: results.h:70
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:126
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
char * crm_system_name
Definition: utils.c:52
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:131
uint32_t pid
Definition: internal.h:81
guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:309
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition: utils.c:171
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:413
#define CRM_SCORE_INFINITY
Definition: crm.h:81
Wrappers for and extensions to glib mainloop.
char * crm_meta_name(const char *field)
Definition: utils.c:454
#define CRM_SYSTEM_DC
Definition: crm.h:98
#define CRM_SYSTEM_MCP
Definition: crm.h:107
#define CRM_TRACE_INIT_DATA(name)
Definition: logging.h:134
void * md5_buffer(const char *buffer, size_t len, void *resblock)
Definition: md5.c:227
gboolean crm_config_error
Definition: utils.c:50
#define PW_BUFFER_LEN
Definition: utils.c:45
#define crm_warn(fmt, args...)
Definition: logging.h:364
int rc
Definition: pcmk_fence.c:34
const char * pcmk_message_name(const char *name)
Get name to be used as identifier for cluster messages.
Definition: utils.c:517
Utility functions.
#define crm_trace(fmt, args...)
Definition: logging.h:369
#define CRM_SYSTEM_PENGINE
Definition: crm.h:104
#define CRM_MINUS_INFINITY_S
Definition: crm.h:84
Wrappers for and extensions to libxml2.
ISO_8601 Date handling.
#define CRM_DAEMON_USER
Definition: config.h:32
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:620
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:326
char * crm_itoa_stack(int an_int, char *buf, size_t len)
Definition: strings.c:24
#define CRM_SYSTEM_CRMD
Definition: crm.h:102
long long int crm_time_get_seconds(crm_time_t *dt)
Definition: iso8601.c:308
int pcmk__score_green
Definition: utils.c:55
#define CRM_XS
Definition: logging.h:54
#define CRM_SYSTEM_STONITHD
Definition: crm.h:106
#define CRM_SYSTEM_CIB
Definition: crm.h:101
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition: pid.c:214
#define CRM_SYSTEM_TENGINE
Definition: crm.h:105
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:314
int pcmk__score_red
Definition: utils.c:54
#define CRM_META
Definition: crm.h:71
#define crm_err(fmt, args...)
Definition: logging.h:363
#define CRM_ASSERT(expr)
Definition: results.h:42
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:625
#define CRM_INFINITY_S
Definition: crm.h:82
gboolean crm_config_warning
Definition: utils.c:51
int compare_version(const char *version1, const char *version2)
Definition: utils.c:227
int pcmk__score_yellow
Definition: utils.c:56
#define CRM_SYSTEM_LRMD
Definition: crm.h:103
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition: pid.c:172
#define pcmk_ok
Definition: results.h:67
#define MD5_DIGEST_SIZE
Definition: md5.h:30
Wrappers for and extensions to libqb IPC.
char * generate_hash_key(const char *crm_msg_reference, const char *sys)
Definition: utils.c:120
#define CRM_PLUS_INFINITY_S
Definition: crm.h:83
char * crm_md5sum(const char *buffer)
Definition: utils.c:570
bool crm_is_daemon
Definition: logging.c:40
char * pcmk_hostname(void)
Get the local hostname.
Definition: utils.c:612
#define safe_str_eq(a, b)
Definition: util.h:65
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:341
char * name
Definition: pcmk_fence.c:30
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define crm_info(fmt, args...)
Definition: logging.h:366
char * score2char(int score)
Definition: utils.c:108
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:141