Coverage Report

Created: 2026-09-29 06:41

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
unsigned int
403
pcmk__timeout_ms2s(unsigned int timeout_ms)
404
0
{
405
0
    unsigned int quot = 0;
406
0
    unsigned int rem = 0;
407
408
0
    if (timeout_ms == 0) {
409
0
        return 0;
410
0
    } else if (timeout_ms < 1000) {
411
0
        return 1;
412
0
    }
413
414
0
    quot = timeout_ms / 1000;
415
0
    rem = timeout_ms % 1000;
416
417
0
    if (rem >= 500) {
418
0
        quot += 1;
419
0
    }
420
421
0
    return quot;
422
0
}
423
424
// Deprecated functions kept only for backward API compatibility
425
// LCOV_EXCL_START
426
427
#include <gnutls/gnutls.h>          // gnutls_global_init(), etc.
428
429
#include <crm/common/util_compat.h>
430
431
static void
432
_gnutls_log_func(int level, const char *msg)
433
0
{
434
0
    pcmk__trace("%s", msg);
435
0
}
436
437
void
438
crm_gnutls_global_init(void)
439
0
{
440
0
    signal(SIGPIPE, SIG_IGN);
441
0
    gnutls_global_init();
442
0
    gnutls_global_set_log_level(8);
443
0
    gnutls_global_set_log_function(_gnutls_log_func);
444
0
}
445
446
/*!
447
 * \brief Check whether string represents a client name used by cluster daemons
448
 *
449
 * \param[in] name  String to check
450
 *
451
 * \return true if name is standard client name used by daemons, false otherwise
452
 *
453
 * \note This is provided by the client, and so cannot be used by itself as a
454
 *       secure means of authentication.
455
 */
456
bool
457
crm_is_daemon_name(const char *name)
458
0
{
459
0
    return pcmk__str_any_of(name,
460
0
                            "attrd",
461
0
                            CRM_SYSTEM_CIB,
462
0
                            CRM_SYSTEM_CRMD,
463
0
                            CRM_SYSTEM_DC,
464
0
                            CRM_SYSTEM_LRMD,
465
0
                            CRM_SYSTEM_MCP,
466
0
                            CRM_SYSTEM_PENGINE,
467
0
                            CRM_SYSTEM_TENGINE,
468
0
                            "pacemaker-attrd",
469
0
                            "pacemaker-based",
470
0
                            "pacemaker-controld",
471
0
                            "pacemaker-execd",
472
0
                            "pacemaker-fenced",
473
0
                            "pacemaker-remoted",
474
0
                            "pacemaker-schedulerd",
475
0
                            "stonith-ng",
476
0
                            "stonithd",
477
0
                            NULL);
478
0
}
479
480
char *
481
crm_generate_uuid(void)
482
0
{
483
0
    return pcmk__generate_uuid();
484
0
}
485
486
0
#define PW_BUFFER_LEN 500
487
488
int
489
crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
490
0
{
491
0
    int rc = pcmk_ok;
492
0
    char *buffer = NULL;
493
0
    struct passwd pwd;
494
0
    struct passwd *pwentry = NULL;
495
496
0
    buffer = calloc(1, PW_BUFFER_LEN);
497
0
    if (buffer == NULL) {
498
0
        return -ENOMEM;
499
0
    }
500
501
0
    rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
502
0
    if (pwentry) {
503
0
        if (uid) {
504
0
            *uid = pwentry->pw_uid;
505
0
        }
506
0
        if (gid) {
507
0
            *gid = pwentry->pw_gid;
508
0
        }
509
0
        pcmk__trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid,
510
0
                    pwentry->pw_gid);
511
512
0
    } else {
513
0
        rc = rc? -rc : -EINVAL;
514
0
        pcmk__info("User %s lookup: %s", name, pcmk_strerror(rc));
515
0
    }
516
517
0
    free(buffer);
518
0
    return rc;
519
0
}
520
521
int
522
pcmk_daemon_user(uid_t *uid, gid_t *gid)
523
0
{
524
0
    static uid_t daemon_uid;
525
0
    static gid_t daemon_gid;
526
0
    static bool found = false;
527
0
    int rc = pcmk_ok;
528
529
0
    if (!found) {
530
0
        rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
531
0
        if (rc == pcmk_ok) {
532
0
            found = true;
533
0
        }
534
0
    }
535
0
    if (found) {
536
0
        if (uid) {
537
0
            *uid = daemon_uid;
538
0
        }
539
0
        if (gid) {
540
0
            *gid = daemon_gid;
541
0
        }
542
0
    }
543
0
    return rc;
544
0
}
545
546
static int
547
version_helper(const char *text, const char **end_text)
548
0
{
549
0
    int atoi_result = -1;
550
551
0
    pcmk__assert(end_text != NULL);
552
553
0
    errno = 0;
554
555
0
    if (text != NULL && text[0] != 0) {
556
        /* seemingly sacrificing const-correctness -- because while strtol
557
           doesn't modify the input, it doesn't want to artificially taint the
558
           "end_text" pointer-to-pointer-to-first-char-in-string with constness
559
           in case the input wasn't actually constant -- by semantic definition
560
           not a single character will get modified so it shall be perfectly
561
           safe to make compiler happy with dropping "const" qualifier here */
562
0
        atoi_result = (int) strtol(text, (char **) end_text, 10);
563
564
0
        if (errno == EINVAL) {
565
0
            pcmk__err("Conversion of '%s' %c failed", text, text[0]);
566
0
            atoi_result = -1;
567
0
        }
568
0
    }
569
0
    return atoi_result;
570
0
}
571
572
int
573
compare_version(const char *version1, const char *version2)
574
0
{
575
0
    int rc = 0;
576
0
    int lpc = 0;
577
0
    const char *ver1_iter, *ver2_iter;
578
579
0
    if (version1 == NULL && version2 == NULL) {
580
0
        return 0;
581
0
    } else if (version1 == NULL) {
582
0
        return -1;
583
0
    } else if (version2 == NULL) {
584
0
        return 1;
585
0
    }
586
587
0
    ver1_iter = version1;
588
0
    ver2_iter = version2;
589
590
0
    while (1) {
591
0
        int digit1 = 0;
592
0
        int digit2 = 0;
593
594
0
        lpc++;
595
596
0
        if (ver1_iter == ver2_iter) {
597
0
            break;
598
0
        }
599
600
0
        if (ver1_iter != NULL) {
601
0
            digit1 = version_helper(ver1_iter, &ver1_iter);
602
0
        }
603
604
0
        if (ver2_iter != NULL) {
605
0
            digit2 = version_helper(ver2_iter, &ver2_iter);
606
0
        }
607
608
0
        if (digit1 < digit2) {
609
0
            rc = -1;
610
0
            break;
611
612
0
        } else if (digit1 > digit2) {
613
0
            rc = 1;
614
0
            break;
615
0
        }
616
617
0
        if (ver1_iter != NULL && *ver1_iter == '.') {
618
0
            ver1_iter++;
619
0
        }
620
0
        if (ver1_iter != NULL && *ver1_iter == '\0') {
621
0
            ver1_iter = NULL;
622
0
        }
623
624
0
        if (ver2_iter != NULL && *ver2_iter == '.') {
625
0
            ver2_iter++;
626
0
        }
627
0
        if (ver2_iter != NULL && *ver2_iter == 0) {
628
0
            ver2_iter = NULL;
629
0
        }
630
0
    }
631
632
0
    if (rc == 0) {
633
0
        pcmk__trace("%s == %s (%d)", version1, version2, lpc);
634
0
    } else if (rc < 0) {
635
0
        pcmk__trace("%s < %s (%d)", version1, version2, lpc);
636
0
    } else if (rc > 0) {
637
0
        pcmk__trace("%s > %s (%d)", version1, version2, lpc);
638
0
    }
639
640
0
    return rc;
641
0
}
642
643
// LCOV_EXCL_STOP
644
// End deprecated API