pacemaker  1.1.24-3850484742
Scalable High-Availability cluster resource manager
io.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This source code is licensed under the GNU Lesser General Public License
5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6  */
7 
8 #include <crm_internal.h>
9 
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13 
14 #include <sys/param.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/resource.h>
18 
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <dirent.h>
25 #include <pwd.h>
26 #include <grp.h>
27 
28 #include <crm/crm.h>
29 #include <crm/common/util.h>
30 
39 void
40 crm_build_path(const char *path_c, mode_t mode)
41 {
42  int offset = 1, len = 0;
43  char *path = strdup(path_c);
44 
45  CRM_CHECK(path != NULL, return);
46  for (len = strlen(path); offset < len; offset++) {
47  if (path[offset] == '/') {
48  path[offset] = 0;
49  if (mkdir(path, mode) < 0 && errno != EEXIST) {
50  crm_perror(LOG_ERR, "Could not create directory '%s'", path);
51  break;
52  }
53  path[offset] = '/';
54  }
55  }
56  if (mkdir(path, mode) < 0 && errno != EEXIST) {
57  crm_perror(LOG_ERR, "Could not create directory '%s'", path);
58  }
59 
60  free(path);
61 }
62 
75 char *
76 generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
77 {
78  int len = 40;
79  char *filename = NULL;
80  const char *ext = "raw";
81 
82  CRM_CHECK(directory != NULL, return NULL);
83  CRM_CHECK(series != NULL, return NULL);
84 
85 #if !HAVE_BZLIB_H
86  bzip = FALSE;
87 #endif
88 
89  len += strlen(directory);
90  len += strlen(series);
91  filename = malloc(len);
92  CRM_CHECK(filename != NULL, return NULL);
93 
94  if (bzip) {
95  ext = "bz2";
96  }
97  sprintf(filename, "%s/%s-%d.%s", directory, series, sequence, ext);
98 
99  return filename;
100 }
101 
111 int
112 get_last_sequence(const char *directory, const char *series)
113 {
114  FILE *file_strm = NULL;
115  int start = 0, length = 0, read_len = 0;
116  char *series_file = NULL;
117  char *buffer = NULL;
118  int seq = 0;
119  int len = 36;
120 
121  CRM_CHECK(directory != NULL, return 0);
122  CRM_CHECK(series != NULL, return 0);
123 
124  len += strlen(directory);
125  len += strlen(series);
126  series_file = malloc(len);
127  CRM_CHECK(series_file != NULL, return 0);
128  sprintf(series_file, "%s/%s.last", directory, series);
129 
130  file_strm = fopen(series_file, "r");
131  if (file_strm == NULL) {
132  crm_debug("Series file %s does not exist", series_file);
133  free(series_file);
134  return 0;
135  }
136 
137  /* see how big the file is */
138  start = ftell(file_strm);
139  fseek(file_strm, 0L, SEEK_END);
140  length = ftell(file_strm);
141  fseek(file_strm, 0L, start);
142 
143  CRM_ASSERT(length >= 0);
144  CRM_ASSERT(start == ftell(file_strm));
145 
146  if (length <= 0) {
147  crm_info("%s was not valid", series_file);
148  free(buffer);
149  buffer = NULL;
150 
151  } else {
152  crm_trace("Reading %d bytes from file", length);
153  buffer = calloc(1, (length + 1));
154  read_len = fread(buffer, 1, length, file_strm);
155  if (read_len != length) {
156  crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len);
157  free(buffer);
158  buffer = NULL;
159  }
160  }
161 
162  seq = crm_parse_int(buffer, "0");
163  fclose(file_strm);
164 
165  crm_trace("Found %d in %s", seq, series_file);
166 
167  free(series_file);
168  free(buffer);
169  return seq;
170 }
171 
183 void
184 write_last_sequence(const char *directory, const char *series, int sequence, int max)
185 {
186  int rc = 0;
187  int len = 36;
188  FILE *file_strm = NULL;
189  char *series_file = NULL;
190 
191  CRM_CHECK(directory != NULL, return);
192  CRM_CHECK(series != NULL, return);
193 
194  if (max == 0) {
195  return;
196  }
197  if (max > 0 && sequence >= max) {
198  sequence = 0;
199  }
200 
201  len += strlen(directory);
202  len += strlen(series);
203  series_file = malloc(len);
204 
205  if (series_file) {
206  sprintf(series_file, "%s/%s.last", directory, series);
207  file_strm = fopen(series_file, "w");
208  }
209 
210  if (file_strm != NULL) {
211  rc = fprintf(file_strm, "%d", sequence);
212  if (rc < 0) {
213  crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
214  }
215 
216  } else {
217  crm_err("Cannot open series file %s for writing", series_file);
218  }
219 
220  if (file_strm != NULL) {
221  fflush(file_strm);
222  fclose(file_strm);
223  }
224 
225  crm_trace("Wrote %d to %s", sequence, series_file);
226  free(series_file);
227 }
228 
240 int
241 crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
242 {
243  char *series_file = NULL;
244  int rc;
245 
246  CRM_CHECK((directory != NULL) && (series != NULL), errno = EINVAL; return -1);
247 
248  series_file = crm_strdup_printf("%s/%s.last", directory, series);
249  CRM_CHECK(series_file != NULL, return -1);
250 
251  rc = chown(series_file, uid, gid);
252  free(series_file);
253  return rc;
254 }
255 
256 static bool
257 pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
258 {
259  struct passwd *sys_user = NULL;
260 
261  errno = 0;
262  sys_user = getpwnam(CRM_DAEMON_USER);
263  if (sys_user == NULL) {
264  crm_notice("Could not find user %s: %s",
266  return FALSE;
267  }
268  if (target_stat->st_uid != sys_user->pw_uid) {
269  crm_notice("%s is not owned by user %s " CRM_XS " uid %d != %d",
270  target_name, CRM_DAEMON_USER, sys_user->pw_uid,
271  target_stat->st_uid);
272  return FALSE;
273  }
274  if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
275  crm_notice("%s is not readable and writable by user %s "
276  CRM_XS " st_mode=0%lo",
277  target_name, CRM_DAEMON_USER,
278  (unsigned long) target_stat->st_mode);
279  return FALSE;
280  }
281  return TRUE;
282 }
283 
284 static bool
285 pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
286 {
287  struct group *sys_grp = NULL;
288 
289  errno = 0;
290  sys_grp = getgrnam(CRM_DAEMON_GROUP);
291  if (sys_grp == NULL) {
292  crm_notice("Could not find group %s: %s",
294  return FALSE;
295  }
296 
297  if (target_stat->st_gid != sys_grp->gr_gid) {
298  crm_notice("%s is not owned by group %s " CRM_XS " uid %d != %d",
299  target_name, CRM_DAEMON_GROUP,
300  sys_grp->gr_gid, target_stat->st_gid);
301  return FALSE;
302  }
303 
304  if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
305  crm_notice("%s is not readable and writable by group %s "
306  CRM_XS " st_mode=0%lo",
307  target_name, CRM_DAEMON_GROUP,
308  (unsigned long) target_stat->st_mode);
309  return FALSE;
310  }
311  return TRUE;
312 }
313 
328 bool
329 pcmk__daemon_can_write(const char *dir, const char *file)
330 {
331  int s_res = 0;
332  struct stat buf;
333  char *full_file = NULL;
334  const char *target = NULL;
335 
336  // Caller must supply directory
337  CRM_ASSERT(dir != NULL);
338 
339  // If file is given, check whether it exists as a regular file
340  if (file != NULL) {
341  full_file = crm_concat(dir, file, '/');
342  target = full_file;
343 
344  s_res = stat(full_file, &buf);
345  if (s_res < 0) {
346  crm_notice("%s not found: %s", target, pcmk_strerror(errno));
347  free(full_file);
348  full_file = NULL;
349  target = NULL;
350 
351  } else if (S_ISREG(buf.st_mode) == FALSE) {
352  crm_err("%s must be a regular file " CRM_XS " st_mode=0%lo",
353  target, (unsigned long) buf.st_mode);
354  free(full_file);
355  return FALSE;
356  }
357  }
358 
359  // If file is not given, ensure dir exists as directory
360  if (target == NULL) {
361  target = dir;
362  s_res = stat(dir, &buf);
363  if (s_res < 0) {
364  crm_err("%s not found: %s", dir, pcmk_strerror(errno));
365  return FALSE;
366 
367  } else if (S_ISDIR(buf.st_mode) == FALSE) {
368  crm_err("%s must be a directory " CRM_XS " st_mode=0%lo",
369  dir, (unsigned long) buf.st_mode);
370  return FALSE;
371  }
372  }
373 
374  if (!pcmk__daemon_user_can_write(target, &buf)
375  && !pcmk__daemon_group_can_write(target, &buf)) {
376 
377  crm_err("%s must be owned and writable by either user %s or group %s "
378  CRM_XS " st_mode=0%ol",
380  (unsigned long) buf.st_mode);
381  free(full_file);
382  return FALSE;
383  }
384 
385  free(full_file);
386  return TRUE;
387 }
388 
396 void
397 crm_sync_directory(const char *name)
398 {
399  int fd;
400  DIR *directory;
401 
402  directory = opendir(name);
403  if (directory == NULL) {
404  crm_perror(LOG_ERR, "Could not open %s for syncing", name);
405  return;
406  }
407 
408  fd = dirfd(directory);
409  if (fd < 0) {
410  crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
411  return;
412  }
413 
414  if (fsync(fd) < 0) {
415  crm_perror(LOG_ERR, "Could not sync %s", name);
416  }
417  if (closedir(directory) < 0) {
418  crm_perror(LOG_ERR, "Could not close %s after fsync", name);
419  }
420 }
421 
433 char *
434 crm_read_contents(const char *filename)
435 {
436  char *contents = NULL;
437  FILE *fp;
438  int length, read_len;
439 
440  errno = 0; /* enable caller to distinguish error from empty file */
441 
442  fp = fopen(filename, "r");
443  if (fp == NULL) {
444  return NULL;
445  }
446 
447  fseek(fp, 0L, SEEK_END);
448  length = ftell(fp);
449 
450  if (length > 0) {
451  contents = calloc(length + 1, sizeof(char));
452  if (contents == NULL) {
453  fclose(fp);
454  return NULL;
455  }
456 
457  crm_trace("Reading %d bytes from %s", length, filename);
458  rewind(fp);
459  read_len = fread(contents, 1, length, fp); /* Coverity: False positive */
460  if (read_len != length) {
461  free(contents);
462  contents = NULL;
463  }
464  }
465 
466  fclose(fp);
467  return contents;
468 }
469 
479 int
480 crm_write_sync(int fd, const char *contents)
481 {
482  int rc = 0;
483  FILE *fp = fdopen(fd, "w");
484 
485  if (fp == NULL) {
486  return -1;
487  }
488  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
489  rc = -1;
490  }
491  if (fflush(fp) != 0) {
492  rc = -1;
493  }
494  if (fsync(fileno(fp)) < 0) {
495  rc = -1;
496  }
497  fclose(fp);
498  return rc;
499 }
500 
509 int
511 {
512  int flag = fcntl(fd, F_GETFL);
513 
514  if (flag < 0) {
515  return -errno;
516  }
517  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
518  return -errno;
519  }
520  return pcmk_ok;
521 }
522 
533 void
535 {
536  DIR *dir;
537  struct rlimit rlim;
538  rlim_t max_fd;
539  int min_fd = (all? 0 : (STDERR_FILENO + 1));
540 
541  /* Find the current process's (soft) limit for open files. getrlimit()
542  * should always work, but have a fallback just in case.
543  */
544  if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
545  max_fd = rlim.rlim_cur - 1;
546  } else {
547  long conf_max = sysconf(_SC_OPEN_MAX);
548 
549  max_fd = (conf_max > 0)? conf_max : 1024;
550  }
551 
552  /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
553  * all open files for the current process, named as the file descriptor.
554  * Use this if available, because it's more efficient than a shotgun
555  * approach to closing descriptors.
556  */
557 #if SUPPORT_PROCFS
558  dir = opendir("/proc/self/fd");
559  if (dir == NULL) {
560  dir = opendir("/dev/fd");
561  }
562 #else
563  dir = opendir("/dev/fd");
564 #endif
565  if (dir != NULL) {
566  struct dirent *entry;
567  int dir_fd = dirfd(dir);
568 
569  while ((entry = readdir(dir)) != NULL) {
570  int lpc = atoi(entry->d_name);
571 
572  /* How could one of these entries be higher than max_fd, you ask?
573  * It isn't possible in normal operation, but when run under
574  * valgrind, valgrind can open high-numbered file descriptors for
575  * its own use that are higher than the process's soft limit.
576  * These will show up in the fd directory but aren't closable.
577  */
578  if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
579  close(lpc);
580  }
581  }
582  closedir(dir);
583  return;
584  }
585 
586  /* If no fd directory is available, iterate over all possible descriptors.
587  * This is less efficient due to the overhead of many system calls.
588  */
589  for (int lpc = max_fd; lpc >= min_fd; lpc--) {
590  close(lpc);
591  }
592 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:190
A dumping ground.
void write_last_sequence(const char *directory, const char *series, int sequence, int max)
Definition: io.c:184
#define crm_notice(fmt, args...)
Definition: logging.h:276
const char * pcmk_strerror(int rc)
Definition: logging.c:1017
#define pcmk_ok
Definition: error.h:45
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:157
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:329
int crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:241
#define CRM_DAEMON_GROUP
Definition: config.h:44
#define crm_debug(fmt, args...)
Definition: logging.h:279
Utility functions.
void crm_build_path(const char *path_c, mode_t mode)
Create a directory, including any parent directories needed.
Definition: io.c:40
#define crm_trace(fmt, args...)
Definition: logging.h:280
int get_last_sequence(const char *directory, const char *series)
Definition: io.c:112
#define CRM_DAEMON_USER
Definition: config.h:47
#define CRM_XS
Definition: logging.h:42
int crm_write_sync(int fd, const char *contents)
Definition: io.c:480
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:252
#define crm_err(fmt, args...)
Definition: logging.h:274
#define CRM_ASSERT(expr)
Definition: error.h:20
void pcmk__close_fds_in_child(bool all)
Definition: io.c:534
void crm_sync_directory(const char *name)
Definition: io.c:397
char * crm_read_contents(const char *filename)
Definition: io.c:434
char * crm_concat(const char *prefix, const char *suffix, char join)
Definition: strings.c:33
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
int crm_set_nonblocking(int fd)
Definition: io.c:510
#define crm_info(fmt, args...)
Definition: logging.h:277
char * generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
Definition: io.c:76