pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
io.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/param.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/resource.h>
16 
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <fcntl.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <pwd.h>
26 #include <grp.h>
27 
28 #include <crm/crm.h>
29 #include <crm/common/util.h>
30 
40 int
41 pcmk__build_path(const char *path_c, mode_t mode)
42 {
43  int offset = 1, len = 0;
44  int rc = pcmk_rc_ok;
45  char *path = strdup(path_c);
46 
47  // cppcheck seems not to understand the abort logic in CRM_CHECK
48  // cppcheck-suppress memleak
49  CRM_CHECK(path != NULL, return -ENOMEM);
50  for (len = strlen(path); offset < len; offset++) {
51  if (path[offset] == '/') {
52  path[offset] = 0;
53  if ((mkdir(path, mode) < 0) && (errno != EEXIST)) {
54  rc = errno;
55  goto done;
56  }
57  path[offset] = '/';
58  }
59  }
60  if ((mkdir(path, mode) < 0) && (errno != EEXIST)) {
61  rc = errno;
62  }
63 done:
64  free(path);
65  return rc;
66 }
67 
80 int
81 pcmk__real_path(const char *path, char **resolved_path)
82 {
83  CRM_CHECK((path != NULL) && (resolved_path != NULL), return EINVAL);
84 
85 #if _POSIX_VERSION >= 200809L
86  /* Recent C libraries can dynamically allocate memory as needed */
87  *resolved_path = realpath(path, NULL);
88  return (*resolved_path == NULL)? errno : pcmk_rc_ok;
89 
90 #elif defined(PATH_MAX)
91  /* Older implementations require pre-allocated memory */
92  /* (this is less desirable because PATH_MAX may be huge or not defined) */
93  *resolved_path = malloc(PATH_MAX);
94  if ((*resolved_path == NULL) || (realpath(path, *resolved_path) == NULL)) {
95  return errno;
96  }
97  return pcmk_rc_ok;
98 #else
99  *resolved_path = NULL;
100  return ENOTSUP;
101 #endif
102 }
103 
116 char *
117 pcmk__series_filename(const char *directory, const char *series,
118  unsigned int sequence, bool bzip)
119 {
120  pcmk__assert((directory != NULL) && (series != NULL));
121  return crm_strdup_printf("%s/%s-%u.%s", directory, series, sequence,
122  (bzip? "bz2" : "raw"));
123 }
124 
135 int
136 pcmk__read_series_sequence(const char *directory, const char *series,
137  unsigned int *seq)
138 {
139  int rc;
140  FILE *fp = NULL;
141  char *series_file = NULL;
142 
143  if ((directory == NULL) || (series == NULL) || (seq == NULL)) {
144  return EINVAL;
145  }
146 
147  series_file = crm_strdup_printf("%s/%s.last", directory, series);
148  fp = fopen(series_file, "r");
149  if (fp == NULL) {
150  rc = errno;
151  crm_debug("Could not open series file %s: %s",
152  series_file, strerror(rc));
153  free(series_file);
154  return rc;
155  }
156  errno = 0;
157  if (fscanf(fp, "%u", seq) != 1) {
158  rc = (errno == 0)? ENODATA : errno;
159  crm_debug("Could not read sequence number from series file %s: %s",
160  series_file, pcmk_rc_str(rc));
161  fclose(fp);
162  return rc;
163  }
164  fclose(fp);
165  crm_trace("Found last sequence number %u in series file %s",
166  *seq, series_file);
167  free(series_file);
168  return pcmk_rc_ok;
169 }
170 
182 void
183 pcmk__write_series_sequence(const char *directory, const char *series,
184  unsigned int sequence, int max)
185 {
186  int rc = 0;
187  FILE *file_strm = NULL;
188  char *series_file = NULL;
189 
190  CRM_CHECK(directory != NULL, return);
191  CRM_CHECK(series != NULL, return);
192 
193  if (max == 0) {
194  return;
195  }
196  if (max > 0 && sequence >= max) {
197  sequence = 0;
198  }
199 
200  series_file = crm_strdup_printf("%s/%s.last", directory, series);
201  file_strm = fopen(series_file, "w");
202  if (file_strm != NULL) {
203  rc = fprintf(file_strm, "%u", sequence);
204  if (rc < 0) {
205  crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
206  }
207 
208  } else {
209  crm_err("Cannot open series file %s for writing", series_file);
210  }
211 
212  if (file_strm != NULL) {
213  fflush(file_strm);
214  fclose(file_strm);
215  }
216 
217  crm_trace("Wrote %d to %s", sequence, series_file);
218  free(series_file);
219 }
220 
233 int
234 pcmk__chown_series_sequence(const char *directory, const char *series,
235  uid_t uid, gid_t gid)
236 {
237  char *series_file = NULL;
238  int rc = pcmk_rc_ok;
239 
240  if ((directory == NULL) || (series == NULL)) {
241  return EINVAL;
242  }
243  series_file = crm_strdup_printf("%s/%s.last", directory, series);
244  if (chown(series_file, uid, gid) < 0) {
245  rc = errno;
246  }
247  free(series_file);
248  return rc;
249 }
250 
251 static bool
252 pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
253 {
254  struct passwd *sys_user = NULL;
255 
256  errno = 0;
257  sys_user = getpwnam(CRM_DAEMON_USER);
258  if (sys_user == NULL) {
259  crm_notice("Could not find user %s: %s",
260  CRM_DAEMON_USER, pcmk_rc_str(errno));
261  return FALSE;
262  }
263  if (target_stat->st_uid != sys_user->pw_uid) {
264  crm_notice("%s is not owned by user %s " QB_XS " uid %d != %d",
265  target_name, CRM_DAEMON_USER, sys_user->pw_uid,
266  target_stat->st_uid);
267  return FALSE;
268  }
269  if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
270  crm_notice("%s is not readable and writable by user %s "
271  QB_XS " st_mode=0%lo",
272  target_name, CRM_DAEMON_USER,
273  (unsigned long) target_stat->st_mode);
274  return FALSE;
275  }
276  return TRUE;
277 }
278 
279 static bool
280 pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
281 {
282  struct group *sys_grp = NULL;
283 
284  errno = 0;
285  sys_grp = getgrnam(CRM_DAEMON_GROUP);
286  if (sys_grp == NULL) {
287  crm_notice("Could not find group %s: %s",
288  CRM_DAEMON_GROUP, pcmk_rc_str(errno));
289  return FALSE;
290  }
291 
292  if (target_stat->st_gid != sys_grp->gr_gid) {
293  crm_notice("%s is not owned by group %s " QB_XS " uid %d != %d",
294  target_name, CRM_DAEMON_GROUP,
295  sys_grp->gr_gid, target_stat->st_gid);
296  return FALSE;
297  }
298 
299  if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
300  crm_notice("%s is not readable and writable by group %s "
301  QB_XS " st_mode=0%lo",
302  target_name, CRM_DAEMON_GROUP,
303  (unsigned long) target_stat->st_mode);
304  return FALSE;
305  }
306  return TRUE;
307 }
308 
323 bool
324 pcmk__daemon_can_write(const char *dir, const char *file)
325 {
326  int s_res = 0;
327  struct stat buf;
328  char *full_file = NULL;
329  const char *target = NULL;
330 
331  // Caller must supply directory
332  pcmk__assert(dir != NULL);
333 
334  // If file is given, check whether it exists as a regular file
335  if (file != NULL) {
336  full_file = crm_strdup_printf("%s/%s", dir, file);
337  target = full_file;
338 
339  s_res = stat(full_file, &buf);
340  if (s_res < 0) {
341  crm_notice("%s not found: %s", target, pcmk_rc_str(errno));
342  free(full_file);
343  full_file = NULL;
344  target = NULL;
345 
346  } else if (S_ISREG(buf.st_mode) == FALSE) {
347  crm_err("%s must be a regular file " QB_XS " st_mode=0%lo",
348  target, (unsigned long) buf.st_mode);
349  free(full_file);
350  return false;
351  }
352  }
353 
354  // If file is not given, ensure dir exists as directory
355  if (target == NULL) {
356  target = dir;
357  s_res = stat(dir, &buf);
358  if (s_res < 0) {
359  crm_err("%s not found: %s", dir, pcmk_rc_str(errno));
360  return false;
361 
362  } else if (S_ISDIR(buf.st_mode) == FALSE) {
363  crm_err("%s must be a directory " QB_XS " st_mode=0%lo",
364  dir, (unsigned long) buf.st_mode);
365  return false;
366  }
367  }
368 
369  if (!pcmk__daemon_user_can_write(target, &buf)
370  && !pcmk__daemon_group_can_write(target, &buf)) {
371 
372  crm_err("%s must be owned and writable by either user %s or group %s "
373  QB_XS " st_mode=0%lo",
375  (unsigned long) buf.st_mode);
376  free(full_file);
377  return false;
378  }
379 
380  free(full_file);
381  return true;
382 }
383 
391 void
393 {
394  int fd;
395  DIR *directory;
396 
397  directory = opendir(name);
398  if (directory == NULL) {
399  crm_perror(LOG_ERR, "Could not open %s for syncing", name);
400  return;
401  }
402 
403  fd = dirfd(directory);
404  if (fd < 0) {
405  crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
406  return;
407  }
408 
409  if (fsync(fd) < 0) {
410  crm_perror(LOG_ERR, "Could not sync %s", name);
411  }
412  if (closedir(directory) < 0) {
413  crm_perror(LOG_ERR, "Could not close %s after fsync", name);
414  }
415 }
416 
427 int
428 pcmk__file_contents(const char *filename, char **contents)
429 {
430  FILE *fp;
431  int length, read_len;
432  int rc = pcmk_rc_ok;
433 
434  if ((filename == NULL) || (contents == NULL)) {
435  return EINVAL;
436  }
437 
438  fp = fopen(filename, "r");
439  if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
440  rc = errno;
441  goto bail;
442  }
443 
444  length = ftell(fp);
445  if (length < 0) {
446  rc = errno;
447  goto bail;
448  }
449 
450  if (length == 0) {
451  *contents = NULL;
452  } else {
453  *contents = calloc(length + 1, sizeof(char));
454  if (*contents == NULL) {
455  rc = errno;
456  goto bail;
457  }
458  rewind(fp);
459 
460  read_len = fread(*contents, 1, length, fp);
461  if (read_len != length) {
462  free(*contents);
463  *contents = NULL;
464  rc = EIO;
465  } else {
466  /* Coverity thinks *contents isn't null-terminated. It doesn't
467  * understand calloc().
468  */
469  (*contents)[length] = '\0';
470  }
471  }
472 
473 bail:
474  if (fp != NULL) {
475  fclose(fp);
476  }
477  return rc;
478 }
479 
489 int
490 pcmk__write_sync(int fd, const char *contents)
491 {
492  int rc = 0;
493  FILE *fp = fdopen(fd, "w");
494 
495  if (fp == NULL) {
496  return errno;
497  }
498  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
499  rc = EIO;
500  }
501  if (fflush(fp) != 0) {
502  rc = errno;
503  }
504  if (fsync(fileno(fp)) < 0) {
505  rc = errno;
506  }
507  fclose(fp);
508  return rc;
509 }
510 
519 int
521 {
522  int flag = fcntl(fd, F_GETFL);
523 
524  if (flag < 0) {
525  return errno;
526  }
527  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
528  return errno;
529  }
530  return pcmk_rc_ok;
531 }
532 
542 const char *
544 {
545  const char *dir = getenv("TMPDIR");
546 
547  return (dir && (*dir == '/'))? dir : "/tmp";
548 }
549 
560 void
562 {
563  DIR *dir;
564  struct rlimit rlim;
565  rlim_t max_fd;
566  int min_fd = (all? 0 : (STDERR_FILENO + 1));
567 
568  /* Find the current process's (soft) limit for open files. getrlimit()
569  * should always work, but have a fallback just in case.
570  */
571  if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
572  max_fd = rlim.rlim_cur - 1;
573  } else {
574  long conf_max = sysconf(_SC_OPEN_MAX);
575 
576  max_fd = (conf_max > 0)? conf_max : 1024;
577  }
578 
579  /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
580  * all open files for the current process, named as the file descriptor.
581  * Use this if available, because it's more efficient than a shotgun
582  * approach to closing descriptors.
583  */
584 #if HAVE_LINUX_PROCFS
585  dir = opendir("/proc/self/fd");
586  if (dir == NULL) {
587  dir = opendir("/dev/fd");
588  }
589 #else
590  dir = opendir("/dev/fd");
591 #endif // HAVE_LINUX_PROCFS
592  if (dir != NULL) {
593  struct dirent *entry;
594  int dir_fd = dirfd(dir);
595 
596  while ((entry = readdir(dir)) != NULL) {
597  int lpc = atoi(entry->d_name);
598 
599  /* How could one of these entries be higher than max_fd, you ask?
600  * It isn't possible in normal operation, but when run under
601  * valgrind, valgrind can open high-numbered file descriptors for
602  * its own use that are higher than the process's soft limit.
603  * These will show up in the fd directory but aren't closable.
604  */
605  if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
606  close(lpc);
607  }
608  }
609  closedir(dir);
610  return;
611  }
612 
613  /* If no fd directory is available, iterate over all possible descriptors.
614  * This is less efficient due to the overhead of many system calls.
615  */
616  for (int lpc = max_fd; lpc >= min_fd; lpc--) {
617  close(lpc);
618  }
619 }
620 
629 char *
630 pcmk__full_path(const char *filename, const char *dirname)
631 {
632  pcmk__assert(filename != NULL);
633 
634  if (filename[0] == '/') {
635  return pcmk__str_copy(filename);
636  }
637  pcmk__assert(dirname != NULL);
638  return crm_strdup_printf("%s/%s", dirname, filename);
639 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:365
const char * name
Definition: cib.c:26
int pcmk__real_path(const char *path, char **resolved_path)
Definition: io.c:81
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:234
void pcmk__write_series_sequence(const char *directory, const char *series, unsigned int sequence, int max)
Definition: io.c:183
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:609
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:324
const char * pcmk__get_tmpdir(void)
Definition: io.c:543
#define CRM_DAEMON_GROUP
Definition: config.h:24
#define crm_debug(fmt, args...)
Definition: logging.h:370
Utility functions.
#define crm_trace(fmt, args...)
Definition: logging.h:372
#define CRM_DAEMON_USER
Definition: config.h:27
#define pcmk__str_copy(str)
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:41
#define pcmk__assert(expr)
const char * target
Definition: pcmk_fence.c:31
#define ENODATA
Definition: portability.h:61
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:299
const char * path
Definition: cib.c:28
char * pcmk__series_filename(const char *directory, const char *series, unsigned int sequence, bool bzip)
Definition: io.c:117
#define crm_err(fmt, args...)
Definition: logging.h:359
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition: io.c:136
char * pcmk__full_path(const char *filename, const char *dirname)
Duplicate a file path, inserting a prefix if not absolute.
Definition: io.c:630
void pcmk__close_fds_in_child(bool all)
Definition: io.c:561
void pcmk__sync_directory(const char *name)
Definition: io.c:392
int pcmk__write_sync(int fd, const char *contents)
Definition: io.c:490
int pcmk__set_nonblocking(int fd)
Definition: io.c:520
int pcmk__file_contents(const char *filename, char **contents)
Definition: io.c:428
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1