LCOV - code coverage report
Current view: top level - common - utils.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 107 183 58.5 %
Date: 2024-05-07 11:09:47 Functions: 9 15 60.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2004-2024 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             : #include <grp.h>
      31             : 
      32             : #include <qb/qbdefs.h>
      33             : 
      34             : #include <crm/crm.h>
      35             : #include <crm/services.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             : CRM_TRACE_INIT_DATA(common);
      47             : 
      48             : gboolean crm_config_error = FALSE;
      49             : gboolean crm_config_warning = FALSE;
      50             : char *crm_system_name = NULL;
      51             : 
      52             : bool
      53           5 : pcmk__is_user_in_group(const char *user, const char *group)
      54             : {
      55             :     struct group *grent;
      56             :     char **gr_mem;
      57             : 
      58           5 :     if (user == NULL || group == NULL) {
      59           2 :         return false;
      60             :     }
      61             :     
      62           3 :     setgrent();
      63           9 :     while ((grent = getgrent()) != NULL) {
      64           7 :         if (grent->gr_mem == NULL) {
      65           0 :             continue;
      66             :         }
      67             : 
      68           7 :         if(strcmp(group, grent->gr_name) != 0) {
      69           5 :             continue;
      70             :         }
      71             : 
      72           2 :         gr_mem = grent->gr_mem;
      73           4 :         while (*gr_mem != NULL) {
      74           3 :             if (!strcmp(user, *gr_mem++)) {
      75           1 :                 endgrent();
      76           1 :                 return true;
      77             :             }
      78             :         }
      79             :     }
      80           2 :     endgrent();
      81           2 :     return false;
      82             : }
      83             : 
      84             : int
      85           7 : crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
      86             : {
      87           7 :     int rc = pcmk_ok;
      88           7 :     char *buffer = NULL;
      89             :     struct passwd pwd;
      90           7 :     struct passwd *pwentry = NULL;
      91             : 
      92           7 :     buffer = calloc(1, PCMK__PW_BUFFER_LEN);
      93           7 :     if (buffer == NULL) {
      94           1 :         return -ENOMEM;
      95             :     }
      96             : 
      97           6 :     rc = getpwnam_r(name, &pwd, buffer, PCMK__PW_BUFFER_LEN, &pwentry);
      98           6 :     if (pwentry) {
      99           3 :         if (uid) {
     100           2 :             *uid = pwentry->pw_uid;
     101             :         }
     102           3 :         if (gid) {
     103           2 :             *gid = pwentry->pw_gid;
     104             :         }
     105           3 :         crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
     106             : 
     107             :     } else {
     108           3 :         rc = rc? -rc : -EINVAL;
     109           3 :         crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
     110             :     }
     111             : 
     112           6 :     free(buffer);
     113           6 :     return rc;
     114             : }
     115             : 
     116             : /*!
     117             :  * \brief Get user and group IDs of pacemaker daemon user
     118             :  *
     119             :  * \param[out] uid  If non-NULL, where to store daemon user ID
     120             :  * \param[out] gid  If non-NULL, where to store daemon group ID
     121             :  *
     122             :  * \return pcmk_ok on success, -errno otherwise
     123             :  */
     124             : int
     125           3 : pcmk_daemon_user(uid_t *uid, gid_t *gid)
     126             : {
     127             :     static uid_t daemon_uid;
     128             :     static gid_t daemon_gid;
     129             :     static bool found = false;
     130           3 :     int rc = pcmk_ok;
     131             : 
     132           3 :     if (!found) {
     133           2 :         rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
     134           2 :         if (rc == pcmk_ok) {
     135           1 :             found = true;
     136             :         }
     137             :     }
     138           3 :     if (found) {
     139           2 :         if (uid) {
     140           1 :             *uid = daemon_uid;
     141             :         }
     142           2 :         if (gid) {
     143           1 :             *gid = daemon_gid;
     144             :         }
     145             :     }
     146           3 :     return rc;
     147             : }
     148             : 
     149             : /*!
     150             :  * \internal
     151             :  * \brief Return the integer equivalent of a portion of a string
     152             :  *
     153             :  * \param[in]  text      Pointer to beginning of string portion
     154             :  * \param[out] end_text  This will point to next character after integer
     155             :  */
     156             : static int
     157          76 : version_helper(const char *text, const char **end_text)
     158             : {
     159          76 :     int atoi_result = -1;
     160             : 
     161          76 :     CRM_ASSERT(end_text != NULL);
     162             : 
     163          76 :     errno = 0;
     164             : 
     165          76 :     if (text != NULL && text[0] != 0) {
     166             :         /* seemingly sacrificing const-correctness -- because while strtol
     167             :            doesn't modify the input, it doesn't want to artificially taint the
     168             :            "end_text" pointer-to-pointer-to-first-char-in-string with constness
     169             :            in case the input wasn't actually constant -- by semantic definition
     170             :            not a single character will get modified so it shall be perfectly
     171             :            safe to make compiler happy with dropping "const" qualifier here */
     172          76 :         atoi_result = (int) strtol(text, (char **) end_text, 10);
     173             : 
     174          76 :         if (errno == EINVAL) {
     175           0 :             crm_err("Conversion of '%s' %c failed", text, text[0]);
     176           0 :             atoi_result = -1;
     177             :         }
     178             :     }
     179          76 :     return atoi_result;
     180             : }
     181             : 
     182             : /*
     183             :  * version1 < version2 : -1
     184             :  * version1 = version2 :  0
     185             :  * version1 > version2 :  1
     186             :  */
     187             : int
     188          28 : compare_version(const char *version1, const char *version2)
     189             : {
     190          28 :     int rc = 0;
     191          28 :     int lpc = 0;
     192             :     const char *ver1_iter, *ver2_iter;
     193             : 
     194          28 :     if (version1 == NULL && version2 == NULL) {
     195           1 :         return 0;
     196          27 :     } else if (version1 == NULL) {
     197           2 :         return -1;
     198          25 :     } else if (version2 == NULL) {
     199           2 :         return 1;
     200             :     }
     201             : 
     202          23 :     ver1_iter = version1;
     203          23 :     ver2_iter = version2;
     204             : 
     205          23 :     while (1) {
     206          46 :         int digit1 = 0;
     207          46 :         int digit2 = 0;
     208             : 
     209          46 :         lpc++;
     210             : 
     211          46 :         if (ver1_iter == ver2_iter) {
     212           5 :             break;
     213             :         }
     214             : 
     215          41 :         if (ver1_iter != NULL) {
     216          39 :             digit1 = version_helper(ver1_iter, &ver1_iter);
     217             :         }
     218             : 
     219          41 :         if (ver2_iter != NULL) {
     220          37 :             digit2 = version_helper(ver2_iter, &ver2_iter);
     221             :         }
     222             : 
     223          41 :         if (digit1 < digit2) {
     224          10 :             rc = -1;
     225          10 :             break;
     226             : 
     227          31 :         } else if (digit1 > digit2) {
     228           8 :             rc = 1;
     229           8 :             break;
     230             :         }
     231             : 
     232          23 :         if (ver1_iter != NULL && *ver1_iter == '.') {
     233          19 :             ver1_iter++;
     234             :         }
     235          23 :         if (ver1_iter != NULL && *ver1_iter == '\0') {
     236           4 :             ver1_iter = NULL;
     237             :         }
     238             : 
     239          23 :         if (ver2_iter != NULL && *ver2_iter == '.') {
     240          17 :             ver2_iter++;
     241             :         }
     242          23 :         if (ver2_iter != NULL && *ver2_iter == 0) {
     243           4 :             ver2_iter = NULL;
     244             :         }
     245             :     }
     246             : 
     247          23 :     if (rc == 0) {
     248           5 :         crm_trace("%s == %s (%d)", version1, version2, lpc);
     249          18 :     } else if (rc < 0) {
     250          10 :         crm_trace("%s < %s (%d)", version1, version2, lpc);
     251           8 :     } else if (rc > 0) {
     252           8 :         crm_trace("%s > %s (%d)", version1, version2, lpc);
     253             :     }
     254             : 
     255          23 :     return rc;
     256             : }
     257             : 
     258             : /*!
     259             :  * \internal
     260             :  * \brief Log a failed assertion
     261             :  *
     262             :  * \param[in] file              File making the assertion
     263             :  * \param[in] function          Function making the assertion
     264             :  * \param[in] line              Line of file making the assertion
     265             :  * \param[in] assert_condition  String representation of assertion
     266             :  */
     267             : static void
     268          70 : log_assertion_as(const char *file, const char *function, int line,
     269             :                  const char *assert_condition)
     270             : {
     271          70 :     if (!pcmk__is_daemon) {
     272          70 :         crm_enable_stderr(TRUE); // Make sure command-line user sees message
     273             :     }
     274          70 :     crm_err("%s: Triggered fatal assertion at %s:%d : %s",
     275             :             function, file, line, assert_condition);
     276          70 : }
     277             : 
     278             : /* coverity[+kill] */
     279             : /*!
     280             :  * \internal
     281             :  * \brief Log a failed assertion and abort
     282             :  *
     283             :  * \param[in] file              File making the assertion
     284             :  * \param[in] function          Function making the assertion
     285             :  * \param[in] line              Line of file making the assertion
     286             :  * \param[in] assert_condition  String representation of assertion
     287             :  *
     288             :  * \note This does not return
     289             :  */
     290             : static _Noreturn void
     291          32 : abort_as(const char *file, const char *function, int line,
     292             :          const char *assert_condition)
     293             : {
     294          32 :     log_assertion_as(file, function, line, assert_condition);
     295          32 :     abort();
     296             : }
     297             : 
     298             : /* coverity[+kill] */
     299             : /*!
     300             :  * \internal
     301             :  * \brief Handle a failed assertion
     302             :  *
     303             :  * When called by a daemon, fork a child that aborts (to dump core), otherwise
     304             :  * abort the current process.
     305             :  *
     306             :  * \param[in] file              File making the assertion
     307             :  * \param[in] function          Function making the assertion
     308             :  * \param[in] line              Line of file making the assertion
     309             :  * \param[in] assert_condition  String representation of assertion
     310             :  */
     311             : static void
     312           0 : fail_assert_as(const char *file, const char *function, int line,
     313             :                const char *assert_condition)
     314             : {
     315           0 :     int status = 0;
     316           0 :     pid_t pid = 0;
     317             : 
     318           0 :     if (!pcmk__is_daemon) {
     319           0 :         abort_as(file, function, line, assert_condition); // does not return
     320             :     }
     321             : 
     322           0 :     pid = fork();
     323           0 :     switch (pid) {
     324           0 :         case -1: // Fork failed
     325           0 :             crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
     326             :                      ": %s", function, file, line, assert_condition);
     327           0 :             break;
     328             : 
     329           0 :         case 0: // Child process: just abort to dump core
     330           0 :             abort();
     331             :             break;
     332             : 
     333           0 :         default: // Parent process: wait for child
     334           0 :             crm_err("%s: Forked child [%d] to record non-fatal assertion at "
     335             :                     "%s:%d : %s", function, pid, file, line, assert_condition);
     336           0 :             crm_write_blackbox(SIGTRAP, NULL);
     337             :             do {
     338           0 :                 if (waitpid(pid, &status, 0) == pid) {
     339           0 :                     return; // Child finished dumping core
     340             :                 }
     341           0 :             } while (errno == EINTR);
     342           0 :             if (errno == ECHILD) {
     343             :                 // crm_mon ignores SIGCHLD
     344           0 :                 crm_trace("Cannot wait on forked child [%d] "
     345             :                           "(SIGCHLD is probably ignored)", pid);
     346             :             } else {
     347           0 :                 crm_err("Cannot wait on forked child [%d]: %s",
     348             :                         pid, pcmk_rc_str(errno));
     349             :             }
     350           0 :             break;
     351             :     }
     352             : }
     353             : 
     354             : /* coverity[+kill] */
     355             : void
     356           0 : crm_abort(const char *file, const char *function, int line,
     357             :           const char *assert_condition, gboolean do_core, gboolean do_fork)
     358             : {
     359           0 :     if (!do_fork) {
     360           0 :         abort_as(file, function, line, assert_condition);
     361           0 :     } else if (do_core) {
     362           0 :         fail_assert_as(file, function, line, assert_condition);
     363             :     } else {
     364           0 :         log_assertion_as(file, function, line, assert_condition);
     365             :     }
     366           0 : }
     367             : 
     368             : /*!
     369             :  * \internal
     370             :  * \brief Convert the current process to a daemon process
     371             :  *
     372             :  * Fork a child process, exit the parent, create a PID file with the current
     373             :  * process ID, and close the standard input/output/error file descriptors.
     374             :  * Exit instead if a daemon is already running and using the PID file.
     375             :  *
     376             :  * \param[in] name     Daemon executable name
     377             :  * \param[in] pidfile  File name to use as PID file
     378             :  */
     379             : void
     380           0 : pcmk__daemonize(const char *name, const char *pidfile)
     381             : {
     382             :     int rc;
     383             :     pid_t pid;
     384             : 
     385             :     /* Check before we even try... */
     386           0 :     rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
     387           0 :     if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
     388           0 :         crm_err("%s: already running [pid %lld in %s]",
     389             :                 name, (long long) pid, pidfile);
     390           0 :         printf("%s: already running [pid %lld in %s]\n",
     391             :                name, (long long) pid, pidfile);
     392           0 :         crm_exit(CRM_EX_ERROR);
     393             :     }
     394             : 
     395           0 :     pid = fork();
     396           0 :     if (pid < 0) {
     397           0 :         fprintf(stderr, "%s: could not start daemon\n", name);
     398           0 :         crm_perror(LOG_ERR, "fork");
     399           0 :         crm_exit(CRM_EX_OSERR);
     400             : 
     401           0 :     } else if (pid > 0) {
     402           0 :         crm_exit(CRM_EX_OK);
     403             :     }
     404             : 
     405           0 :     rc = pcmk__lock_pidfile(pidfile, name);
     406           0 :     if (rc != pcmk_rc_ok) {
     407           0 :         crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
     408             :                 pidfile, name, pcmk_rc_str(rc), rc);
     409           0 :         printf("Could not lock '%s' for %s: %s (%d)\n",
     410             :                pidfile, name, pcmk_rc_str(rc), rc);
     411           0 :         crm_exit(CRM_EX_ERROR);
     412             :     }
     413             : 
     414           0 :     umask(S_IWGRP | S_IWOTH | S_IROTH);
     415             : 
     416           0 :     close(STDIN_FILENO);
     417           0 :     pcmk__open_devnull(O_RDONLY);   // stdin (fd 0)
     418             : 
     419           0 :     close(STDOUT_FILENO);
     420           0 :     pcmk__open_devnull(O_WRONLY);   // stdout (fd 1)
     421             : 
     422           0 :     close(STDERR_FILENO);
     423           0 :     pcmk__open_devnull(O_WRONLY);   // stderr (fd 2)
     424           0 : }
     425             : 
     426             : #ifdef HAVE_UUID_UUID_H
     427             : #  include <uuid/uuid.h>
     428             : #endif
     429             : 
     430             : char *
     431           0 : crm_generate_uuid(void)
     432             : {
     433             :     unsigned char uuid[16];
     434           0 :     char *buffer = malloc(37);  /* Including NUL byte */
     435             : 
     436           0 :     pcmk__mem_assert(buffer);
     437           0 :     uuid_generate(uuid);
     438           0 :     uuid_unparse(uuid, buffer);
     439           0 :     return buffer;
     440             : }
     441             : 
     442             : #ifdef HAVE_GNUTLS_GNUTLS_H
     443             : void
     444           0 : crm_gnutls_global_init(void)
     445             : {
     446           0 :     signal(SIGPIPE, SIG_IGN);
     447           0 :     gnutls_global_init();
     448           0 : }
     449             : #endif
     450             : 
     451             : bool
     452         526 : pcmk_str_is_infinity(const char *s) {
     453         526 :     return pcmk__str_any_of(s, PCMK_VALUE_INFINITY, PCMK_VALUE_PLUS_INFINITY,
     454             :                             NULL);
     455             : }
     456             : 
     457             : bool
     458         458 : pcmk_str_is_minus_infinity(const char *s) {
     459         458 :     return pcmk__str_eq(s, PCMK_VALUE_MINUS_INFINITY, pcmk__str_none);
     460             : }
     461             : 
     462             : /*!
     463             :  * \internal
     464             :  * \brief Sleep for given milliseconds
     465             :  *
     466             :  * \param[in] ms  Time to sleep
     467             :  *
     468             :  * \note The full time might not be slept if a signal is received.
     469             :  */
     470             : void
     471           0 : pcmk__sleep_ms(unsigned int ms)
     472             : {
     473             :     // @TODO Impose a sane maximum sleep to avoid hanging a process for long
     474             :     //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
     475             : 
     476             :     // Use sleep() for any whole seconds
     477           0 :     if (ms >= 1000) {
     478           0 :         sleep(ms / 1000);
     479           0 :         ms -= ms / 1000;
     480             :     }
     481             : 
     482           0 :     if (ms == 0) {
     483           0 :         return;
     484             :     }
     485             : 
     486             : #if defined(HAVE_NANOSLEEP)
     487             :     // nanosleep() is POSIX-2008, so prefer that
     488             :     {
     489           0 :         struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
     490             : 
     491           0 :         nanosleep(&req, NULL);
     492             :     }
     493             : #elif defined(HAVE_USLEEP)
     494             :     // usleep() is widely available, though considered obsolete
     495             :     usleep((useconds_t) ms);
     496             : #else
     497             :     // Otherwise use a trick with select() timeout
     498             :     {
     499             :         struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
     500             : 
     501             :         select(0, NULL, NULL, NULL, &tv);
     502             :     }
     503             : #endif
     504             : }
     505             : 
     506             : // Deprecated functions kept only for backward API compatibility
     507             : // LCOV_EXCL_START
     508             : 
     509             : #include <crm/common/util_compat.h>
     510             : 
     511             : guint
     512             : crm_parse_interval_spec(const char *input)
     513             : {
     514             :     long long msec = -1;
     515             : 
     516             :     errno = 0;
     517             :     if (input == NULL) {
     518             :         return 0;
     519             : 
     520             :     } else if (input[0] == 'P') {
     521             :         crm_time_t *period_s = crm_time_parse_duration(input);
     522             : 
     523             :         if (period_s) {
     524             :             msec = 1000 * crm_time_get_seconds(period_s);
     525             :             crm_time_free(period_s);
     526             :         }
     527             : 
     528             :     } else {
     529             :         msec = crm_get_msec(input);
     530             :     }
     531             : 
     532             :     if (msec < 0) {
     533             :         crm_warn("Using 0 instead of '%s'", input);
     534             :         errno = EINVAL;
     535             :         return 0;
     536             :     }
     537             :     return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
     538             : }
     539             : 
     540             : char *
     541             : pcmk_hostname(void)
     542             : {
     543             :     struct utsname hostinfo;
     544             : 
     545             :     return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
     546             : }
     547             : 
     548             : // LCOV_EXCL_STOP
     549             : // End deprecated API

Generated by: LCOV version 1.14