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. crm_abort
  10. pcmk__daemonize
  11. crm_meta_name
  12. crm_meta_value
  13. crm_generate_uuid
  14. crm_gnutls_global_init
  15. pcmk_hostname
  16. pcmk_str_is_infinity
  17. pcmk_str_is_minus_infinity

   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 
  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         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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 153 /*!
 154  * \brief Get user and group IDs of pacemaker daemon user
 155  *
 156  * \param[out] uid  If non-NULL, where to store daemon user ID
 157  * \param[out] gid  If non-NULL, where to store daemon group ID
 158  *
 159  * \return pcmk_ok on success, -errno otherwise
 160  */
 161 int
 162 pcmk_daemon_user(uid_t *uid, gid_t *gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 186 /*!
 187  * \internal
 188  * \brief Return the integer equivalent of a portion of a string
 189  *
 190  * \param[in]  text      Pointer to beginning of string portion
 191  * \param[out] end_text  This will point to next character after integer
 192  */
 193 static int
 194 version_helper(const char *text, const char **end_text)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 295 /*!
 296  * \brief Parse milliseconds from a Pacemaker interval specification
 297  *
 298  * \param[in] input  Pacemaker time interval specification (a bare number of
 299  *                   seconds, a number with a unit optionally with whitespace
 300  *                   before and/or after the number, or an ISO 8601 duration)
 301  *
 302  * \return Milliseconds equivalent of given specification on success (limited
 303  *         to the range of an unsigned integer), 0 if input is NULL,
 304  *         or 0 (and set errno to EINVAL) on error
 305  */
 306 guint
 307 crm_parse_interval_spec(const char *input)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 398 /*!
 399  * \internal
 400  * \brief Convert the current process to a daemon process
 401  *
 402  * Fork a child process, exit the parent, create a PID file with the current
 403  * process ID, and close the standard input/output/error file descriptors.
 404  * Exit instead if a daemon is already running and using the PID file.
 405  *
 406  * \param[in] name     Daemon executable name
 407  * \param[in] pidfile  File name to use as PID file
 408  */
 409 void
 410 pcmk__daemonize(const char *name, const char *pidfile)
     /* [previous][next][first][last][top][bottom][index][help] */
 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);
 422         crm_exit(CRM_EX_ERROR);
 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");
 429         crm_exit(CRM_EX_OSERR);
 430 
 431     } else if (pid > 0) {
 432         crm_exit(CRM_EX_OK);
 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);
 441         crm_exit(CRM_EX_ERROR);
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 *
 498 crm_generate_uuid(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 511 {
 512     signal(SIGPIPE, SIG_IGN);
 513     gnutls_global_init();
 514 }
 515 #endif
 516 
 517 /*!
 518  * \brief Get the local hostname
 519  *
 520  * \return Newly allocated string with name, or NULL (and set errno) on error
 521  */
 522 char *
 523 pcmk_hostname()
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
     /* [previous][next][first][last][top][bottom][index][help] */
 532     return pcmk__str_any_of(s, CRM_INFINITY_S, CRM_PLUS_INFINITY_S, NULL);
 533 }
 534 
 535 bool
 536 pcmk_str_is_minus_infinity(const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
 537     return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
 538 }

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