root/lib/common/utils.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. char2score
  2. score2char_stack
  3. score2char
  4. crm_user_lookup
  5. pcmk_daemon_user
  6. version_helper
  7. compare_version
  8. crm_parse_interval_spec
  9. log_assertion_as
  10. abort_as
  11. fail_assert_as
  12. crm_abort
  13. pcmk__daemonize
  14. crm_meta_name
  15. crm_meta_value
  16. crm_generate_uuid
  17. crm_gnutls_global_init
  18. pcmk_hostname
  19. pcmk_str_is_infinity
  20. pcmk_str_is_minus_infinity
  21. pcmk__sleep_ms

   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 
  56 int pcmk__score_red = 0;
  57 int pcmk__score_green = 0;
  58 int pcmk__score_yellow = 0;
  59 
  60 int
  61 char2score(const char *score)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 160 /*!
 161  * \brief Get user and group IDs of pacemaker daemon user
 162  *
 163  * \param[out] uid  If non-NULL, where to store daemon user ID
 164  * \param[out] gid  If non-NULL, where to store daemon group ID
 165  *
 166  * \return pcmk_ok on success, -errno otherwise
 167  */
 168 int
 169 pcmk_daemon_user(uid_t *uid, gid_t *gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 193 /*!
 194  * \internal
 195  * \brief Return the integer equivalent of a portion of a string
 196  *
 197  * \param[in]  text      Pointer to beginning of string portion
 198  * \param[out] end_text  This will point to next character after integer
 199  */
 200 static int
 201 version_helper(const char *text, const char **end_text)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 302 /*!
 303  * \brief Parse milliseconds from a Pacemaker interval specification
 304  *
 305  * \param[in] input  Pacemaker time interval specification (a bare number of
 306  *                   seconds, a number with a unit optionally with whitespace
 307  *                   before and/or after the number, or an ISO 8601 duration)
 308  *
 309  * \return Milliseconds equivalent of given specification on success (limited
 310  *         to the range of an unsigned integer), 0 if input is NULL,
 311  *         or 0 (and set errno to EINVAL) on error
 312  */
 313 guint
 314 crm_parse_interval_spec(const char *input)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 342 /*!
 343  * \internal
 344  * \brief Log a failed assertion
 345  *
 346  * \param[in] file              File making the assertion
 347  * \param[in] function          Function making the assertion
 348  * \param[in] line              Line of file making the assertion
 349  * \param[in] assert_condition  String representation of assertion
 350  */
 351 static void
 352 log_assertion_as(const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 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] */
 363 /*!
 364  * \internal
 365  * \brief Log a failed assertion and abort
 366  *
 367  * \param[in] file              File making the assertion
 368  * \param[in] function          Function making the assertion
 369  * \param[in] line              Line of file making the assertion
 370  * \param[in] assert_condition  String representation of assertion
 371  *
 372  * \note This does not return
 373  */
 374 static _Noreturn void
 375 abort_as(const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 376          const char *assert_condition)
 377 {
 378     log_assertion_as(file, function, line, assert_condition);
 379     abort();
 380 }
 381 
 382 /* coverity[+kill] */
 383 /*!
 384  * \internal
 385  * \brief Handle a failed assertion
 386  *
 387  * When called by a daemon, fork a child that aborts (to dump core), otherwise
 388  * abort the current process.
 389  *
 390  * \param[in] file              File making the assertion
 391  * \param[in] function          Function making the assertion
 392  * \param[in] line              Line of file making the assertion
 393  * \param[in] assert_condition  String representation of assertion
 394  */
 395 static void
 396 fail_assert_as(const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 452 /*!
 453  * \internal
 454  * \brief Convert the current process to a daemon process
 455  *
 456  * Fork a child process, exit the parent, create a PID file with the current
 457  * process ID, and close the standard input/output/error file descriptors.
 458  * Exit instead if a daemon is already running and using the PID file.
 459  *
 460  * \param[in] name     Daemon executable name
 461  * \param[in] pidfile  File name to use as PID file
 462  */
 463 void
 464 pcmk__daemonize(const char *name, const char *pidfile)
     /* [previous][next][first][last][top][bottom][index][help] */
 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);
 476         crm_exit(CRM_EX_ERROR);
 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");
 483         crm_exit(CRM_EX_OSERR);
 484 
 485     } else if (pid > 0) {
 486         crm_exit(CRM_EX_OK);
 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);
 495         crm_exit(CRM_EX_ERROR);
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 *
 552 crm_generate_uuid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 565 {
 566     signal(SIGPIPE, SIG_IGN);
 567     gnutls_global_init();
 568 }
 569 #endif
 570 
 571 /*!
 572  * \brief Get the local hostname
 573  *
 574  * \return Newly allocated string with name, or NULL (and set errno) on error
 575  */
 576 char *
 577 pcmk_hostname()
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
     /* [previous][next][first][last][top][bottom][index][help] */
 586     return pcmk__str_any_of(s, CRM_INFINITY_S, CRM_PLUS_INFINITY_S, NULL);
 587 }
 588 
 589 bool
 590 pcmk_str_is_minus_infinity(const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
 591     return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
 592 }
 593 
 594 /*!
 595  * \internal
 596  * \brief Sleep for given milliseconds
 597  *
 598  * \param[in] ms  Time to sleep
 599  *
 600  * \note The full time might not be slept if a signal is received.
 601  */
 602 void
 603 pcmk__sleep_ms(unsigned int ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */