pacemaker  2.1.6-802a72226b
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)? 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  read_len = fread(*contents, 1, length, fp); /* Coverity: False positive */
464  if (read_len != length) {
465  free(*contents);
466  *contents = NULL;
467  rc = EIO;
468  }
469  }
470 
471 bail:
472  if (fp != NULL) {
473  fclose(fp);
474  }
475  return rc;
476 }
477 
487 int
488 pcmk__write_sync(int fd, const char *contents)
489 {
490  int rc = 0;
491  FILE *fp = fdopen(fd, "w");
492 
493  if (fp == NULL) {
494  return errno;
495  }
496  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
497  rc = EIO;
498  }
499  if (fflush(fp) != 0) {
500  rc = errno;
501  }
502  if (fsync(fileno(fp)) < 0) {
503  rc = errno;
504  }
505  fclose(fp);
506  return rc;
507 }
508 
517 int
519 {
520  int flag = fcntl(fd, F_GETFL);
521 
522  if (flag < 0) {
523  return errno;
524  }
525  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
526  return errno;
527  }
528  return pcmk_rc_ok;
529 }
530 
540 const char *
542 {
543  const char *dir = getenv("TMPDIR");
544 
545  return (dir && (*dir == '/'))? dir : "/tmp";
546 }
547 
558 void
560 {
561  DIR *dir;
562  struct rlimit rlim;
563  rlim_t max_fd;
564  int min_fd = (all? 0 : (STDERR_FILENO + 1));
565 
566  /* Find the current process's (soft) limit for open files. getrlimit()
567  * should always work, but have a fallback just in case.
568  */
569  if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
570  max_fd = rlim.rlim_cur - 1;
571  } else {
572  long conf_max = sysconf(_SC_OPEN_MAX);
573 
574  max_fd = (conf_max > 0)? conf_max : 1024;
575  }
576 
577  /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
578  * all open files for the current process, named as the file descriptor.
579  * Use this if available, because it's more efficient than a shotgun
580  * approach to closing descriptors.
581  */
582 #if HAVE_LINUX_PROCFS
583  dir = opendir("/proc/self/fd");
584  if (dir == NULL) {
585  dir = opendir("/dev/fd");
586  }
587 #else
588  dir = opendir("/dev/fd");
589 #endif // HAVE_LINUX_PROCFS
590  if (dir != NULL) {
591  struct dirent *entry;
592  int dir_fd = dirfd(dir);
593 
594  while ((entry = readdir(dir)) != NULL) {
595  int lpc = atoi(entry->d_name);
596 
597  /* How could one of these entries be higher than max_fd, you ask?
598  * It isn't possible in normal operation, but when run under
599  * valgrind, valgrind can open high-numbered file descriptors for
600  * its own use that are higher than the process's soft limit.
601  * These will show up in the fd directory but aren't closable.
602  */
603  if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
604  close(lpc);
605  }
606  }
607  closedir(dir);
608  return;
609  }
610 
611  /* If no fd directory is available, iterate over all possible descriptors.
612  * This is less efficient due to the overhead of many system calls.
613  */
614  for (int lpc = max_fd; lpc >= min_fd; lpc--) {
615  close(lpc);
616  }
617 }
618 
627 char *
628 pcmk__full_path(const char *filename, const char *dirname)
629 {
630  char *path = NULL;
631 
632  CRM_ASSERT(filename != NULL);
633 
634  if (filename[0] == '/') {
635  path = strdup(filename);
636  CRM_ASSERT(path != NULL);
637 
638  } else {
639  CRM_ASSERT(dirname != NULL);
640  path = crm_strdup_printf("%s/%s", dirname, filename);
641  }
642 
643  return path;
644 }
645 
646 // Deprecated functions kept only for backward API compatibility
647 // LCOV_EXCL_START
648 
649 #include <crm/common/util_compat.h>
650 
651 void
652 crm_build_path(const char *path_c, mode_t mode)
653 {
654  int rc = pcmk__build_path(path_c, mode);
655 
656  if (rc != pcmk_rc_ok) {
657  crm_err("Could not create directory '%s': %s",
658  path_c, pcmk_rc_str(rc));
659  }
660 }
661 
662 // LCOV_EXCL_STOP
663 // End deprecated API
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:235
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:379
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
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:488
char * strerror(int errnum)
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:328
const char * pcmk__get_tmpdir(void)
Definition: io.c:541
#define CRM_DAEMON_GROUP
Definition: config.h:27
#define crm_debug(fmt, args...)
Definition: logging.h:382
Utility functions.
void crm_build_path(const char *path_c, mode_t mode)
Definition: io.c:652
#define crm_trace(fmt, args...)
Definition: logging.h:383
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:29
#define CRM_XS
Definition: logging.h:55
#define ENODATA
Definition: portability.h:145
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:319
const char * path
Definition: cib.c:26
#define crm_err(fmt, args...)
Definition: logging.h:377
#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:628
void pcmk__close_fds_in_child(bool all)
Definition: io.c:559
void pcmk__sync_directory(const char *name)
Definition: io.c:396
int pcmk__write_sync(int fd, const char *contents)
Definition: io.c:488
int pcmk__set_nonblocking(int fd)
Definition: io.c:518
int pcmk__file_contents(const char *filename, char **contents)
Definition: io.c:432