pacemaker 2.1.4-dc6eb4362e
Scalable High-Availability cluster resource manager
utils.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 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
51
52gboolean crm_config_error = FALSE;
53gboolean crm_config_warning = FALSE;
54char *crm_system_name = NULL;
55
56int
57crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
58{
59 int rc = pcmk_ok;
60 char *buffer = NULL;
61 struct passwd pwd;
62 struct passwd *pwentry = NULL;
63
64 buffer = calloc(1, PW_BUFFER_LEN);
65 if (buffer == NULL) {
66 return -ENOMEM;
67 }
68
69 rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
70 if (pwentry) {
71 if (uid) {
72 *uid = pwentry->pw_uid;
73 }
74 if (gid) {
75 *gid = pwentry->pw_gid;
76 }
77 crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
78
79 } else {
80 rc = rc? -rc : -EINVAL;
81 crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
82 }
83
84 free(buffer);
85 return rc;
86}
87
96int
97pcmk_daemon_user(uid_t *uid, gid_t *gid)
98{
99 static uid_t daemon_uid;
100 static gid_t daemon_gid;
101 static bool found = false;
102 int rc = pcmk_ok;
103
104 if (!found) {
105 rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
106 if (rc == pcmk_ok) {
107 found = true;
108 }
109 }
110 if (found) {
111 if (uid) {
112 *uid = daemon_uid;
113 }
114 if (gid) {
115 *gid = daemon_gid;
116 }
117 }
118 return rc;
119}
120
128static int
129version_helper(const char *text, const char **end_text)
130{
131 int atoi_result = -1;
132
133 CRM_ASSERT(end_text != NULL);
134
135 errno = 0;
136
137 if (text != NULL && text[0] != 0) {
138 /* seemingly sacrificing const-correctness -- because while strtol
139 doesn't modify the input, it doesn't want to artificially taint the
140 "end_text" pointer-to-pointer-to-first-char-in-string with constness
141 in case the input wasn't actually constant -- by semantic definition
142 not a single character will get modified so it shall be perfectly
143 safe to make compiler happy with dropping "const" qualifier here */
144 atoi_result = (int) strtol(text, (char **) end_text, 10);
145
146 if (errno == EINVAL) {
147 crm_err("Conversion of '%s' %c failed", text, text[0]);
148 atoi_result = -1;
149 }
150 }
151 return atoi_result;
152}
153
154/*
155 * version1 < version2 : -1
156 * version1 = version2 : 0
157 * version1 > version2 : 1
158 */
159int
160compare_version(const char *version1, const char *version2)
161{
162 int rc = 0;
163 int lpc = 0;
164 const char *ver1_iter, *ver2_iter;
165
166 if (version1 == NULL && version2 == NULL) {
167 return 0;
168 } else if (version1 == NULL) {
169 return -1;
170 } else if (version2 == NULL) {
171 return 1;
172 }
173
174 ver1_iter = version1;
175 ver2_iter = version2;
176
177 while (1) {
178 int digit1 = 0;
179 int digit2 = 0;
180
181 lpc++;
182
183 if (ver1_iter == ver2_iter) {
184 break;
185 }
186
187 if (ver1_iter != NULL) {
188 digit1 = version_helper(ver1_iter, &ver1_iter);
189 }
190
191 if (ver2_iter != NULL) {
192 digit2 = version_helper(ver2_iter, &ver2_iter);
193 }
194
195 if (digit1 < digit2) {
196 rc = -1;
197 break;
198
199 } else if (digit1 > digit2) {
200 rc = 1;
201 break;
202 }
203
204 if (ver1_iter != NULL && *ver1_iter == '.') {
205 ver1_iter++;
206 }
207 if (ver1_iter != NULL && *ver1_iter == '\0') {
208 ver1_iter = NULL;
209 }
210
211 if (ver2_iter != NULL && *ver2_iter == '.') {
212 ver2_iter++;
213 }
214 if (ver2_iter != NULL && *ver2_iter == 0) {
215 ver2_iter = NULL;
216 }
217 }
218
219 if (rc == 0) {
220 crm_trace("%s == %s (%d)", version1, version2, lpc);
221 } else if (rc < 0) {
222 crm_trace("%s < %s (%d)", version1, version2, lpc);
223 } else if (rc > 0) {
224 crm_trace("%s > %s (%d)", version1, version2, lpc);
225 }
226
227 return rc;
228}
229
241guint
243{
244 long long msec = -1;
245
246 errno = 0;
247 if (input == NULL) {
248 return 0;
249
250 } else if (input[0] == 'P') {
252
253 if (period_s) {
254 msec = 1000 * crm_time_get_seconds(period_s);
255 crm_time_free(period_s);
256 }
257
258 } else {
259 msec = crm_get_msec(input);
260 }
261
262 if (msec < 0) {
263 crm_warn("Using 0 instead of '%s'", input);
264 errno = EINVAL;
265 return 0;
266 }
267 return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
268}
269
279static void
280log_assertion_as(const char *file, const char *function, int line,
281 const char *assert_condition)
282{
283 if (!pcmk__is_daemon) {
284 crm_enable_stderr(TRUE); // Make sure command-line user sees message
285 }
286 crm_err("%s: Triggered fatal assertion at %s:%d : %s",
287 function, file, line, assert_condition);
288}
289
290/* coverity[+kill] */
302static _Noreturn void
303abort_as(const char *file, const char *function, int line,
304 const char *assert_condition)
305{
306 log_assertion_as(file, function, line, assert_condition);
307 abort();
308}
309
310/* coverity[+kill] */
323static void
324fail_assert_as(const char *file, const char *function, int line,
325 const char *assert_condition)
326{
327 int status = 0;
328 pid_t pid = 0;
329
330 if (!pcmk__is_daemon) {
331 abort_as(file, function, line, assert_condition); // does not return
332 }
333
334 pid = fork();
335 switch (pid) {
336 case -1: // Fork failed
337 crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
338 ": %s", function, file, line, assert_condition);
339 break;
340
341 case 0: // Child process: just abort to dump core
342 abort();
343 break;
344
345 default: // Parent process: wait for child
346 crm_err("%s: Forked child [%d] to record non-fatal assertion at "
347 "%s:%d : %s", function, pid, file, line, assert_condition);
348 crm_write_blackbox(SIGTRAP, NULL);
349 do {
350 if (waitpid(pid, &status, 0) == pid) {
351 return; // Child finished dumping core
352 }
353 } while (errno == EINTR);
354 if (errno == ECHILD) {
355 // crm_mon ignores SIGCHLD
356 crm_trace("Cannot wait on forked child [%d] "
357 "(SIGCHLD is probably ignored)", pid);
358 } else {
359 crm_err("Cannot wait on forked child [%d]: %s",
360 pid, pcmk_rc_str(errno));
361 }
362 break;
363 }
364}
365
366/* coverity[+kill] */
367void
368crm_abort(const char *file, const char *function, int line,
369 const char *assert_condition, gboolean do_core, gboolean do_fork)
370{
371 if (!do_fork) {
372 abort_as(file, function, line, assert_condition);
373 } else if (do_core) {
374 fail_assert_as(file, function, line, assert_condition);
375 } else {
376 log_assertion_as(file, function, line, assert_condition);
377 }
378}
379
391void
392pcmk__daemonize(const char *name, const char *pidfile)
393{
394 int rc;
395 pid_t pid;
396
397 /* Check before we even try... */
398 rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
399 if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
400 crm_err("%s: already running [pid %lld in %s]",
401 name, (long long) pid, pidfile);
402 printf("%s: already running [pid %lld in %s]\n",
403 name, (long long) pid, pidfile);
405 }
406
407 pid = fork();
408 if (pid < 0) {
409 fprintf(stderr, "%s: could not start daemon\n", name);
410 crm_perror(LOG_ERR, "fork");
412
413 } else if (pid > 0) {
415 }
416
417 rc = pcmk__lock_pidfile(pidfile, name);
418 if (rc != pcmk_rc_ok) {
419 crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
420 pidfile, name, pcmk_rc_str(rc), rc);
421 printf("Could not lock '%s' for %s: %s (%d)\n",
422 pidfile, name, pcmk_rc_str(rc), rc);
424 }
425
426 umask(S_IWGRP | S_IWOTH | S_IROTH);
427
428 close(STDIN_FILENO);
429 pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
430
431 close(STDOUT_FILENO);
432 pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
433
434 close(STDERR_FILENO);
435 pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
436}
437
438char *
439crm_meta_name(const char *field)
440{
441 int lpc = 0;
442 int max = 0;
443 char *crm_name = NULL;
444
445 CRM_CHECK(field != NULL, return NULL);
446 crm_name = crm_strdup_printf(CRM_META "_%s", field);
447
448 /* Massage the names so they can be used as shell variables */
449 max = strlen(crm_name);
450 for (; lpc < max; lpc++) {
451 switch (crm_name[lpc]) {
452 case '-':
453 crm_name[lpc] = '_';
454 break;
455 }
456 }
457 return crm_name;
458}
459
460const char *
461crm_meta_value(GHashTable * hash, const char *field)
462{
463 char *key = NULL;
464 const char *value = NULL;
465
466 key = crm_meta_name(field);
467 if (key) {
468 value = g_hash_table_lookup(hash, key);
469 free(key);
470 }
471
472 return value;
473}
474
475#ifdef HAVE_UUID_UUID_H
476# include <uuid/uuid.h>
477#endif
478
479char *
481{
482 unsigned char uuid[16];
483 char *buffer = malloc(37); /* Including NUL byte */
484
485 uuid_generate(uuid);
486 uuid_unparse(uuid, buffer);
487 return buffer;
488}
489
490#ifdef HAVE_GNUTLS_GNUTLS_H
491void
492crm_gnutls_global_init(void)
493{
494 signal(SIGPIPE, SIG_IGN);
495 gnutls_global_init();
496}
497#endif
498
504char *
506{
507 struct utsname hostinfo;
508
509 return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
510}
511
512bool
513pcmk_str_is_infinity(const char *s) {
515}
516
517bool
519 return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
520}
521
530void
531pcmk__sleep_ms(unsigned int ms)
532{
533 // @TODO Impose a sane maximum sleep to avoid hanging a process for long
534 //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
535
536 // Use sleep() for any whole seconds
537 if (ms >= 1000) {
538 sleep(ms / 1000);
539 ms -= ms / 1000;
540 }
541
542 if (ms == 0) {
543 return;
544 }
545
546#if defined(HAVE_NANOSLEEP)
547 // nanosleep() is POSIX-2008, so prefer that
548 {
549 struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
550
551 nanosleep(&req, NULL);
552 }
553#elif defined(HAVE_USLEEP)
554 // usleep() is widely available, though considered obsolete
555 usleep((useconds_t) ms);
556#else
557 // Otherwise use a trick with select() timeout
558 {
559 struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
560
561 select(0, NULL, NULL, NULL, &tv);
562 }
563#endif
564}
const char * name
Definition: cib.c:24
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition: pid.c:186
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition: pid.c:228
Utility functions.
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:364
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:461
bool pcmk_str_is_infinity(const char *s)
Definition: utils.c:513
#define PW_BUFFER_LEN
Definition: utils.c:47
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition: utils.c:97
gboolean crm_config_warning
Definition: utils.c:53
char * crm_generate_uuid(void)
Definition: utils.c:480
CRM_TRACE_INIT_DATA(common)
char * crm_meta_name(const char *field)
Definition: utils.c:439
void pcmk__daemonize(const char *name, const char *pidfile)
Definition: utils.c:392
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition: utils.c:57
guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:242
int compare_version(const char *version1, const char *version2)
Definition: utils.c:160
void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:368
void pcmk__sleep_ms(unsigned int ms)
Definition: utils.c:531
gboolean crm_config_error
Definition: utils.c:52
char * pcmk_hostname()
Get the local hostname.
Definition: utils.c:505
char * crm_system_name
Definition: utils.c:54
bool pcmk_str_is_minus_infinity(const char *s)
Definition: utils.c:518
#define CRM_DAEMON_USER
Definition: config.h:30
#define _Noreturn
Definition: config.h:682
char uname[MAX_NAME]
Definition: cpg.c:5
uint32_t pid
Definition: cpg.c:1
A dumping ground.
#define CRM_PLUS_INFINITY_S
Definition: crm.h:87
#define CRM_META
Definition: crm.h:78
#define CRM_INFINITY_S
Definition: crm.h:86
#define CRM_MINUS_INFINITY_S
Definition: crm.h:88
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:982
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140
long long int crm_time_get_seconds(crm_time_t *dt)
Definition: iso8601.c:307
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define crm_info(fmt, args...)
Definition: logging.h:361
#define crm_warn(fmt, args...)
Definition: logging.h:359
#define CRM_XS
Definition: logging.h:55
void crm_enable_stderr(int enable)
Definition: logging.c:998
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:309
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:474
#define crm_err(fmt, args...)
Definition: logging.h:358
#define crm_trace(fmt, args...)
Definition: logging.h:364
Wrappers for and extensions to glib mainloop.
xmlNode * input
bool pcmk__is_daemon
Definition: logging.c:47
const char * pcmk_strerror(int rc)
Definition: results.c:58
#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:370
@ CRM_EX_ERROR
Unspecified error.
Definition: results.h:240
@ CRM_EX_OSERR
External (OS/environmental) problem.
Definition: results.h:258
@ CRM_EX_OK
Success.
Definition: results.h:239
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition: results.c:775
@ pcmk_rc_ok
Definition: results.h:146
#define pcmk_ok
Definition: results.h:68
Services API.
@ pcmk__str_none
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:955
Wrappers for and extensions to libxml2.