Coverage Report

Created: 2026-10-30 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/common/utils.c
Line
Count
Source
1
/*
2
 * Copyright 2004-2026 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
#include <sys/stat.h>
13
#include <sys/utsname.h>
14
15
#include <stdbool.h>
16
#include <stdio.h>
17
#include <unistd.h>
18
#include <string.h>
19
#include <stdlib.h>
20
#include <limits.h>
21
#include <pwd.h>
22
#include <time.h>
23
#include <libgen.h>
24
#include <signal.h>
25
#include <grp.h>
26
27
#include <qb/qbdefs.h>
28
29
#include <crm/crm.h>
30
#include <crm/services.h>
31
#include <crm/cib/internal.h>
32
#include <crm/common/xml.h>
33
#include <crm/common/util.h>
34
#include <crm/common/ipc.h>
35
#include <crm/common/iso8601.h>
36
#include <crm/common/mainloop.h>
37
#include <libxml/parser.h>              // xmlCleanupParser()
38
#include <libxml2/libxml/relaxng.h>
39
40
#include "crmcommon_private.h"
41
42
bool pcmk__config_has_error = false;
43
bool pcmk__config_has_warning = false;
44
char *crm_system_name = NULL;
45
46
/*!
47
 * \brief Free all memory used by libcrmcommon
48
 *
49
 * Free all global memory allocated by the libcrmcommon library. This should be
50
 * called before exiting a process that uses the library, and the process should
51
 * not call any libcrmcommon or libxml2 APIs after calling this one.
52
 */
53
void
54
pcmk_common_cleanup(void)
55
0
{
56
    // @TODO This isn't really everything, move all cleanup here
57
0
    mainloop_cleanup();
58
0
    pcmk__schema_cleanup();
59
0
    crm_log_deinit();
60
61
    // Clean up external library global state
62
0
    qb_log_fini(); // Don't log anything after this point
63
0
    xmlCleanupParser();
64
0
}
65
66
bool
67
pcmk__is_user_in_group(const char *user, const char *group)
68
0
{
69
0
    struct group *grent;
70
0
    char **gr_mem;
71
72
0
    if (user == NULL || group == NULL) {
73
0
        return false;
74
0
    }
75
    
76
0
    setgrent();
77
0
    while ((grent = getgrent()) != NULL) {
78
0
        if (grent->gr_mem == NULL) {
79
0
            continue;
80
0
        }
81
82
0
        if(strcmp(group, grent->gr_name) != 0) {
83
0
            continue;
84
0
        }
85
86
0
        gr_mem = grent->gr_mem;
87
0
        while (*gr_mem != NULL) {
88
0
            if (!strcmp(user, *gr_mem++)) {
89
0
                endgrent();
90
0
                return true;
91
0
            }
92
0
        }
93
0
    }
94
0
    endgrent();
95
0
    return false;
96
0
}
97
98
int
99
pcmk__lookup_user(const char *name, uid_t *uid, gid_t *gid)
100
0
{
101
0
    struct passwd *pwentry = NULL;
102
103
0
    CRM_CHECK(name != NULL, return EINVAL);
104
105
    // getpwnam() is not thread-safe, but Pacemaker is single-threaded
106
0
    errno = 0;
107
0
    pwentry = getpwnam(name);
108
0
    if (pwentry == NULL) {
109
        /* Either an error occurred or no passwd entry was found.
110
         *
111
         * The value of errno is implementation-dependent if no passwd entry is
112
         * found. The POSIX specification does not consider it an error.
113
         * POSIX.1-2008 specifies that errno shall not be changed in this case,
114
         * while POSIX.1-2001 does not specify the value of errno in this case.
115
         * The man page on Linux notes that a variety of values have been
116
         * observed in practice. So an implementation may set errno to an
117
         * arbitrary value, despite the POSIX specification.
118
         *
119
         * However, if pwentry == NULL and errno == 0, then we know that no
120
         * matching entry was found and there was no error. So we default to
121
         * ENOENT as our return code.
122
         */
123
0
        return ((errno != 0)? errno : ENOENT);
124
0
    }
125
126
0
    if (uid != NULL) {
127
0
        *uid = pwentry->pw_uid;
128
0
    }
129
0
    if (gid != NULL) {
130
0
        *gid = pwentry->pw_gid;
131
0
    }
132
0
    pcmk__trace("User %s has uid=%lld gid=%lld", name,
133
0
                (long long) pwentry->pw_uid, (long long) pwentry->pw_gid);
134
135
0
    return pcmk_rc_ok;
136
0
}
137
138
/*!
139
 * \internal
140
 * \brief Get user and group IDs of Pacemaker daemon user
141
 *
142
 * \param[out] uid  Where to store daemon user ID (can be \c NULL)
143
 * \param[out] gid  Where to store daemon group ID (can be \c NULL)
144
 *
145
 * \return Standard Pacemaker return code
146
 */
147
int
148
pcmk__daemon_user(uid_t *uid, gid_t *gid)
149
0
{
150
0
    static uid_t daemon_uid = 0;
151
0
    static gid_t daemon_gid = 0;
152
0
    static bool found = false;
153
154
0
    if (!found) {
155
0
        int rc = pcmk__lookup_user(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
156
157
0
        if (rc != pcmk_rc_ok) {
158
0
            return rc;
159
0
        }
160
0
        found = true;
161
0
    }
162
163
0
    if (uid != NULL) {
164
0
        *uid = daemon_uid;
165
0
    }
166
0
    if (gid != NULL) {
167
0
        *gid = daemon_gid;
168
0
    }
169
0
    return pcmk_rc_ok;
170
0
}
171
172
/*!
173
 * \internal
174
 * \brief Compare two version strings to determine which one is higher
175
 *
176
 * A valid version string is of the form specified by the regex
177
 * <tt>[0-9]+(\.[0-9]+)*</tt>.
178
 *
179
 * Leading whitespace and trailing garbage are allowed and ignored. Anything
180
 * that doesn't match the regex above is considered garbage.
181
 *
182
 * For each string, we get all segments until the first invalid character. A
183
 * segment is a series of digits, and segments are delimited by a single dot.
184
 * The two strings are compared segment by segment, until either we find a
185
 * difference or we've processed all segments in both strings.
186
 *
187
 * If one string runs out of segments to compare before the other string does,
188
 * we treat it as if it has enough padding \c "0" segments to finish the
189
 * comparisons.
190
 *
191
 * Segments are compared by calling \c strtoll() to parse them to long long
192
 * integers and then performing standard integer comparison.
193
 *
194
 * \param[in] version1  First version to compare
195
 * \param[in] version2  Second version to compare
196
 *
197
 * \retval -1  if \p version1 evaluates to a lower version than \p version2
198
 * \retval  1  if \p version1 evaluates to a higher version than \p version2
199
 * \retval  0  if \p version1 and \p version2 evaluate to an equal version
200
 *
201
 * \note Each version segment's parsed value must fit into a <tt>long long</tt>.
202
 */
203
int
204
pcmk__compare_versions(const char *version1, const char *version2)
205
25
{
206
25
    int rc = 0;
207
25
    gchar *match1 = NULL;
208
25
    gchar *match2 = NULL;
209
25
    gchar **segments1 = NULL;
210
25
    gchar **segments2 = NULL;
211
25
    GRegex *regex = NULL;
212
213
25
    if (pcmk__str_eq(version1, version2, pcmk__str_none)) {
214
0
        goto done;
215
0
    }
216
217
    // Ignore leading whitespace and trailing garbage
218
25
    regex = g_regex_new("^\\s*(\\d+(?:\\.\\d+)*)", 0, 0, NULL);
219
220
25
    if (!pcmk__str_empty(version1)) {
221
25
        GMatchInfo *match_info = NULL;
222
223
25
        if (g_regex_match(regex, version1, 0, &match_info)) {
224
25
            match1 = g_match_info_fetch(match_info, 1);
225
25
        }
226
25
        g_match_info_unref(match_info);
227
25
    }
228
25
    if (!pcmk__str_empty(version2)) {
229
25
        GMatchInfo *match_info = NULL;
230
231
25
        if (g_regex_match(regex, version2, 0, &match_info)) {
232
25
            match2 = g_match_info_fetch(match_info, 1);
233
25
        }
234
25
        g_match_info_unref(match_info);
235
25
    }
236
237
25
    segments1 = g_strsplit(pcmk__s(match1, ""), ".", 0);
238
25
    segments2 = g_strsplit(pcmk__s(match2, ""), ".", 0);
239
240
25
    for (gchar **segment1 = segments1, **segment2 = segments2;
241
51
         (*segment1 != NULL) || (*segment2 != NULL); ) {
242
243
51
        long long value1 = 0;
244
51
        long long value2 = 0;
245
246
51
        if (*segment1 != NULL) {
247
            // Make Coverity happy by casting to void
248
51
            (void) pcmk__scan_ll(*segment1, &value1, 0);
249
51
            segment1++;
250
51
        }
251
51
        if (*segment2 != NULL) {
252
51
            (void) pcmk__scan_ll(*segment2, &value2, 0);
253
51
            segment2++;
254
51
        }
255
256
51
        if (value1 < value2) {
257
9
            pcmk__trace("%s < %s", version1, version2);
258
9
            rc = -1;
259
9
            goto done;
260
9
        }
261
42
        if (value1 > value2) {
262
16
            pcmk__trace("%s > %s", version1, version2);
263
16
            rc = 1;
264
16
            goto done;
265
16
        }
266
42
    }
267
268
0
    pcmk__trace("%s == %s", version1, version2);
269
270
25
done:
271
25
    g_free(match1);
272
25
    g_free(match2);
273
25
    g_strfreev(segments1);
274
25
    g_strfreev(segments2);
275
25
    if (regex != NULL) {
276
25
        g_regex_unref(regex);
277
25
    }
278
25
    return rc;
279
0
}
280
281
/* @FIXME uuid.h is an optional header per configure.ac, and we include it
282
 * conditionally above. But uuid_generate() and uuid_unparse() depend on it, on
283
 * many or perhaps all systems with libuuid. So it's not clear how it would ever
284
 * be optional in practice.
285
 *
286
 * Note that these functions are not POSIX, although there is probably no good
287
 * portable alternative.
288
 *
289
 * We do list libuuid as a build dependency in INSTALL.md already.
290
 */
291
292
#ifdef HAVE_UUID_UUID_H
293
#include <uuid/uuid.h>
294
#endif  // HAVE_UUID_UUID_H
295
296
/*!
297
 * \internal
298
 * \brief Generate a 37-byte (36 bytes plus null terminator) UUID string
299
 *
300
 * \return Newly allocated UUID string
301
 *
302
 * \note The caller is responsible for freeing the return value using \c free().
303
 */
304
char *
305
pcmk__generate_uuid(void)
306
23
{
307
23
    uuid_t uuid;
308
309
    // uuid_unparse() converts a UUID to a 37-byte string (including null byte)
310
23
    char *buffer = pcmk__assert_alloc(37, sizeof(char));
311
312
23
    uuid_generate(uuid);
313
23
    uuid_unparse(uuid, buffer);
314
23
    return buffer;
315
23
}
316
317
/*!
318
 * \internal
319
 * \brief Sleep for given milliseconds
320
 *
321
 * \param[in] ms  Time to sleep
322
 *
323
 * \note The full time might not be slept if a signal is received.
324
 */
325
void
326
pcmk__sleep_ms(unsigned int ms)
327
0
{
328
    // @TODO Impose a sane maximum sleep to avoid hanging a process for long
329
    //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
330
331
    // Use sleep() for any whole seconds
332
0
    if (ms >= 1000) {
333
0
        sleep(ms / 1000);
334
0
        ms -= ms / 1000;
335
0
    }
336
337
0
    if (ms == 0) {
338
0
        return;
339
0
    }
340
341
0
#if defined(HAVE_NANOSLEEP)
342
    // nanosleep() is POSIX-2008, so prefer that
343
0
    {
344
0
        struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
345
346
0
        nanosleep(&req, NULL);
347
0
    }
348
#elif defined(HAVE_USLEEP)
349
    // usleep() is widely available, though considered obsolete
350
    usleep((useconds_t) ms);
351
#else
352
    // Otherwise use a trick with select() timeout
353
    {
354
        struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
355
356
        select(0, NULL, NULL, NULL, &tv);
357
    }
358
#endif
359
0
}
360
361
/*!
362
 * \internal
363
 * \brief Add a timer
364
 *
365
 * \param[in] interval_ms The interval for the function to be called, in ms
366
 * \param[in] fn          The function to be called
367
 * \param[in] data        Data to be passed to fn (can be NULL)
368
 *
369
 * \return The ID of the event source
370
 *
371
 * \note If \p fn returns \c G_SOURCE_CONTINUE, then it will be called again
372
 *       after \p interval_ms. If \p fn returns \c G_SOURCE_REMOVE, then the
373
 *       timeout is destroyed and \c fn will not be called again. Note that no
374
 *       \c GDestroyNotify function is set (see \c g_timeout_add_full() and
375
 *       \c g_timeout_add_seconds_full()), so only the timeout is destroyed.
376
 *       \p data is left intact.
377
 */
378
unsigned int
379
pcmk__create_timer(unsigned int interval_ms, GSourceFunc fn, void *data)
380
0
{
381
0
    pcmk__assert(interval_ms != 0 && fn != NULL);
382
383
0
    if (interval_ms % 1000 == 0) {
384
        /* In case interval_ms is 0, the call to pcmk__timeout_ms2s ensures
385
         * an interval of one second.
386
         */
387
0
        return g_timeout_add_seconds(pcmk__timeout_ms2s(interval_ms), fn, data);
388
0
    } else {
389
0
        return g_timeout_add(interval_ms, fn, data);
390
0
    }
391
0
}
392
393
/*!
394
 * \internal
395
 * \brief Convert milliseconds to seconds
396
 *
397
 * \param[in] timeout_ms The interval, in ms
398
 *
399
 * \return If \p timeout_ms is 0, return 0.  Otherwise, return the number of
400
 *         seconds, rounded to the nearest integer, with a minimum of 1.
401
 *
402
 * \note The return value is guaranteed to be less than \c INT_MAX, assuming
403
 *       \c UINT_MAX is one plus double \c INT_MAX.
404
 */
405
unsigned int
406
pcmk__timeout_ms2s(unsigned int timeout_ms)
407
0
{
408
0
    unsigned int quot = 0;
409
0
    unsigned int rem = 0;
410
411
0
    if (timeout_ms == 0) {
412
0
        return 0;
413
0
    } else if (timeout_ms < 1000) {
414
0
        return 1;
415
0
    }
416
417
0
    quot = timeout_ms / 1000;
418
0
    rem = timeout_ms % 1000;
419
420
0
    if (rem >= 500) {
421
0
        quot += 1;
422
0
    }
423
424
0
    return quot;
425
0
}
426
427
// Deprecated functions kept only for backward API compatibility
428
// LCOV_EXCL_START
429
430
#include <gnutls/gnutls.h>          // gnutls_global_init(), etc.
431
432
#include <crm/common/util_compat.h>
433
434
static void
435
_gnutls_log_func(int level, const char *msg)
436
0
{
437
0
    pcmk__trace("%s", msg);
438
0
}
439
440
void
441
crm_gnutls_global_init(void)
442
0
{
443
0
    signal(SIGPIPE, SIG_IGN);
444
0
    gnutls_global_init();
445
0
    gnutls_global_set_log_level(8);
446
0
    gnutls_global_set_log_function(_gnutls_log_func);
447
0
}
448
449
/*!
450
 * \brief Check whether string represents a client name used by cluster daemons
451
 *
452
 * \param[in] name  String to check
453
 *
454
 * \return true if name is standard client name used by daemons, false otherwise
455
 *
456
 * \note This is provided by the client, and so cannot be used by itself as a
457
 *       secure means of authentication.
458
 */
459
bool
460
crm_is_daemon_name(const char *name)
461
0
{
462
0
    return pcmk__str_any_of(name,
463
0
                            "attrd",
464
0
                            CRM_SYSTEM_CIB,
465
0
                            CRM_SYSTEM_CRMD,
466
0
                            CRM_SYSTEM_DC,
467
0
                            CRM_SYSTEM_LRMD,
468
0
                            CRM_SYSTEM_MCP,
469
0
                            CRM_SYSTEM_PENGINE,
470
0
                            CRM_SYSTEM_TENGINE,
471
0
                            "pacemaker-attrd",
472
0
                            "pacemaker-based",
473
0
                            "pacemaker-controld",
474
0
                            "pacemaker-execd",
475
0
                            "pacemaker-fenced",
476
0
                            "pacemaker-remoted",
477
0
                            "pacemaker-schedulerd",
478
0
                            "stonith-ng",
479
0
                            "stonithd",
480
0
                            NULL);
481
0
}
482
483
char *
484
crm_generate_uuid(void)
485
0
{
486
0
    return pcmk__generate_uuid();
487
0
}
488
489
0
#define PW_BUFFER_LEN 500
490
491
int
492
crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
493
0
{
494
0
    int rc = pcmk_ok;
495
0
    char *buffer = NULL;
496
0
    struct passwd pwd;
497
0
    struct passwd *pwentry = NULL;
498
499
0
    buffer = calloc(1, PW_BUFFER_LEN);
500
0
    if (buffer == NULL) {
501
0
        return -ENOMEM;
502
0
    }
503
504
0
    rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
505
0
    if (pwentry) {
506
0
        if (uid) {
507
0
            *uid = pwentry->pw_uid;
508
0
        }
509
0
        if (gid) {
510
0
            *gid = pwentry->pw_gid;
511
0
        }
512
0
        pcmk__trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid,
513
0
                    pwentry->pw_gid);
514
515
0
    } else {
516
0
        rc = rc? -rc : -EINVAL;
517
0
        pcmk__info("User %s lookup: %s", name, pcmk_strerror(rc));
518
0
    }
519
520
0
    free(buffer);
521
0
    return rc;
522
0
}
523
524
int
525
pcmk_daemon_user(uid_t *uid, gid_t *gid)
526
0
{
527
0
    static uid_t daemon_uid;
528
0
    static gid_t daemon_gid;
529
0
    static bool found = false;
530
0
    int rc = pcmk_ok;
531
532
0
    if (!found) {
533
0
        rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
534
0
        if (rc == pcmk_ok) {
535
0
            found = true;
536
0
        }
537
0
    }
538
0
    if (found) {
539
0
        if (uid) {
540
0
            *uid = daemon_uid;
541
0
        }
542
0
        if (gid) {
543
0
            *gid = daemon_gid;
544
0
        }
545
0
    }
546
0
    return rc;
547
0
}
548
549
static int
550
version_helper(const char *text, const char **end_text)
551
0
{
552
0
    int atoi_result = -1;
553
554
0
    pcmk__assert(end_text != NULL);
555
556
0
    errno = 0;
557
558
0
    if (text != NULL && text[0] != 0) {
559
        /* seemingly sacrificing const-correctness -- because while strtol
560
           doesn't modify the input, it doesn't want to artificially taint the
561
           "end_text" pointer-to-pointer-to-first-char-in-string with constness
562
           in case the input wasn't actually constant -- by semantic definition
563
           not a single character will get modified so it shall be perfectly
564
           safe to make compiler happy with dropping "const" qualifier here */
565
0
        atoi_result = (int) strtol(text, (char **) end_text, 10);
566
567
0
        if (errno == EINVAL) {
568
0
            pcmk__err("Conversion of '%s' %c failed", text, text[0]);
569
0
            atoi_result = -1;
570
0
        }
571
0
    }
572
0
    return atoi_result;
573
0
}
574
575
int
576
compare_version(const char *version1, const char *version2)
577
0
{
578
0
    int rc = 0;
579
0
    int lpc = 0;
580
0
    const char *ver1_iter, *ver2_iter;
581
582
0
    if (version1 == NULL && version2 == NULL) {
583
0
        return 0;
584
0
    } else if (version1 == NULL) {
585
0
        return -1;
586
0
    } else if (version2 == NULL) {
587
0
        return 1;
588
0
    }
589
590
0
    ver1_iter = version1;
591
0
    ver2_iter = version2;
592
593
0
    while (1) {
594
0
        int digit1 = 0;
595
0
        int digit2 = 0;
596
597
0
        lpc++;
598
599
0
        if (ver1_iter == ver2_iter) {
600
0
            break;
601
0
        }
602
603
0
        if (ver1_iter != NULL) {
604
0
            digit1 = version_helper(ver1_iter, &ver1_iter);
605
0
        }
606
607
0
        if (ver2_iter != NULL) {
608
0
            digit2 = version_helper(ver2_iter, &ver2_iter);
609
0
        }
610
611
0
        if (digit1 < digit2) {
612
0
            rc = -1;
613
0
            break;
614
615
0
        } else if (digit1 > digit2) {
616
0
            rc = 1;
617
0
            break;
618
0
        }
619
620
0
        if (ver1_iter != NULL && *ver1_iter == '.') {
621
0
            ver1_iter++;
622
0
        }
623
0
        if (ver1_iter != NULL && *ver1_iter == '\0') {
624
0
            ver1_iter = NULL;
625
0
        }
626
627
0
        if (ver2_iter != NULL && *ver2_iter == '.') {
628
0
            ver2_iter++;
629
0
        }
630
0
        if (ver2_iter != NULL && *ver2_iter == 0) {
631
0
            ver2_iter = NULL;
632
0
        }
633
0
    }
634
635
0
    if (rc == 0) {
636
0
        pcmk__trace("%s == %s (%d)", version1, version2, lpc);
637
0
    } else if (rc < 0) {
638
0
        pcmk__trace("%s < %s (%d)", version1, version2, lpc);
639
0
    } else if (rc > 0) {
640
0
        pcmk__trace("%s > %s (%d)", version1, version2, lpc);
641
0
    }
642
643
0
    return rc;
644
0
}
645
646
// LCOV_EXCL_STOP
647
// End deprecated API