pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
utils.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2025 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 <stdio.h>
16#include <unistd.h>
17#include <string.h>
18#include <stdlib.h>
19#include <limits.h>
20#include <pwd.h>
21#include <time.h>
22#include <libgen.h>
23#include <signal.h>
24#include <grp.h>
25
26#include <qb/qbdefs.h>
27
28#include <crm/crm.h>
29#include <crm/services.h>
30#include <crm/cib/internal.h>
31#include <crm/common/xml.h>
32#include <crm/common/util.h>
33#include <crm/common/ipc.h>
34#include <crm/common/iso8601.h>
35#include <crm/common/mainloop.h>
36#include <libxml/parser.h> // xmlCleanupParser()
37#include <libxml2/libxml/relaxng.h>
38
39#include "crmcommon_private.h"
40
42
45char *crm_system_name = NULL;
46
54void
56{
57 // @TODO This isn't really everything, move all cleanup here
61
62 free(crm_system_name);
63 crm_system_name = NULL;
64
65 // Clean up external library global state
66 qb_log_fini(); // Don't log anything after this point
67 xmlCleanupParser();
68}
69
70bool
71pcmk__is_user_in_group(const char *user, const char *group)
72{
73 struct group *grent;
74 char **gr_mem;
75
76 if (user == NULL || group == NULL) {
77 return false;
78 }
79
80 setgrent();
81 while ((grent = getgrent()) != NULL) {
82 if (grent->gr_mem == NULL) {
83 continue;
84 }
85
86 if(strcmp(group, grent->gr_name) != 0) {
87 continue;
88 }
89
90 gr_mem = grent->gr_mem;
91 while (*gr_mem != NULL) {
92 if (!strcmp(user, *gr_mem++)) {
93 endgrent();
94 return true;
95 }
96 }
97 }
98 endgrent();
99 return false;
100}
101
102int
103crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
104{
105 int rc = pcmk_ok;
106 char *buffer = NULL;
107 struct passwd pwd;
108 struct passwd *pwentry = NULL;
109
110 buffer = calloc(1, PCMK__PW_BUFFER_LEN);
111 if (buffer == NULL) {
112 return -ENOMEM;
113 }
114
115 rc = getpwnam_r(name, &pwd, buffer, PCMK__PW_BUFFER_LEN, &pwentry);
116 if (pwentry) {
117 if (uid) {
118 *uid = pwentry->pw_uid;
119 }
120 if (gid) {
121 *gid = pwentry->pw_gid;
122 }
123 crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
124
125 } else {
126 rc = rc? -rc : -EINVAL;
127 crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
128 }
129
130 free(buffer);
131 return rc;
132}
133
142int
143pcmk_daemon_user(uid_t *uid, gid_t *gid)
144{
145 static uid_t daemon_uid;
146 static gid_t daemon_gid;
147 static bool found = false;
148 int rc = pcmk_ok;
149
150 if (!found) {
151 rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid);
152 if (rc == pcmk_ok) {
153 found = true;
154 }
155 }
156 if (found) {
157 if (uid) {
158 *uid = daemon_uid;
159 }
160 if (gid) {
161 *gid = daemon_gid;
162 }
163 }
164 return rc;
165}
166
174static int
175version_helper(const char *text, const char **end_text)
176{
177 int atoi_result = -1;
178
179 pcmk__assert(end_text != NULL);
180
181 errno = 0;
182
183 if (text != NULL && text[0] != 0) {
184 /* seemingly sacrificing const-correctness -- because while strtol
185 doesn't modify the input, it doesn't want to artificially taint the
186 "end_text" pointer-to-pointer-to-first-char-in-string with constness
187 in case the input wasn't actually constant -- by semantic definition
188 not a single character will get modified so it shall be perfectly
189 safe to make compiler happy with dropping "const" qualifier here */
190 atoi_result = (int) strtol(text, (char **) end_text, 10);
191
192 if (errno == EINVAL) {
193 crm_err("Conversion of '%s' %c failed", text, text[0]);
194 atoi_result = -1;
195 }
196 }
197 return atoi_result;
198}
199
200/*
201 * version1 < version2 : -1
202 * version1 = version2 : 0
203 * version1 > version2 : 1
204 */
205int
206compare_version(const char *version1, const char *version2)
207{
208 int rc = 0;
209 int lpc = 0;
210 const char *ver1_iter, *ver2_iter;
211
212 if (version1 == NULL && version2 == NULL) {
213 return 0;
214 } else if (version1 == NULL) {
215 return -1;
216 } else if (version2 == NULL) {
217 return 1;
218 }
219
220 ver1_iter = version1;
221 ver2_iter = version2;
222
223 while (1) {
224 int digit1 = 0;
225 int digit2 = 0;
226
227 lpc++;
228
229 if (ver1_iter == ver2_iter) {
230 break;
231 }
232
233 if (ver1_iter != NULL) {
234 digit1 = version_helper(ver1_iter, &ver1_iter);
235 }
236
237 if (ver2_iter != NULL) {
238 digit2 = version_helper(ver2_iter, &ver2_iter);
239 }
240
241 if (digit1 < digit2) {
242 rc = -1;
243 break;
244
245 } else if (digit1 > digit2) {
246 rc = 1;
247 break;
248 }
249
250 if (ver1_iter != NULL && *ver1_iter == '.') {
251 ver1_iter++;
252 }
253 if (ver1_iter != NULL && *ver1_iter == '\0') {
254 ver1_iter = NULL;
255 }
256
257 if (ver2_iter != NULL && *ver2_iter == '.') {
258 ver2_iter++;
259 }
260 if (ver2_iter != NULL && *ver2_iter == 0) {
261 ver2_iter = NULL;
262 }
263 }
264
265 if (rc == 0) {
266 crm_trace("%s == %s (%d)", version1, version2, lpc);
267 } else if (rc < 0) {
268 crm_trace("%s < %s (%d)", version1, version2, lpc);
269 } else if (rc > 0) {
270 crm_trace("%s > %s (%d)", version1, version2, lpc);
271 }
272
273 return rc;
274}
275
287void
288pcmk__daemonize(const char *name, const char *pidfile)
289{
290 int rc;
291 pid_t pid;
292
293 /* Check before we even try... */
294 rc = pcmk__pidfile_matches(pidfile, 1, name, &pid);
295 if ((rc != pcmk_rc_ok) && (rc != ENOENT)) {
296 crm_err("%s: already running [pid %lld in %s]",
297 name, (long long) pid, pidfile);
298 printf("%s: already running [pid %lld in %s]\n",
299 name, (long long) pid, pidfile);
301 }
302
303 pid = fork();
304 if (pid < 0) {
305 fprintf(stderr, "%s: could not start daemon\n", name);
306 crm_perror(LOG_ERR, "fork");
308
309 } else if (pid > 0) {
311 }
312
313 rc = pcmk__lock_pidfile(pidfile, name);
314 if (rc != pcmk_rc_ok) {
315 crm_err("Could not lock '%s' for %s: %s " QB_XS " rc=%d",
316 pidfile, name, pcmk_rc_str(rc), rc);
317 printf("Could not lock '%s' for %s: %s (%d)\n",
318 pidfile, name, pcmk_rc_str(rc), rc);
320 }
321
322 umask(S_IWGRP | S_IWOTH | S_IROTH);
323
324 close(STDIN_FILENO);
325 pcmk__open_devnull(O_RDONLY); // stdin (fd 0)
326
327 close(STDOUT_FILENO);
328 pcmk__open_devnull(O_WRONLY); // stdout (fd 1)
329
330 close(STDERR_FILENO);
331 pcmk__open_devnull(O_WRONLY); // stderr (fd 2)
332}
333
334#ifdef HAVE_UUID_UUID_H
335# include <uuid/uuid.h>
336#endif
337
338char *
340{
341 unsigned char uuid[16];
342 char *buffer = malloc(37); /* Including NUL byte */
343
344 pcmk__mem_assert(buffer);
345 uuid_generate(uuid);
346 uuid_unparse(uuid, buffer);
347 return buffer;
348}
349
358void
359pcmk__sleep_ms(unsigned int ms)
360{
361 // @TODO Impose a sane maximum sleep to avoid hanging a process for long
362 //CRM_CHECK(ms <= MAX_SLEEP, ms = MAX_SLEEP);
363
364 // Use sleep() for any whole seconds
365 if (ms >= 1000) {
366 sleep(ms / 1000);
367 ms -= ms / 1000;
368 }
369
370 if (ms == 0) {
371 return;
372 }
373
374#if defined(HAVE_NANOSLEEP)
375 // nanosleep() is POSIX-2008, so prefer that
376 {
377 struct timespec req = { .tv_sec = 0, .tv_nsec = (long) (ms * 1000000) };
378
379 nanosleep(&req, NULL);
380 }
381#elif defined(HAVE_USLEEP)
382 // usleep() is widely available, though considered obsolete
383 usleep((useconds_t) ms);
384#else
385 // Otherwise use a trick with select() timeout
386 {
387 struct timeval tv = { .tv_sec = 0, .tv_usec = (suseconds_t) ms };
388
389 select(0, NULL, NULL, NULL, &tv);
390 }
391#endif
392}
393
404guint
405pcmk__create_timer(guint interval_ms, GSourceFunc fn, gpointer data)
406{
407 pcmk__assert(interval_ms != 0 && fn != NULL);
408
409 if (interval_ms % 1000 == 0) {
410 /* In case interval_ms is 0, the call to pcmk__timeout_ms2s ensures
411 * an interval of one second.
412 */
413 return g_timeout_add_seconds(pcmk__timeout_ms2s(interval_ms), fn, data);
414 } else {
415 return g_timeout_add(interval_ms, fn, data);
416 }
417}
418
428guint
429pcmk__timeout_ms2s(guint timeout_ms)
430{
431 guint quot, rem;
432
433 if (timeout_ms == 0) {
434 return 0;
435 } else if (timeout_ms < 1000) {
436 return 1;
437 }
438
439 quot = timeout_ms / 1000;
440 rem = timeout_ms % 1000;
441
442 if (rem >= 500) {
443 quot += 1;
444 }
445
446 return quot;
447}
448
449// Deprecated functions kept only for backward API compatibility
450// LCOV_EXCL_START
451
453
454static void
455_gnutls_log_func(int level, const char *msg)
456{
457 crm_trace("%s", msg);
458}
459
460void
462{
463 signal(SIGPIPE, SIG_IGN);
464 gnutls_global_init();
465 gnutls_global_set_log_level(8);
466 gnutls_global_set_log_function(_gnutls_log_func);
467}
468
479bool
481{
482 return pcmk__str_any_of(name,
483 "attrd",
491 "pacemaker-attrd",
492 "pacemaker-based",
493 "pacemaker-controld",
494 "pacemaker-execd",
495 "pacemaker-fenced",
496 "pacemaker-remoted",
497 "pacemaker-schedulerd",
498 "stonith-ng",
499 "stonithd",
500 NULL);
501}
502
503// LCOV_EXCL_STOP
504// End deprecated API
const char * name
Definition cib.c:26
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, const char *expected_name, pid_t *pid)
Definition pid.c:168
int pcmk__lock_pidfile(const char *filename, const char *name)
Definition pid.c:210
Utility functions.
guint pcmk__timeout_ms2s(guint timeout_ms)
Definition utils.c:429
guint pcmk__create_timer(guint interval_ms, GSourceFunc fn, gpointer data)
Definition utils.c:405
void pcmk_common_cleanup(void)
Free all memory used by libcrmcommon.
Definition utils.c:55
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition utils.c:143
char * crm_generate_uuid(void)
Definition utils.c:339
bool pcmk__config_has_error
Definition utils.c:43
void pcmk__daemonize(const char *name, const char *pidfile)
Definition utils.c:288
bool pcmk__is_user_in_group(const char *user, const char *group)
Definition utils.c:71
int crm_user_lookup(const char *name, uid_t *uid, gid_t *gid)
Definition utils.c:103
void crm_gnutls_global_init(void)
Definition utils.c:461
int compare_version(const char *version1, const char *version2)
Definition utils.c:206
bool crm_is_daemon_name(const char *name)
Check whether string represents a client name used by cluster daemons.
Definition utils.c:480
void pcmk__sleep_ms(unsigned int ms)
Definition utils.c:359
char * crm_system_name
Definition utils.c:45
bool pcmk__config_has_warning
Definition utils.c:44
#define CRM_DAEMON_USER
Definition config.h:27
char data[0]
Definition cpg.c:10
uint32_t pid
Definition cpg.c:1
A dumping ground.
#define CRM_SYSTEM_CIB
Definition crm.h:83
#define CRM_SYSTEM_CRMD
Definition crm.h:84
#define CRM_SYSTEM_DC
Definition crm.h:81
#define CRM_SYSTEM_MCP
Definition crm.h:88
#define CRM_SYSTEM_LRMD
Definition crm.h:85
#define CRM_SYSTEM_TENGINE
Definition crm.h:87
#define CRM_SYSTEM_PENGINE
Definition crm.h:86
#define PCMK__PW_BUFFER_LEN
IPC interface to Pacemaker daemons.
ISO_8601 Date handling.
#define CRM_TRACE_INIT_DATA(name)
Definition logging.h:111
#define crm_info(fmt, args...)
Definition logging.h:365
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:299
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_trace(fmt, args...)
Definition logging.h:370
void pcmk__free_common_logger(void)
Definition logging.c:1280
Wrappers for and extensions to glib mainloop.
void mainloop_cleanup(void)
Definition mainloop.c:417
const char * pcmk_strerror(int rc)
Definition results.c:257
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ CRM_EX_ERROR
Unspecified error.
Definition results.h:234
@ CRM_EX_OSERR
External (OS/environmental) problem.
Definition results.h:254
@ CRM_EX_OK
Success.
Definition results.h:233
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition results.c:1058
@ pcmk_rc_ok
Definition results.h:159
#define pcmk_ok
Definition results.h:65
#define pcmk__assert(expr)
#define pcmk__mem_assert(ptr)
void pcmk__schema_cleanup(void)
Definition schemas.c:712
Services API.
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1053
Deprecated Pacemaker utilities.
Wrappers for and extensions to libxml2.