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