pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
io.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2020 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 
80 void
81 crm_build_path(const char *path_c, mode_t mode)
82 {
83  int rc = pcmk__build_path(path_c, mode);
84 
85  if (rc != pcmk_rc_ok) {
86  crm_err("Could not create directory '%s': %s",
87  path_c, pcmk_rc_str(rc));
88  }
89 }
90 
103 int
104 pcmk__real_path(const char *path, char **resolved_path)
105 {
106  CRM_CHECK((path != NULL) && (resolved_path != NULL), return EINVAL);
107 
108 #if _POSIX_VERSION >= 200809L
109  /* Recent C libraries can dynamically allocate memory as needed */
110  *resolved_path = realpath(path, NULL);
111  return (*resolved_path == NULL)? errno : pcmk_rc_ok;
112 
113 #elif defined(PATH_MAX)
114  /* Older implementations require pre-allocated memory */
115  /* (this is less desirable because PATH_MAX may be huge or not defined) */
116  *resolved_path = malloc(PATH_MAX);
117  if ((*resolved_path == NULL) || (realpath(path, *resolved_path) == NULL)) {
118  return errno;
119  }
120  return pcmk_rc_ok;
121 #else
122  *resolved_path = NULL;
123  return ENOTSUP;
124 #endif
125 }
126 
139 char *
140 pcmk__series_filename(const char *directory, const char *series, int sequence,
141  bool bzip)
142 {
143  CRM_ASSERT((directory != NULL) && (series != NULL));
144  return crm_strdup_printf("%s/%s-%d.%s", directory, series, sequence,
145  (bzip? "bz2" : "raw"));
146 }
147 
158 int
159 pcmk__read_series_sequence(const char *directory, const char *series,
160  unsigned int *seq)
161 {
162  int rc;
163  FILE *fp = NULL;
164  char *series_file = NULL;
165 
166  if ((directory == NULL) || (series == NULL) || (seq == NULL)) {
167  return EINVAL;
168  }
169 
170  series_file = crm_strdup_printf("%s/%s.last", directory, series);
171  fp = fopen(series_file, "r");
172  if (fp == NULL) {
173  rc = errno;
174  crm_debug("Could not open series file %s: %s",
175  series_file, strerror(rc));
176  free(series_file);
177  return rc;
178  }
179  errno = 0;
180  if (fscanf(fp, "%u", seq) != 1) {
181  rc = (errno == 0)? pcmk_rc_unknown_format : errno;
182  crm_debug("Could not read sequence number from series file %s: %s",
183  series_file, pcmk_rc_str(rc));
184  fclose(fp);
185  return rc;
186  }
187  fclose(fp);
188  crm_trace("Found last sequence number %u in series file %s",
189  *seq, series_file);
190  free(series_file);
191  return pcmk_rc_ok;
192 }
193 
205 void
206 pcmk__write_series_sequence(const char *directory, const char *series,
207  unsigned int sequence, int max)
208 {
209  int rc = 0;
210  FILE *file_strm = NULL;
211  char *series_file = NULL;
212 
213  CRM_CHECK(directory != NULL, return);
214  CRM_CHECK(series != NULL, return);
215 
216  if (max == 0) {
217  return;
218  }
219  if (max > 0 && sequence >= max) {
220  sequence = 0;
221  }
222 
223  series_file = crm_strdup_printf("%s/%s.last", directory, series);
224  file_strm = fopen(series_file, "w");
225  if (file_strm != NULL) {
226  rc = fprintf(file_strm, "%u", sequence);
227  if (rc < 0) {
228  crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
229  }
230 
231  } else {
232  crm_err("Cannot open series file %s for writing", series_file);
233  }
234 
235  if (file_strm != NULL) {
236  fflush(file_strm);
237  fclose(file_strm);
238  }
239 
240  crm_trace("Wrote %d to %s", sequence, series_file);
241  free(series_file);
242 }
243 
255 int
256 pcmk__chown_series_sequence(const char *directory, const char *series,
257  uid_t uid, gid_t gid)
258 {
259  char *series_file = NULL;
260  int rc = pcmk_rc_ok;
261 
262  if ((directory == NULL) || (series == NULL)) {
263  return EINVAL;
264  }
265  series_file = crm_strdup_printf("%s/%s.last", directory, series);
266  if (chown(series_file, uid, gid) < 0) {
267  rc = errno;
268  }
269  free(series_file);
270  return rc;
271 }
272 
273 static bool
274 pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
275 {
276  struct passwd *sys_user = NULL;
277 
278  errno = 0;
279  sys_user = getpwnam(CRM_DAEMON_USER);
280  if (sys_user == NULL) {
281  crm_notice("Could not find user %s: %s",
283  return FALSE;
284  }
285  if (target_stat->st_uid != sys_user->pw_uid) {
286  crm_notice("%s is not owned by user %s " CRM_XS " uid %d != %d",
287  target_name, CRM_DAEMON_USER, sys_user->pw_uid,
288  target_stat->st_uid);
289  return FALSE;
290  }
291  if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
292  crm_notice("%s is not readable and writable by user %s "
293  CRM_XS " st_mode=0%lo",
294  target_name, CRM_DAEMON_USER,
295  (unsigned long) target_stat->st_mode);
296  return FALSE;
297  }
298  return TRUE;
299 }
300 
301 static bool
302 pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
303 {
304  struct group *sys_grp = NULL;
305 
306  errno = 0;
307  sys_grp = getgrnam(CRM_DAEMON_GROUP);
308  if (sys_grp == NULL) {
309  crm_notice("Could not find group %s: %s",
311  return FALSE;
312  }
313 
314  if (target_stat->st_gid != sys_grp->gr_gid) {
315  crm_notice("%s is not owned by group %s " CRM_XS " uid %d != %d",
316  target_name, CRM_DAEMON_GROUP,
317  sys_grp->gr_gid, target_stat->st_gid);
318  return FALSE;
319  }
320 
321  if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
322  crm_notice("%s is not readable and writable by group %s "
323  CRM_XS " st_mode=0%lo",
324  target_name, CRM_DAEMON_GROUP,
325  (unsigned long) target_stat->st_mode);
326  return FALSE;
327  }
328  return TRUE;
329 }
330 
345 bool
346 pcmk__daemon_can_write(const char *dir, const char *file)
347 {
348  int s_res = 0;
349  struct stat buf;
350  char *full_file = NULL;
351  const char *target = NULL;
352 
353  // Caller must supply directory
354  CRM_ASSERT(dir != NULL);
355 
356  // If file is given, check whether it exists as a regular file
357  if (file != NULL) {
358  full_file = crm_strdup_printf("%s/%s", dir, file);
359  target = full_file;
360 
361  s_res = stat(full_file, &buf);
362  if (s_res < 0) {
363  crm_notice("%s not found: %s", target, pcmk_strerror(errno));
364  free(full_file);
365  full_file = NULL;
366  target = NULL;
367 
368  } else if (S_ISREG(buf.st_mode) == FALSE) {
369  crm_err("%s must be a regular file " CRM_XS " st_mode=0%lo",
370  target, (unsigned long) buf.st_mode);
371  free(full_file);
372  return false;
373  }
374  }
375 
376  // If file is not given, ensure dir exists as directory
377  if (target == NULL) {
378  target = dir;
379  s_res = stat(dir, &buf);
380  if (s_res < 0) {
381  crm_err("%s not found: %s", dir, pcmk_strerror(errno));
382  return false;
383 
384  } else if (S_ISDIR(buf.st_mode) == FALSE) {
385  crm_err("%s must be a directory " CRM_XS " st_mode=0%lo",
386  dir, (unsigned long) buf.st_mode);
387  return false;
388  }
389  }
390 
391  if (!pcmk__daemon_user_can_write(target, &buf)
392  && !pcmk__daemon_group_can_write(target, &buf)) {
393 
394  crm_err("%s must be owned and writable by either user %s or group %s "
395  CRM_XS " st_mode=0%lo",
397  (unsigned long) buf.st_mode);
398  free(full_file);
399  return false;
400  }
401 
402  free(full_file);
403  return true;
404 }
405 
413 void
415 {
416  int fd;
417  DIR *directory;
418 
419  directory = opendir(name);
420  if (directory == NULL) {
421  crm_perror(LOG_ERR, "Could not open %s for syncing", name);
422  return;
423  }
424 
425  fd = dirfd(directory);
426  if (fd < 0) {
427  crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
428  return;
429  }
430 
431  if (fsync(fd) < 0) {
432  crm_perror(LOG_ERR, "Could not sync %s", name);
433  }
434  if (closedir(directory) < 0) {
435  crm_perror(LOG_ERR, "Could not close %s after fsync", name);
436  }
437 }
438 
449 int
450 pcmk__file_contents(const char *filename, char **contents)
451 {
452  FILE *fp;
453  int length, read_len;
454  int rc = pcmk_rc_ok;
455 
456  if ((filename == NULL) || (contents == NULL)) {
457  return EINVAL;
458  }
459 
460  fp = fopen(filename, "r");
461  if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
462  rc = errno;
463  goto bail;
464  }
465 
466  length = ftell(fp);
467  if (length < 0) {
468  rc = errno;
469  goto bail;
470  }
471 
472  if (length == 0) {
473  *contents = NULL;
474  } else {
475  *contents = calloc(length + 1, sizeof(char));
476  if (*contents == NULL) {
477  rc = errno;
478  goto bail;
479  }
480  rewind(fp);
481  read_len = fread(*contents, 1, length, fp); /* Coverity: False positive */
482  if (read_len != length) {
483  free(*contents);
484  *contents = NULL;
485  rc = EIO;
486  }
487  }
488 
489 bail:
490  if (fp != NULL) {
491  fclose(fp);
492  }
493  return rc;
494 }
495 
505 int
506 pcmk__write_sync(int fd, const char *contents)
507 {
508  int rc = 0;
509  FILE *fp = fdopen(fd, "w");
510 
511  if (fp == NULL) {
512  return errno;
513  }
514  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
515  rc = EIO;
516  }
517  if (fflush(fp) != 0) {
518  rc = errno;
519  }
520  if (fsync(fileno(fp)) < 0) {
521  rc = errno;
522  }
523  fclose(fp);
524  return rc;
525 }
526 
535 int
537 {
538  int flag = fcntl(fd, F_GETFL);
539 
540  if (flag < 0) {
541  return errno;
542  }
543  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
544  return errno;
545  }
546  return pcmk_rc_ok;
547 }
548 
558 const char *
560 {
561  const char *dir = getenv("TMPDIR");
562 
563  return (dir && (*dir == '/'))? dir : "/tmp";
564 }
565 
576 void
578 {
579  DIR *dir;
580  struct rlimit rlim;
581  rlim_t max_fd;
582  int min_fd = (all? 0 : (STDERR_FILENO + 1));
583 
584  /* Find the current process's (soft) limit for open files. getrlimit()
585  * should always work, but have a fallback just in case.
586  */
587  if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
588  max_fd = rlim.rlim_cur - 1;
589  } else {
590  long conf_max = sysconf(_SC_OPEN_MAX);
591 
592  max_fd = (conf_max > 0)? conf_max : 1024;
593  }
594 
595  /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
596  * all open files for the current process, named as the file descriptor.
597  * Use this if available, because it's more efficient than a shotgun
598  * approach to closing descriptors.
599  */
600 #if SUPPORT_PROCFS
601  dir = opendir("/proc/self/fd");
602  if (dir == NULL) {
603  dir = opendir("/dev/fd");
604  }
605 #else
606  dir = opendir("/dev/fd");
607 #endif
608  if (dir != NULL) {
609  struct dirent *entry;
610  int dir_fd = dirfd(dir);
611 
612  while ((entry = readdir(dir)) != NULL) {
613  int lpc = atoi(entry->d_name);
614 
615  /* How could one of these entries be higher than max_fd, you ask?
616  * It isn't possible in normal operation, but when run under
617  * valgrind, valgrind can open high-numbered file descriptors for
618  * its own use that are higher than the process's soft limit.
619  * These will show up in the fd directory but aren't closable.
620  */
621  if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
622  close(lpc);
623  }
624  }
625  closedir(dir);
626  return;
627  }
628 
629  /* If no fd directory is available, iterate over all possible descriptors.
630  * This is less efficient due to the overhead of many system calls.
631  */
632  for (int lpc = max_fd; lpc >= min_fd; lpc--) {
633  close(lpc);
634  }
635 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:365
const char * pcmk_strerror(int rc)
Definition: results.c:55
void crm_build_path(const char *path_c, mode_t mode)
Create a directory, including any parent directories needed.
Definition: io.c:81
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition: io.c:159
int pcmk__write_sync(int fd, const char *contents)
Definition: io.c:506
void pcmk__close_fds_in_child(bool)
Definition: io.c:577
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:413
char * strerror(int errnum)
char * pcmk__series_filename(const char *directory, const char *series, int sequence, bool bzip)
Definition: io.c:140
#define CRM_DAEMON_GROUP
Definition: config.h:29
int rc
Definition: pcmk_fence.c:34
#define crm_debug(fmt, args...)
Definition: logging.h:368
Utility functions.
int pcmk__real_path(const char *path, char **resolved_path)
Definition: io.c:104
#define crm_trace(fmt, args...)
Definition: logging.h:369
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:346
int pcmk__file_contents(const char *filename, char **contents)
Definition: io.c:450
#define CRM_DAEMON_USER
Definition: config.h:32
void pcmk__write_series_sequence(const char *directory, const char *series, unsigned int sequence, int max)
Definition: io.c:206
const char * target
Definition: pcmk_fence.c:28
#define CRM_XS
Definition: logging.h:54
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:314
#define crm_err(fmt, args...)
Definition: logging.h:363
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * pcmk__get_tmpdir(void)
Definition: io.c:559
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:256
void pcmk__sync_directory(const char *name)
Definition: io.c:414
char * name
Definition: pcmk_fence.c:30
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:45
int pcmk__set_nonblocking(int fd)
Definition: io.c:536