pacemaker  2.1.3-ea053b43a
Scalable High-Availability cluster resource manager
io.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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)? pcmk_rc_unknown_format : 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 
236 int
237 pcmk__chown_series_sequence(const char *directory, const char *series,
238  uid_t uid, gid_t gid)
239 {
240  char *series_file = NULL;
241  int rc = pcmk_rc_ok;
242 
243  if ((directory == NULL) || (series == NULL)) {
244  return EINVAL;
245  }
246  series_file = crm_strdup_printf("%s/%s.last", directory, series);
247  if (chown(series_file, uid, gid) < 0) {
248  rc = errno;
249  }
250  free(series_file);
251  return rc;
252 }
253 
254 static bool
255 pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
256 {
257  struct passwd *sys_user = NULL;
258 
259  errno = 0;
260  sys_user = getpwnam(CRM_DAEMON_USER);
261  if (sys_user == NULL) {
262  crm_notice("Could not find user %s: %s",
263  CRM_DAEMON_USER, pcmk_rc_str(errno));
264  return FALSE;
265  }
266  if (target_stat->st_uid != sys_user->pw_uid) {
267  crm_notice("%s is not owned by user %s " CRM_XS " uid %d != %d",
268  target_name, CRM_DAEMON_USER, sys_user->pw_uid,
269  target_stat->st_uid);
270  return FALSE;
271  }
272  if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
273  crm_notice("%s is not readable and writable by user %s "
274  CRM_XS " st_mode=0%lo",
275  target_name, CRM_DAEMON_USER,
276  (unsigned long) target_stat->st_mode);
277  return FALSE;
278  }
279  return TRUE;
280 }
281 
282 static bool
283 pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
284 {
285  struct group *sys_grp = NULL;
286 
287  errno = 0;
288  sys_grp = getgrnam(CRM_DAEMON_GROUP);
289  if (sys_grp == NULL) {
290  crm_notice("Could not find group %s: %s",
291  CRM_DAEMON_GROUP, pcmk_rc_str(errno));
292  return FALSE;
293  }
294 
295  if (target_stat->st_gid != sys_grp->gr_gid) {
296  crm_notice("%s is not owned by group %s " CRM_XS " uid %d != %d",
297  target_name, CRM_DAEMON_GROUP,
298  sys_grp->gr_gid, target_stat->st_gid);
299  return FALSE;
300  }
301 
302  if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
303  crm_notice("%s is not readable and writable by group %s "
304  CRM_XS " st_mode=0%lo",
305  target_name, CRM_DAEMON_GROUP,
306  (unsigned long) target_stat->st_mode);
307  return FALSE;
308  }
309  return TRUE;
310 }
311 
326 bool
327 pcmk__daemon_can_write(const char *dir, const char *file)
328 {
329  int s_res = 0;
330  struct stat buf;
331  char *full_file = NULL;
332  const char *target = NULL;
333 
334  // Caller must supply directory
335  CRM_ASSERT(dir != NULL);
336 
337  // If file is given, check whether it exists as a regular file
338  if (file != NULL) {
339  full_file = crm_strdup_printf("%s/%s", dir, file);
340  target = full_file;
341 
342  s_res = stat(full_file, &buf);
343  if (s_res < 0) {
344  crm_notice("%s not found: %s", target, pcmk_rc_str(errno));
345  free(full_file);
346  full_file = NULL;
347  target = NULL;
348 
349  } else if (S_ISREG(buf.st_mode) == FALSE) {
350  crm_err("%s must be a regular file " CRM_XS " st_mode=0%lo",
351  target, (unsigned long) buf.st_mode);
352  free(full_file);
353  return false;
354  }
355  }
356 
357  // If file is not given, ensure dir exists as directory
358  if (target == NULL) {
359  target = dir;
360  s_res = stat(dir, &buf);
361  if (s_res < 0) {
362  crm_err("%s not found: %s", dir, pcmk_rc_str(errno));
363  return false;
364 
365  } else if (S_ISDIR(buf.st_mode) == FALSE) {
366  crm_err("%s must be a directory " CRM_XS " st_mode=0%lo",
367  dir, (unsigned long) buf.st_mode);
368  return false;
369  }
370  }
371 
372  if (!pcmk__daemon_user_can_write(target, &buf)
373  && !pcmk__daemon_group_can_write(target, &buf)) {
374 
375  crm_err("%s must be owned and writable by either user %s or group %s "
376  CRM_XS " st_mode=0%lo",
378  (unsigned long) buf.st_mode);
379  free(full_file);
380  return false;
381  }
382 
383  free(full_file);
384  return true;
385 }
386 
394 void
396 {
397  int fd;
398  DIR *directory;
399 
400  directory = opendir(name);
401  if (directory == NULL) {
402  crm_perror(LOG_ERR, "Could not open %s for syncing", name);
403  return;
404  }
405 
406  fd = dirfd(directory);
407  if (fd < 0) {
408  crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
409  return;
410  }
411 
412  if (fsync(fd) < 0) {
413  crm_perror(LOG_ERR, "Could not sync %s", name);
414  }
415  if (closedir(directory) < 0) {
416  crm_perror(LOG_ERR, "Could not close %s after fsync", name);
417  }
418 }
419 
430 int
431 pcmk__file_contents(const char *filename, char **contents)
432 {
433  FILE *fp;
434  int length, read_len;
435  int rc = pcmk_rc_ok;
436 
437  if ((filename == NULL) || (contents == NULL)) {
438  return EINVAL;
439  }
440 
441  fp = fopen(filename, "r");
442  if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
443  rc = errno;
444  goto bail;
445  }
446 
447  length = ftell(fp);
448  if (length < 0) {
449  rc = errno;
450  goto bail;
451  }
452 
453  if (length == 0) {
454  *contents = NULL;
455  } else {
456  *contents = calloc(length + 1, sizeof(char));
457  if (*contents == NULL) {
458  rc = errno;
459  goto bail;
460  }
461  rewind(fp);
462  read_len = fread(*contents, 1, length, fp); /* Coverity: False positive */
463  if (read_len != length) {
464  free(*contents);
465  *contents = NULL;
466  rc = EIO;
467  }
468  }
469 
470 bail:
471  if (fp != NULL) {
472  fclose(fp);
473  }
474  return rc;
475 }
476 
486 int
487 pcmk__write_sync(int fd, const char *contents)
488 {
489  int rc = 0;
490  FILE *fp = fdopen(fd, "w");
491 
492  if (fp == NULL) {
493  return errno;
494  }
495  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
496  rc = EIO;
497  }
498  if (fflush(fp) != 0) {
499  rc = errno;
500  }
501  if (fsync(fileno(fp)) < 0) {
502  rc = errno;
503  }
504  fclose(fp);
505  return rc;
506 }
507 
516 int
518 {
519  int flag = fcntl(fd, F_GETFL);
520 
521  if (flag < 0) {
522  return errno;
523  }
524  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
525  return errno;
526  }
527  return pcmk_rc_ok;
528 }
529 
539 const char *
541 {
542  const char *dir = getenv("TMPDIR");
543 
544  return (dir && (*dir == '/'))? dir : "/tmp";
545 }
546 
557 void
559 {
560  DIR *dir;
561  struct rlimit rlim;
562  rlim_t max_fd;
563  int min_fd = (all? 0 : (STDERR_FILENO + 1));
564 
565  /* Find the current process's (soft) limit for open files. getrlimit()
566  * should always work, but have a fallback just in case.
567  */
568  if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
569  max_fd = rlim.rlim_cur - 1;
570  } else {
571  long conf_max = sysconf(_SC_OPEN_MAX);
572 
573  max_fd = (conf_max > 0)? conf_max : 1024;
574  }
575 
576  /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
577  * all open files for the current process, named as the file descriptor.
578  * Use this if available, because it's more efficient than a shotgun
579  * approach to closing descriptors.
580  */
581 #if SUPPORT_PROCFS
582  dir = opendir("/proc/self/fd");
583  if (dir == NULL) {
584  dir = opendir("/dev/fd");
585  }
586 #else
587  dir = opendir("/dev/fd");
588 #endif
589  if (dir != NULL) {
590  struct dirent *entry;
591  int dir_fd = dirfd(dir);
592 
593  while ((entry = readdir(dir)) != NULL) {
594  int lpc = atoi(entry->d_name);
595 
596  /* How could one of these entries be higher than max_fd, you ask?
597  * It isn't possible in normal operation, but when run under
598  * valgrind, valgrind can open high-numbered file descriptors for
599  * its own use that are higher than the process's soft limit.
600  * These will show up in the fd directory but aren't closable.
601  */
602  if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
603  close(lpc);
604  }
605  }
606  closedir(dir);
607  return;
608  }
609 
610  /* If no fd directory is available, iterate over all possible descriptors.
611  * This is less efficient due to the overhead of many system calls.
612  */
613  for (int lpc = max_fd; lpc >= min_fd; lpc--) {
614  close(lpc);
615  }
616 }
617 
626 char *
627 pcmk__full_path(const char *filename, const char *dirname)
628 {
629  char *path = NULL;
630 
631  CRM_ASSERT(filename != NULL);
632 
633  if (filename[0] == '/') {
634  path = strdup(filename);
635  CRM_ASSERT(path != NULL);
636 
637  } else {
638  CRM_ASSERT(dirname != NULL);
639  path = crm_strdup_printf("%s/%s", dirname, filename);
640  }
641 
642  return path;
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:226
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:360
const char * name
Definition: cib.c:24
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
const char * pcmk__get_tmpdir()
Definition: io.c:540
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:237
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:370
char * strerror(int errnum)
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:327
#define CRM_DAEMON_GROUP
Definition: config.h:27
#define crm_debug(fmt, args...)
Definition: logging.h:363
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:364
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define CRM_DAEMON_USER
Definition: config.h:30
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:45
const char * target
Definition: pcmk_fence.c:28
#define CRM_XS
Definition: logging.h:55
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:309
const char * path
Definition: cib.c:26
#define crm_err(fmt, args...)
Definition: logging.h:358
#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:627
void pcmk__close_fds_in_child(bool all)
Definition: io.c:558
void pcmk__sync_directory(const char *name)
Definition: io.c:395
int pcmk__write_sync(int fd, const char *contents)
Definition: io.c:487
int pcmk__set_nonblocking(int fd)
Definition: io.c:517
int pcmk__file_contents(const char *filename, char **contents)
Definition: io.c:431