pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
utils.c
Go to the documentation of this file.
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
47
48gboolean crm_config_error = FALSE;
49gboolean crm_config_warning = FALSE;
50char *crm_system_name = NULL;
51
52bool
53pcmk__is_user_in_group(const char *user, const char *group)
54{
55 struct group *grent;
56 char **gr_mem;
57
58 if (user == NULL || group == NULL) {
59 return false;
60 }
61
62 setgrent();
63 while ((grent = getgrent()) != NULL) {
64 if (grent->gr_mem == NULL) {
65 continue;
66 }
67
68 if(strcmp(group, grent->gr_name) != 0) {
69 continue;
70 }
71
72 gr_mem = grent->gr_mem;
73 while (*gr_mem != NULL) {
74 if (!strcmp(user, *gr_mem++)) {
75 endgrent();
76 return true;
77 }
78 }
79 }
80 endgrent();
81 return false;
82}
83
84int
85crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
86{
87 int rc = pcmk_ok;
88 char *buffer = NULL;
89 struct passwd pwd;
90 struct passwd *pwentry = NULL;
91
92 buffer = calloc(1, PCMK__PW_BUFFER_LEN);
93 if (buffer == NULL) {
94 return -ENOMEM;
95 }
96
97 rc = getpwnam_r(name, &pwd, buffer, PCMK__PW_BUFFER_LEN, &pwentry);
98 if (pwentry) {
99 if (uid) {
100 *uid = pwentry->pw_uid;
101 }
102 if (gid) {
103 *gid = pwentry->pw_gid;
104 }
105 crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
106
107 } else {
108 rc = rc? -rc : -EINVAL;
109 crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
110 }
111
112 free(buffer);
113 return rc;
114}
115
124int
125pcmk_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 int rc = pcmk_ok;
131
132 if (!found) {
133 rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
134 if (rc == pcmk_ok) {
135 found = true;
136 }
137 }
138 if (found) {
139 if (uid) {
140 *uid = daemon_uid;
141 }
142 if (gid) {
143 *gid = daemon_gid;
144 }
145 }
146 return rc;
147}
148
156static int
157version_helper(const char *text, const char **end_text)
158{
159 int atoi_result = -1;
160
161 CRM_ASSERT(end_text != NULL);
162
163 errno = 0;
164
165 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 atoi_result = (int) strtol(text, (char **) end_text, 10);
173
174 if (errno == EINVAL) {
175 crm_err("Conversion of '%s' %c failed", text, text[0]);
176 atoi_result = -1;
177 }
178 }
179 return atoi_result;
180}
181
182/*
183 * version1 < version2 : -1
184 * version1 = version2 : 0
185 * version1 > version2 : 1
186 */
187int
188compare_version(const char *version1, const char *version2)
189{
190 int rc = 0;
191 int lpc = 0;
192 const char *ver1_iter, *ver2_iter;
193
194 if (version1 == NULL && version2 == NULL) {
195 return 0;
196 } else if (version1 == NULL) {
197 return -1;
198 } else if (version2 == NULL) {
199 return 1;
200 }
201
202 ver1_iter = version1;
203 ver2_iter = version2;
204
205 while (1) {
206 int digit1 = 0;
207 int digit2 = 0;
208
209 lpc++;
210
211 if (ver1_iter == ver2_iter) {
212 break;
213 }
214
215 if (ver1_iter != NULL) {
216 digit1 = version_helper(ver1_iter, &ver1_iter);
217 }
218
219 if (ver2_iter != NULL) {
220 digit2 = version_helper(ver2_iter, &ver2_iter);
221 }
222
223 if (digit1 < digit2) {
224 rc = -1;
225 break;
226
227 } else if (digit1 > digit2) {
228 rc = 1;
229 break;
230 }
231
232 if (ver1_iter != NULL && *ver1_iter == '.') {
233 ver1_iter++;
234 }
235 if (ver1_iter != NULL && *ver1_iter == '\0') {
236 ver1_iter = NULL;
237 }
238
239 if (ver2_iter != NULL && *ver2_iter == '.') {
240 ver2_iter++;
241 }
242 if (ver2_iter != NULL && *ver2_iter == 0) {
243 ver2_iter = NULL;
244 }
245 }
246
247 if (rc == 0) {
248 crm_trace("%s == %s (%d)", version1, version2, lpc);
249 } else if (rc < 0) {
250 crm_trace("%s < %s (%d)", version1, version2, lpc);
251 } else if (rc > 0) {
252 crm_trace("%s > %s (%d)", version1, version2, lpc);
253 }
254
255 return rc;
256}
257
267static void
268log_assertion_as(const char *file, const char *function, int line,
269 const char *assert_condition)
270{
271 if (!pcmk__is_daemon) {
272 crm_enable_stderr(TRUE); // Make sure command-line user sees message
273 }
274 crm_err("%s: Triggered fatal assertion at %s:%d : %s",
275 function, file, line, assert_condition);
276}
277
278/* coverity[+kill] */
290static _Noreturn void
291abort_as(const char *file, const char *function, int line,
292 const char *assert_condition)
293{
294 log_assertion_as(file, function, line, assert_condition);
295 abort();
296}
297
298/* coverity[+kill] */
311static void
312fail_assert_as(const char *file, const char *function, int line,
313 const char *assert_condition)
314{
315 int status = 0;
316 pid_t pid = 0;
317
318 if (!pcmk__is_daemon) {
319 abort_as(file, function, line, assert_condition); // does not return
320 }
321
322 pid = fork();
323 switch (pid) {
324 case -1: // Fork failed
325 crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
326 ": %s", function, file, line, assert_condition);
327 break;
328
329 case 0: // Child process: just abort to dump core
330 abort();
331 break;
332
333 default: // Parent process: wait for child
334 crm_err("%s: Forked child [%d] to record non-fatal assertion at "
335 "%s:%d : %s", function, pid, file, line, assert_condition);
336 crm_write_blackbox(SIGTRAP, NULL);
337 do {
338 if (waitpid(pid, &status, 0) == pid) {
339 return; // Child finished dumping core
340 }
341 } while (errno == EINTR);
342 if (errno == ECHILD) {
343 // crm_mon ignores SIGCHLD
344 crm_trace("Cannot wait on forked child [%d] "
345 "(SIGCHLD is probably ignored)", pid);
346 } else {
347 crm_err("Cannot wait on forked child [%d]: %s",
348 pid, pcmk_rc_str(errno));
349 }
350 break;
351 }
352}
353
354/* coverity[+kill] */
355void
356crm_abort(const char *file, const char *function, int line,
357 const char *assert_condition, gboolean do_core, gboolean do_fork)
358{
359 if (!do_fork) {
360 abort_as(file, function, line, assert_condition);
361 } else if (do_core) {
362 fail_assert_as(file, function, line, assert_condition);
363 } else {
364 log_assertion_as(file, function, line, assert_condition);
365 }
366}
367
379void
380pcmk__daemonize(const char *name, const char *pidfile)
381{
382 int rc;
383 pid_t pid;
384
385 /* Check before we even try... */
386 rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
387 if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
388 crm_err("%s: already running [pid %lld in %s]",
389 name, (long long) pid, pidfile);
390 printf("%s: already running [pid %lld in %s]\n",
391 name, (long long) pid, pidfile);
393 }
394
395 pid = fork();
396 if (pid < 0) {
397 fprintf(stderr, "%s: could not start daemon\n", name);
398 crm_perror(LOG_ERR, "fork");
400
401 } else if (pid > 0) {
403 }
404
405 rc = pcmk__lock_pidfile(pidfile, name);
406 if (rc != pcmk_rc_ok) {
407 crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
408 pidfile, name, pcmk_rc_str(rc), rc);
409 printf("Could not lock '%s' for %s: %s (%d)\n",
410 pidfile, name, pcmk_rc_str(rc), rc);
412 }
413
414 umask(S_IWGRP | S_IWOTH | S_IROTH);
415
416 close(STDIN_FILENO);
417 pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
418
419 close(STDOUT_FILENO);
420 pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
421
422 close(STDERR_FILENO);
423 pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
424}
425
426#ifdef HAVE_UUID_UUID_H
427# include <uuid/uuid.h>
428#endif
429
430char *
432{
433 unsigned char uuid[16];
434 char *buffer = malloc(37); /* Including NUL byte */
435
436 pcmk__mem_assert(buffer);
437 uuid_generate(uuid);
438 uuid_unparse(uuid, buffer);
439 return buffer;
440}
441
442#ifdef HAVE_GNUTLS_GNUTLS_H
443void
444crm_gnutls_global_init(void)
445{
446 signal(SIGPIPE, SIG_IGN);
447 gnutls_global_init();
448}
449#endif
450
451bool
456
457bool
459 return pcmk__str_eq(s, PCMK_VALUE_MINUS_INFINITY, pcmk__str_none);
460}
461
470void
471pcmk__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 if (ms >= 1000) {
478 sleep(ms / 1000);
479 ms -= ms / 1000;
480 }
481
482 if (ms == 0) {
483 return;
484 }
485
486#if defined(HAVE_NANOSLEEP)
487 // nanosleep() is POSIX-2008, so prefer that
488 {
489 struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
490
491 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
510
511guint
513{
514 long long msec = -1;
515
516 errno = 0;
517 if (input == NULL) {
518 return 0;
519
520 } else if (input[0] == 'P') {
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
540char *
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
const char * name
Definition cib.c:26
bool pcmk__is_daemon
Definition logging.c:47
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition pid.c:172
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition pid.c:214
Utility functions.
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:356
Deprecated Pacemaker utilities.
bool pcmk_str_is_infinity(const char *s)
Definition utils.c:452
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition utils.c:125
gboolean crm_config_warning
Definition utils.c:49
char * crm_generate_uuid(void)
Definition utils.c:431
void pcmk__daemonize(const char *name, const char *pidfile)
Definition utils.c:380
bool pcmk__is_user_in_group(const char *user, const char *group)
Definition utils.c:53
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition utils.c:85
guint crm_parse_interval_spec(const char *input)
Definition utils.c:512
int compare_version(const char *version1, const char *version2)
Definition utils.c:188
void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork)
Definition utils.c:356
char * pcmk_hostname(void)
Definition utils.c:541
void pcmk__sleep_ms(unsigned int ms)
Definition utils.c:471
gboolean crm_config_error
Definition utils.c:48
char * crm_system_name
Definition utils.c:50
bool pcmk_str_is_minus_infinity(const char *s)
Definition utils.c:458
#define CRM_DAEMON_USER
Definition config.h:30
#define _Noreturn
Definition config.h:660
char uname[MAX_NAME]
Definition cpg.c:5
uint32_t pid
Definition cpg.c:1
A dumping ground.
#define PCMK__PW_BUFFER_LEN
IPC interface to Pacemaker daemons.
ISO_8601 Date handling.
crm_time_t * crm_time_parse_duration(const char *duration_str)
Parse a time duration from an ISO 8601 duration specification.
Definition iso8601.c:1086
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
long long crm_time_get_seconds(const crm_time_t *dt)
Definition iso8601.c:316
struct crm_time_s crm_time_t
Definition iso8601.h:32
#define CRM_TRACE_INIT_DATA(name)
Definition logging.h:143
#define crm_info(fmt, args...)
Definition logging.h:397
void crm_write_blackbox(int nsig, const struct qb_log_callsite *callsite)
Definition logging.c:505
#define crm_warn(fmt, args...)
Definition logging.h:392
#define CRM_XS
Definition logging.h:56
void crm_enable_stderr(int enable)
Definition logging.c:1042
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:331
#define crm_err(fmt, args...)
Definition logging.h:389
#define crm_trace(fmt, args...)
Definition logging.h:402
Wrappers for and extensions to glib mainloop.
xmlNode * input
#define PCMK_VALUE_MINUS_INFINITY
Definition options.h:173
#define PCMK_VALUE_PLUS_INFINITY
Definition options.h:192
#define PCMK_VALUE_INFINITY
Definition options.h:163
const char * pcmk_strerror(int rc)
Definition results.c:149
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:501
@ CRM_EX_ERROR
Unspecified error.
Definition results.h:256
@ CRM_EX_OSERR
External (OS/environmental) problem.
Definition results.h:276
@ CRM_EX_OK
Success.
Definition results.h:255
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition results.c:936
@ pcmk_rc_ok
Definition results.h:162
#define pcmk_ok
Definition results.h:69
#define pcmk__mem_assert(ptr)
Services API.
@ pcmk__str_none
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1050
Wrappers for and extensions to libxml2.