pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
utils.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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
59
60int
61char2score(const char *score)
62{
63 int score_f = 0;
64
65 if (score == NULL) {
66
67 } else if (pcmk_str_is_minus_infinity(score)) {
68 score_f = -CRM_SCORE_INFINITY;
69
70 } else if (pcmk_str_is_infinity(score)) {
71 score_f = CRM_SCORE_INFINITY;
72
73 } else if (pcmk__str_eq(score, "red", pcmk__str_casei)) {
74 score_f = pcmk__score_red;
75
76 } else if (pcmk__str_eq(score, "yellow", pcmk__str_casei)) {
77 score_f = pcmk__score_yellow;
78
79 } else if (pcmk__str_eq(score, "green", pcmk__str_casei)) {
80 score_f = pcmk__score_green;
81
82 } else {
83 long long score_ll;
84
85 pcmk__scan_ll(score, &score_ll, 0LL);
86 if (score_ll > CRM_SCORE_INFINITY) {
87 score_f = CRM_SCORE_INFINITY;
88
89 } else if (score_ll < -CRM_SCORE_INFINITY) {
90 score_f = -CRM_SCORE_INFINITY;
91
92 } else {
93 score_f = (int) score_ll;
94 }
95 }
96
97 return score_f;
98}
99
100char *
101score2char_stack(int score, char *buf, size_t len)
102{
103 CRM_CHECK((buf != NULL) && (len >= sizeof(CRM_MINUS_INFINITY_S)),
104 return NULL);
105
106 if (score >= CRM_SCORE_INFINITY) {
107 strncpy(buf, CRM_INFINITY_S, 9);
108 } else if (score <= -CRM_SCORE_INFINITY) {
109 strncpy(buf, CRM_MINUS_INFINITY_S , 10);
110 } else {
111 snprintf(buf, len, "%d", score);
112 }
113 return buf;
114}
115
116char *
117score2char(int score)
118{
119 if (score >= CRM_SCORE_INFINITY) {
120 return strdup(CRM_INFINITY_S);
121
122 } else if (score <= -CRM_SCORE_INFINITY) {
123 return strdup(CRM_MINUS_INFINITY_S);
124 }
125 return pcmk__itoa(score);
126}
127
128int
129crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
130{
131 int rc = pcmk_ok;
132 char *buffer = NULL;
133 struct passwd pwd;
134 struct passwd *pwentry = NULL;
135
136 buffer = calloc(1, PW_BUFFER_LEN);
137 if (buffer == NULL) {
138 return -ENOMEM;
139 }
140
141 rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
142 if (pwentry) {
143 if (uid) {
144 *uid = pwentry->pw_uid;
145 }
146 if (gid) {
147 *gid = pwentry->pw_gid;
148 }
149 crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
150
151 } else {
152 rc = rc? -rc : -EINVAL;
153 crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
154 }
155
156 free(buffer);
157 return rc;
158}
159
168int
169pcmk_daemon_user(uid_t *uid, gid_t *gid)
170{
171 static uid_t daemon_uid;
172 static gid_t daemon_gid;
173 static bool found = false;
174 int rc = pcmk_ok;
175
176 if (!found) {
177 rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
178 if (rc == pcmk_ok) {
179 found = true;
180 }
181 }
182 if (found) {
183 if (uid) {
184 *uid = daemon_uid;
185 }
186 if (gid) {
187 *gid = daemon_gid;
188 }
189 }
190 return rc;
191}
192
200static int
201version_helper(const char *text, const char **end_text)
202{
203 int atoi_result = -1;
204
205 CRM_ASSERT(end_text != NULL);
206
207 errno = 0;
208
209 if (text != NULL && text[0] != 0) {
210 /* seemingly sacrificing const-correctness -- because while strtol
211 doesn't modify the input, it doesn't want to artificially taint the
212 "end_text" pointer-to-pointer-to-first-char-in-string with constness
213 in case the input wasn't actually constant -- by semantic definition
214 not a single character will get modified so it shall be perfectly
215 safe to make compiler happy with dropping "const" qualifier here */
216 atoi_result = (int) strtol(text, (char **) end_text, 10);
217
218 if (errno == EINVAL) {
219 crm_err("Conversion of '%s' %c failed", text, text[0]);
220 atoi_result = -1;
221 }
222 }
223 return atoi_result;
224}
225
226/*
227 * version1 < version2 : -1
228 * version1 = version2 : 0
229 * version1 > version2 : 1
230 */
231int
232compare_version(const char *version1, const char *version2)
233{
234 int rc = 0;
235 int lpc = 0;
236 const char *ver1_iter, *ver2_iter;
237
238 if (version1 == NULL && version2 == NULL) {
239 return 0;
240 } else if (version1 == NULL) {
241 return -1;
242 } else if (version2 == NULL) {
243 return 1;
244 }
245
246 ver1_iter = version1;
247 ver2_iter = version2;
248
249 while (1) {
250 int digit1 = 0;
251 int digit2 = 0;
252
253 lpc++;
254
255 if (ver1_iter == ver2_iter) {
256 break;
257 }
258
259 if (ver1_iter != NULL) {
260 digit1 = version_helper(ver1_iter, &ver1_iter);
261 }
262
263 if (ver2_iter != NULL) {
264 digit2 = version_helper(ver2_iter, &ver2_iter);
265 }
266
267 if (digit1 < digit2) {
268 rc = -1;
269 break;
270
271 } else if (digit1 > digit2) {
272 rc = 1;
273 break;
274 }
275
276 if (ver1_iter != NULL && *ver1_iter == '.') {
277 ver1_iter++;
278 }
279 if (ver1_iter != NULL && *ver1_iter == '\0') {
280 ver1_iter = NULL;
281 }
282
283 if (ver2_iter != NULL && *ver2_iter == '.') {
284 ver2_iter++;
285 }
286 if (ver2_iter != NULL && *ver2_iter == 0) {
287 ver2_iter = NULL;
288 }
289 }
290
291 if (rc == 0) {
292 crm_trace("%s == %s (%d)", version1, version2, lpc);
293 } else if (rc < 0) {
294 crm_trace("%s < %s (%d)", version1, version2, lpc);
295 } else if (rc > 0) {
296 crm_trace("%s > %s (%d)", version1, version2, lpc);
297 }
298
299 return rc;
300}
301
313guint
314crm_parse_interval_spec(const char *input)
315{
316 long long msec = -1;
317
318 errno = 0;
319 if (input == NULL) {
320 return 0;
321
322 } else if (input[0] == 'P') {
323 crm_time_t *period_s = crm_time_parse_duration(input);
324
325 if (period_s) {
326 msec = 1000 * crm_time_get_seconds(period_s);
327 crm_time_free(period_s);
328 }
329
330 } else {
331 msec = crm_get_msec(input);
332 }
333
334 if (msec < 0) {
335 crm_warn("Using 0 instead of '%s'", input);
336 errno = EINVAL;
337 return 0;
338 }
339 return (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
340}
341
351static void
352log_assertion_as(const char *file, const char *function, int line,
353 const char *assert_condition)
354{
355 if (!pcmk__is_daemon) {
356 crm_enable_stderr(TRUE); // Make sure command-line user sees message
357 }
358 crm_err("%s: Triggered fatal assertion at %s:%d : %s",
359 function, file, line, assert_condition);
360}
361
362/* coverity[+kill] */
374static _Noreturn void
375abort_as(const char *file, const char *function, int line,
376 const char *assert_condition)
377{
378 log_assertion_as(file, function, line, assert_condition);
379 abort();
380}
381
382/* coverity[+kill] */
395static void
396fail_assert_as(const char *file, const char *function, int line,
397 const char *assert_condition)
398{
399 int status = 0;
400 pid_t pid = 0;
401
402 if (!pcmk__is_daemon) {
403 abort_as(file, function, line, assert_condition); // does not return
404 }
405
406 pid = fork();
407 switch (pid) {
408 case -1: // Fork failed
409 crm_warn("%s: Cannot dump core for non-fatal assertion at %s:%d "
410 ": %s", function, file, line, assert_condition);
411 break;
412
413 case 0: // Child process: just abort to dump core
414 abort();
415 break;
416
417 default: // Parent process: wait for child
418 crm_err("%s: Forked child [%d] to record non-fatal assertion at "
419 "%s:%d : %s", function, pid, file, line, assert_condition);
420 crm_write_blackbox(SIGTRAP, NULL);
421 do {
422 if (waitpid(pid, &status, 0) == pid) {
423 return; // Child finished dumping core
424 }
425 } while (errno == EINTR);
426 if (errno == ECHILD) {
427 // crm_mon ignores SIGCHLD
428 crm_trace("Cannot wait on forked child [%d] "
429 "(SIGCHLD is probably ignored)", pid);
430 } else {
431 crm_err("Cannot wait on forked child [%d]: %s",
432 pid, pcmk_rc_str(errno));
433 }
434 break;
435 }
436}
437
438/* coverity[+kill] */
439void
440crm_abort(const char *file, const char *function, int line,
441 const char *assert_condition, gboolean do_core, gboolean do_fork)
442{
443 if (!do_fork) {
444 abort_as(file, function, line, assert_condition);
445 } else if (do_core) {
446 fail_assert_as(file, function, line, assert_condition);
447 } else {
448 log_assertion_as(file, function, line, assert_condition);
449 }
450}
451
463void
464pcmk__daemonize(const char *name, const char *pidfile)
465{
466 int rc;
467 pid_t pid;
468
469 /* Check before we even try... */
470 rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
471 if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
472 crm_err("%s: already running [pid %lld in %s]",
473 name, (long long) pid, pidfile);
474 printf("%s: already running [pid %lld in %s]\n",
475 name, (long long) pid, pidfile);
477 }
478
479 pid = fork();
480 if (pid < 0) {
481 fprintf(stderr, "%s: could not start daemon\n", name);
482 crm_perror(LOG_ERR, "fork");
484
485 } else if (pid > 0) {
487 }
488
489 rc = pcmk__lock_pidfile(pidfile, name);
490 if (rc != pcmk_rc_ok) {
491 crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d",
492 pidfile, name, pcmk_rc_str(rc), rc);
493 printf("Could not lock '%s' for %s: %s (%d)\n",
494 pidfile, name, pcmk_rc_str(rc), rc);
496 }
497
498 umask(S_IWGRP | S_IWOTH | S_IROTH);
499
500 close(STDIN_FILENO);
501 pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
502
503 close(STDOUT_FILENO);
504 pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
505
506 close(STDERR_FILENO);
507 pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
508}
509
510char *
511crm_meta_name(const char *field)
512{
513 int lpc = 0;
514 int max = 0;
515 char *crm_name = NULL;
516
517 CRM_CHECK(field != NULL, return NULL);
518 crm_name = crm_strdup_printf(CRM_META "_%s", field);
519
520 /* Massage the names so they can be used as shell variables */
521 max = strlen(crm_name);
522 for (; lpc < max; lpc++) {
523 switch (crm_name[lpc]) {
524 case '-':
525 crm_name[lpc] = '_';
526 break;
527 }
528 }
529 return crm_name;
530}
531
532const char *
533crm_meta_value(GHashTable * hash, const char *field)
534{
535 char *key = NULL;
536 const char *value = NULL;
537
538 key = crm_meta_name(field);
539 if (key) {
540 value = g_hash_table_lookup(hash, key);
541 free(key);
542 }
543
544 return value;
545}
546
547#ifdef HAVE_UUID_UUID_H
548# include <uuid/uuid.h>
549#endif
550
551char *
553{
554 unsigned char uuid[16];
555 char *buffer = malloc(37); /* Including NUL byte */
556
557 uuid_generate(uuid);
558 uuid_unparse(uuid, buffer);
559 return buffer;
560}
561
562#ifdef HAVE_GNUTLS_GNUTLS_H
563void
564crm_gnutls_global_init(void)
565{
566 signal(SIGPIPE, SIG_IGN);
567 gnutls_global_init();
568}
569#endif
570
576char *
578{
579 struct utsname hostinfo;
580
581 return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename);
582}
583
584bool
585pcmk_str_is_infinity(const char *s) {
587}
588
589bool
591 return pcmk__str_eq(s, CRM_MINUS_INFINITY_S, pcmk__str_none);
592}
593
602void
603pcmk__sleep_ms(unsigned int ms)
604{
605 // @TODO Impose a sane maximum sleep to avoid hanging a process for long
606 //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
607
608 // Use sleep() for any whole seconds
609 if (ms >= 1000) {
610 sleep(ms / 1000);
611 ms -= ms / 1000;
612 }
613
614 if (ms == 0) {
615 return;
616 }
617
618#if defined(HAVE_NANOSLEEP)
619 // nanosleep() is POSIX-2008, so prefer that
620 {
621 struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
622
623 nanosleep(&req, NULL);
624 }
625#elif defined(HAVE_USLEEP)
626 // usleep() is widely available, though considered obsolete
627 usleep((useconds_t) ms);
628#else
629 // Otherwise use a trick with select() timeout
630 {
631 struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
632
633 select(0, NULL, NULL, NULL, &tv);
634 }
635#endif
636}
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:177
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition pid.c:219
Utility functions.
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:363
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition utils.c:533
bool pcmk_str_is_infinity(const char *s)
Definition utils.c:585
int pcmk__score_yellow
Definition utils.c:58
char * score2char_stack(int score, char *buf, size_t len)
Definition utils.c:101
#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:169
gboolean crm_config_warning
Definition utils.c:53
char * crm_generate_uuid(void)
Definition utils.c:552
char * crm_meta_name(const char *field)
Definition utils.c:511
void pcmk__daemonize(const char *name, const char *pidfile)
Definition utils.c:464
char * score2char(int score)
Definition utils.c:117
int char2score(const char *score)
Definition utils.c:61
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition utils.c:129
int pcmk__score_green
Definition utils.c:57
guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition utils.c:314
int compare_version(const char *version1, const char *version2)
Definition utils.c:232
void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork)
Definition utils.c:440
void pcmk__sleep_ms(unsigned int ms)
Definition utils.c:603
gboolean crm_config_error
Definition utils.c:52
char * pcmk_hostname()
Get the local hostname.
Definition utils.c:577
char * crm_system_name
Definition utils.c:54
bool pcmk_str_is_minus_infinity(const char *s)
Definition utils.c:590
int pcmk__score_red
Definition utils.c:56
#define CRM_DAEMON_USER
Definition config.h:32
#define _Noreturn
Definition config.h:652
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_SCORE_INFINITY
Definition crm.h:85
#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:983
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:141
long long int crm_time_get_seconds(crm_time_t *dt)
Definition iso8601.c:308
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:353
#define crm_warn(fmt, args...)
Definition logging.h:351
#define CRM_XS
Definition logging.h:54
void crm_enable_stderr(int enable)
Definition logging.c:960
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:301
#define CRM_CHECK(expr, failure_action)
Definition logging.h:218
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition logging.c:468
#define crm_err(fmt, args...)
Definition logging.h:350
#define crm_trace(fmt, args...)
Definition logging.h:356
Wrappers for and extensions to glib mainloop.
char * name
Definition pcmk_fence.c:31
int rc
Definition pcmk_fence.c:35
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:420
@ CRM_EX_ERROR
Definition results.h:218
@ CRM_EX_OSERR
Definition results.h:236
@ CRM_EX_OK
Definition results.h:217
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition results.c:759
@ pcmk_rc_ok
Definition results.h:142
#define pcmk_ok
Definition results.h:67
Services API.
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:97
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:979
Wrappers for and extensions to libxml2.