pacemaker  2.0.5-ba59be712
Scalable High-Availability cluster resource manager
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 #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 bool pcmk__config_error = false;
53 bool pcmk__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  score_f = crm_parse_int(score, NULL);
84  if (score_f > 0 && score_f > CRM_SCORE_INFINITY) {
85  score_f = CRM_SCORE_INFINITY;
86 
87  } else if (score_f < 0 && score_f < -CRM_SCORE_INFINITY) {
88  score_f = -CRM_SCORE_INFINITY;
89  }
90  }
91 
92  return score_f;
93 }
94 
95 char *
96 score2char_stack(int score, char *buf, size_t len)
97 {
98  if (score >= CRM_SCORE_INFINITY) {
99  strncpy(buf, CRM_INFINITY_S, 9);
100  } else if (score <= -CRM_SCORE_INFINITY) {
101  strncpy(buf, CRM_MINUS_INFINITY_S , 10);
102  } else {
103  return crm_itoa_stack(score, buf, len);
104  }
105 
106  return buf;
107 }
108 
109 char *
110 score2char(int score)
111 {
112  if (score >= CRM_SCORE_INFINITY) {
113  return strdup(CRM_INFINITY_S);
114 
115  } else if (score <= -CRM_SCORE_INFINITY) {
116  return strdup(CRM_MINUS_INFINITY_S);
117  }
118  return crm_itoa(score);
119 }
120 
121 int
122 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
123 {
124  int rc = pcmk_ok;
125  char *buffer = NULL;
126  struct passwd pwd;
127  struct passwd *pwentry = NULL;
128 
129  buffer = calloc(1, PW_BUFFER_LEN);
130  if (buffer == NULL) {
131  return -ENOMEM;
132  }
133 
134  rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
135  if (pwentry) {
136  if (uid) {
137  *uid = pwentry->pw_uid;
138  }
139  if (gid) {
140  *gid = pwentry->pw_gid;
141  }
142  crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
143 
144  } else {
145  rc = rc? -rc : -EINVAL;
146  crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
147  }
148 
149  free(buffer);
150  return rc;
151 }
152 
161 int
162 pcmk_daemon_user(uid_t *uid, gid_t *gid)
163 {
164  static uid_t daemon_uid;
165  static gid_t daemon_gid;
166  static bool found = false;
167  int rc = pcmk_err_generic;
168 
169  if (!found) {
170  rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
171  if (rc == pcmk_ok) {
172  found = true;
173  }
174  }
175  if (found) {
176  if (uid) {
177  *uid = daemon_uid;
178  }
179  if (gid) {
180  *gid = daemon_gid;
181  }
182  }
183  return rc;
184 }
185 
193 static int
194 version_helper(const char *text, const char **end_text)
195 {
196  int atoi_result = -1;
197 
198  CRM_ASSERT(end_text != NULL);
199 
200  errno = 0;
201 
202  if (text != NULL && text[0] != 0) {
203  /* seemingly sacrificing const-correctness -- because while strtol
204  doesn't modify the input, it doesn't want to artificially taint the
205  "end_text" pointer-to-pointer-to-first-char-in-string with constness
206  in case the input wasn't actually constant -- by semantic definition
207  not a single character will get modified so it shall be perfectly
208  safe to make compiler happy with dropping "const" qualifier here */
209  atoi_result = (int) strtol(text, (char **) end_text, 10);
210 
211  if (errno == EINVAL) {
212  crm_err("Conversion of '%s' %c failed", text, text[0]);
213  atoi_result = -1;
214  }
215  }
216  return atoi_result;
217 }
218 
219 /*
220  * version1 < version2 : -1
221  * version1 = version2 : 0
222  * version1 > version2 : 1
223  */
224 int
225 compare_version(const char *version1, const char *version2)
226 {
227  int rc = 0;
228  int lpc = 0;
229  const char *ver1_iter, *ver2_iter;
230 
231  if (version1 == NULL && version2 == NULL) {
232  return 0;
233  } else if (version1 == NULL) {
234  return -1;
235  } else if (version2 == NULL) {
236  return 1;
237  }
238 
239  ver1_iter = version1;
240  ver2_iter = version2;
241 
242  while (1) {
243  int digit1 = 0;
244  int digit2 = 0;
245 
246  lpc++;
247 
248  if (ver1_iter == ver2_iter) {
249  break;
250  }
251 
252  if (ver1_iter != NULL) {
253  digit1 = version_helper(ver1_iter, &ver1_iter);
254  }
255 
256  if (ver2_iter != NULL) {
257  digit2 = version_helper(ver2_iter, &ver2_iter);
258  }
259 
260  if (digit1 < digit2) {
261  rc = -1;
262  break;
263 
264  } else if (digit1 > digit2) {
265  rc = 1;
266  break;
267  }
268 
269  if (ver1_iter != NULL && *ver1_iter == '.') {
270  ver1_iter++;
271  }
272  if (ver1_iter != NULL && *ver1_iter == '\0') {
273  ver1_iter = NULL;
274  }
275 
276  if (ver2_iter != NULL && *ver2_iter == '.') {
277  ver2_iter++;
278  }
279  if (ver2_iter != NULL && *ver2_iter == 0) {
280  ver2_iter = NULL;
281  }
282  }
283 
284  if (rc == 0) {
285  crm_trace("%s == %s (%d)", version1, version2, lpc);
286  } else 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  }
291 
292  return rc;
293 }
294 
306 guint
307 crm_parse_interval_spec(const char *input)
308 {
309  long long msec = -1;
310 
311  errno = 0;
312  if (input == NULL) {
313  return 0;
314 
315  } else if (input[0] == 'P') {
316  crm_time_t *period_s = crm_time_parse_duration(input);
317 
318  if (period_s) {
319  msec = 1000 * crm_time_get_seconds(period_s);
320  crm_time_free(period_s);
321  }
322 
323  } else {
324  msec = crm_get_msec(input);
325  }
326 
327  if (msec < 0) {
328  crm_warn("Using 0 instead of '%s'", input);
329  errno = EINVAL;
330  return 0;
331  }
332  return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
333 }
334 
335 /* coverity[+kill] */
336 void
337 crm_abort(const char *file, const char *function, int line,
338  const char *assert_condition, gboolean do_core, gboolean do_fork)
339 {
340  int rc = 0;
341  int pid = 0;
342  int status = 0;
343 
344  /* Implied by the parent's error logging below */
345  /* crm_write_blackbox(0); */
346 
347  if (!pcmk__is_daemon) {
348  /* This is a command line tool - do not fork */
349 
350  /* crm_add_logfile(NULL); * Record it to a file? */
351  crm_enable_stderr(TRUE); /* Make sure stderr is enabled so we can tell the caller */
352  do_fork = FALSE; /* Just crash if needed */
353  }
354 
355  if (do_core == FALSE) {
356  crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition);
357  return;
358 
359  } else if (do_fork) {
360  pid = fork();
361 
362  } else {
363  crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition);
364  }
365 
366  if (pid == -1) {
367  crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s",
368  function, file, line, assert_condition);
369  return;
370 
371  } else if(pid == 0) {
372  /* Child process */
373  abort();
374  return;
375  }
376 
377  /* Parent process */
378  crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s",
379  function, pid, file, line, assert_condition);
380  crm_write_blackbox(SIGTRAP, NULL);
381 
382  do {
383  rc = waitpid(pid, &status, 0);
384  if(rc == pid) {
385  return; /* Job done */
386  }
387 
388  } while(errno == EINTR);
389 
390  if (errno == ECHILD) {
391  /* crm_mon does this */
392  crm_trace("Cannot wait on forked child %d - SIGCHLD is probably set to SIG_IGN", pid);
393  return;
394  }
395  crm_perror(LOG_ERR, "Cannot wait on forked child %d", pid);
396 }
397 
409 void
410 pcmk__daemonize(const char *name, const char *pidfile)
411 {
412  int rc;
413  pid_t pid;
414 
415  /* Check before we even try... */
416  rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
417  if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
418  crm_err("%s: already running [pid %lld in %s]",
419  name, (long long) pid, pidfile);
420  printf("%s: already running [pid %lld in %s]\n",
421  name, (long long) pid, pidfile);
423  }
424 
425  pid = fork();
426  if (pid < 0) {
427  fprintf(stderr, "%s: could not start daemon\n", name);
428  crm_perror(LOG_ERR, "fork");
430 
431  } else if (pid > 0) {
433  }
434 
435  rc = pcmk__lock_pidfile(pidfile, name);
436  if (rc != pcmk_rc_ok) {
437  crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
438  pidfile, name, pcmk_rc_str(rc), rc);
439  printf("Could not lock '%s' for %s: %s (%d)\n",
440  pidfile, name, pcmk_rc_str(rc), rc);
442  }
443 
444  umask(S_IWGRP | S_IWOTH | S_IROTH);
445 
446  close(STDIN_FILENO);
447  pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
448 
449  close(STDOUT_FILENO);
450  pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
451 
452  close(STDERR_FILENO);
453  pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
454 }
455 
456 char *
457 crm_meta_name(const char *field)
458 {
459  int lpc = 0;
460  int max = 0;
461  char *crm_name = NULL;
462 
463  CRM_CHECK(field != NULL, return NULL);
464  crm_name = crm_strdup_printf(CRM_META "_%s", field);
465 
466  /* Massage the names so they can be used as shell variables */
467  max = strlen(crm_name);
468  for (; lpc < max; lpc++) {
469  switch (crm_name[lpc]) {
470  case '-':
471  crm_name[lpc] = '_';
472  break;
473  }
474  }
475  return crm_name;
476 }
477 
478 const char *
479 crm_meta_value(GHashTable * hash, const char *field)
480 {
481  char *key = NULL;
482  const char *value = NULL;
483 
484  key = crm_meta_name(field);
485  if (key) {
486  value = g_hash_table_lookup(hash, key);
487  free(key);
488  }
489 
490  return value;
491 }
492 
493 #ifdef HAVE_UUID_UUID_H
494 # include <uuid/uuid.h>
495 #endif
496 
497 char *
499 {
500  unsigned char uuid[16];
501  char *buffer = malloc(37); /* Including NUL byte */
502 
503  uuid_generate(uuid);
504  uuid_unparse(uuid, buffer);
505  return buffer;
506 }
507 
508 #ifdef HAVE_GNUTLS_GNUTLS_H
509 void
510 crm_gnutls_global_init(void)
511 {
512  signal(SIGPIPE, SIG_IGN);
513  gnutls_global_init();
514 }
515 #endif
516 
522 char *
524 {
525  struct utsname hostinfo;
526 
527  return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
528 }
529 
530 bool
531 pcmk_str_is_infinity(const char *s) {
533 }
534 
535 bool
537  return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
538 }
Services API.
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:215
char uname[MAX_NAME]
Definition: internal.h:85
int pcmk__score_green
Definition: utils.c:57
char * pcmk_hostname()
Get the local hostname.
Definition: utils.c:523
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:410
A dumping ground.
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:122
const char * pcmk_strerror(int rc)
Definition: results.c:58
void crm_enable_stderr(int enable)
Definition: logging.c:906
#define crm_crit(fmt, args...)
Definition: logging.h:346
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:479
_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:536
struct crm_time_s crm_time_t
Definition: iso8601.h:32
bool pcmk__config_error
Definition: utils.c:52
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:340
#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:134
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:531
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
uint32_t pid
Definition: internal.h:81
void pcmk__daemonize(const char *name, const char *pidfile)
Definition: utils.c:410
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 CRM_SCORE_INFINITY
Definition: crm.h:81
Wrappers for and extensions to glib mainloop.
char * crm_meta_name(const char *field)
Definition: utils.c:457
#define PW_BUFFER_LEN
Definition: utils.c:47
#define crm_warn(fmt, args...)
Definition: logging.h:348
int rc
Definition: pcmk_fence.c:35
guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:307
int pcmk__score_yellow
Definition: utils.c:58
Utility functions.
char * score2char(int score)
Definition: utils.c:110
#define crm_trace(fmt, args...)
Definition: logging.h:353
#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
char * crm_generate_uuid(void)
Definition: utils.c:498
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:866
char * crm_itoa_stack(int an_int, char *buf, size_t len)
Definition: strings.c:28
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:225
#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:337
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:298
int pcmk__score_red
Definition: utils.c:56
G_GNUC_INTERNAL bool pcmk__is_daemon
Definition: logging.c:38
#define CRM_META
Definition: crm.h:71
int char2score(const char *score)
Definition: utils.c:61
#define crm_err(fmt, args...)
Definition: logging.h:347
#define CRM_ASSERT(expr)
Definition: results.h:42
#define CRM_INFINITY_S
Definition: crm.h:82
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
IPC interface to Pacemaker daemons.
#define CRM_PLUS_INFINITY_S
Definition: crm.h:83
bool pcmk__config_warning
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:162
char * name
Definition: pcmk_fence.c:31
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define crm_info(fmt, args...)
Definition: logging.h:350
char * score2char_stack(int score, char *buf, size_t len)
Definition: utils.c:96
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:141