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