pacemaker  2.0.3-4b1f869f0f
Scalable High-Availability cluster resource manager
io.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 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 <pwd.h>
28 #include <grp.h>
29 
30 #include <crm/crm.h>
31 #include <crm/common/util.h>
32 
41 void
42 crm_build_path(const char *path_c, mode_t mode)
43 {
44  int offset = 1, len = 0;
45  char *path = strdup(path_c);
46 
47  CRM_CHECK(path != NULL, return);
48  for (len = strlen(path); offset < len; offset++) {
49  if (path[offset] == '/') {
50  path[offset] = 0;
51  if (mkdir(path, mode) < 0 && errno != EEXIST) {
52  crm_perror(LOG_ERR, "Could not create directory '%s'", path);
53  break;
54  }
55  path[offset] = '/';
56  }
57  }
58  if (mkdir(path, mode) < 0 && errno != EEXIST) {
59  crm_perror(LOG_ERR, "Could not create directory '%s'", path);
60  }
61 
62  free(path);
63 }
64 
77 char *
78 generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
79 {
80  const char *ext = "raw";
81 
82  CRM_CHECK(directory != NULL, return NULL);
83  CRM_CHECK(series != NULL, return NULL);
84 
85  if (bzip) {
86  ext = "bz2";
87  }
88  return crm_strdup_printf("%s/%s-%d.%s", directory, series, sequence, ext);
89 }
90 
100 int
101 get_last_sequence(const char *directory, const char *series)
102 {
103  FILE *file_strm = NULL;
104  int start = 0, length = 0, read_len = 0;
105  char *series_file = NULL;
106  char *buffer = NULL;
107  int seq = 0;
108 
109  CRM_CHECK(directory != NULL, return 0);
110  CRM_CHECK(series != NULL, return 0);
111 
112  series_file = crm_strdup_printf("%s/%s.last", directory, series);
113  file_strm = fopen(series_file, "r");
114  if (file_strm == NULL) {
115  crm_debug("Series file %s does not exist", series_file);
116  free(series_file);
117  return 0;
118  }
119 
120  /* see how big the file is */
121  start = ftell(file_strm);
122  fseek(file_strm, 0L, SEEK_END);
123  length = ftell(file_strm);
124  fseek(file_strm, 0L, start);
125 
126  CRM_ASSERT(length >= 0);
127  CRM_ASSERT(start == ftell(file_strm));
128 
129  if (length <= 0) {
130  crm_info("%s was not valid", series_file);
131  free(buffer);
132  buffer = NULL;
133 
134  } else {
135  crm_trace("Reading %d bytes from file", length);
136  buffer = calloc(1, (length + 1));
137  read_len = fread(buffer, 1, length, file_strm);
138  if (read_len != length) {
139  crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len);
140  free(buffer);
141  buffer = NULL;
142  }
143  }
144 
145  seq = crm_parse_int(buffer, "0");
146  fclose(file_strm);
147 
148  crm_trace("Found %d in %s", seq, series_file);
149 
150  free(series_file);
151  free(buffer);
152  return seq;
153 }
154 
166 void
167 write_last_sequence(const char *directory, const char *series, int sequence, int max)
168 {
169  int rc = 0;
170  FILE *file_strm = NULL;
171  char *series_file = NULL;
172 
173  CRM_CHECK(directory != NULL, return);
174  CRM_CHECK(series != NULL, return);
175 
176  if (max == 0) {
177  return;
178  }
179  if (max > 0 && sequence >= max) {
180  sequence = 0;
181  }
182 
183  series_file = crm_strdup_printf("%s/%s.last", directory, series);
184  file_strm = fopen(series_file, "w");
185  if (file_strm != NULL) {
186  rc = fprintf(file_strm, "%d", sequence);
187  if (rc < 0) {
188  crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
189  }
190 
191  } else {
192  crm_err("Cannot open series file %s for writing", series_file);
193  }
194 
195  if (file_strm != NULL) {
196  fflush(file_strm);
197  fclose(file_strm);
198  }
199 
200  crm_trace("Wrote %d to %s", sequence, series_file);
201  free(series_file);
202 }
203 
215 int
216 crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
217 {
218  char *series_file = NULL;
219  int rc;
220 
221  CRM_CHECK((directory != NULL) && (series != NULL), errno = EINVAL; return -1);
222 
223  series_file = crm_strdup_printf("%s/%s.last", directory, series);
224  CRM_CHECK(series_file != NULL, return -1);
225 
226  rc = chown(series_file, uid, gid);
227  free(series_file);
228  return rc;
229 }
230 
231 static bool
232 pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
233 {
234  struct passwd *sys_user = NULL;
235 
236  errno = 0;
237  sys_user = getpwnam(CRM_DAEMON_USER);
238  if (sys_user == NULL) {
239  crm_notice("Could not find user %s: %s",
241  return FALSE;
242  }
243  if (target_stat->st_uid != sys_user->pw_uid) {
244  crm_notice("%s is not owned by user %s " CRM_XS " uid %d != %d",
245  target_name, CRM_DAEMON_USER, sys_user->pw_uid,
246  target_stat->st_uid);
247  return FALSE;
248  }
249  if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
250  crm_notice("%s is not readable and writable by user %s "
251  CRM_XS " st_mode=0%lo",
252  target_name, CRM_DAEMON_USER,
253  (unsigned long) target_stat->st_mode);
254  return FALSE;
255  }
256  return TRUE;
257 }
258 
259 static bool
260 pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
261 {
262  struct group *sys_grp = NULL;
263 
264  errno = 0;
265  sys_grp = getgrnam(CRM_DAEMON_GROUP);
266  if (sys_grp == NULL) {
267  crm_notice("Could not find group %s: %s",
269  return FALSE;
270  }
271 
272  if (target_stat->st_gid != sys_grp->gr_gid) {
273  crm_notice("%s is not owned by group %s " CRM_XS " uid %d != %d",
274  target_name, CRM_DAEMON_GROUP,
275  sys_grp->gr_gid, target_stat->st_gid);
276  return FALSE;
277  }
278 
279  if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
280  crm_notice("%s is not readable and writable by group %s "
281  CRM_XS " st_mode=0%lo",
282  target_name, CRM_DAEMON_GROUP,
283  (unsigned long) target_stat->st_mode);
284  return FALSE;
285  }
286  return TRUE;
287 }
288 
303 bool
304 pcmk__daemon_can_write(const char *dir, const char *file)
305 {
306  int s_res = 0;
307  struct stat buf;
308  char *full_file = NULL;
309  const char *target = NULL;
310 
311  // Caller must supply directory
312  CRM_ASSERT(dir != NULL);
313 
314  // If file is given, check whether it exists as a regular file
315  if (file != NULL) {
316  full_file = crm_concat(dir, file, '/');
317  target = full_file;
318 
319  s_res = stat(full_file, &buf);
320  if (s_res < 0) {
321  crm_notice("%s not found: %s", target, pcmk_strerror(errno));
322  free(full_file);
323  full_file = NULL;
324  target = NULL;
325 
326  } else if (S_ISREG(buf.st_mode) == FALSE) {
327  crm_err("%s must be a regular file " CRM_XS " st_mode=0%lo",
328  target, (unsigned long) buf.st_mode);
329  free(full_file);
330  return FALSE;
331  }
332  }
333 
334  // If file is not given, ensure dir exists as directory
335  if (target == NULL) {
336  target = dir;
337  s_res = stat(dir, &buf);
338  if (s_res < 0) {
339  crm_err("%s not found: %s", dir, pcmk_strerror(errno));
340  return FALSE;
341 
342  } else if (S_ISDIR(buf.st_mode) == FALSE) {
343  crm_err("%s must be a directory " CRM_XS " st_mode=0%lo",
344  dir, (unsigned long) buf.st_mode);
345  return FALSE;
346  }
347  }
348 
349  if (!pcmk__daemon_user_can_write(target, &buf)
350  && !pcmk__daemon_group_can_write(target, &buf)) {
351 
352  crm_err("%s must be owned and writable by either user %s or group %s "
353  CRM_XS " st_mode=0%lo",
355  (unsigned long) buf.st_mode);
356  free(full_file);
357  return FALSE;
358  }
359 
360  free(full_file);
361  return TRUE;
362 }
363 
371 void
372 crm_sync_directory(const char *name)
373 {
374  int fd;
375  DIR *directory;
376 
377  directory = opendir(name);
378  if (directory == NULL) {
379  crm_perror(LOG_ERR, "Could not open %s for syncing", name);
380  return;
381  }
382 
383  fd = dirfd(directory);
384  if (fd < 0) {
385  crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
386  return;
387  }
388 
389  if (fsync(fd) < 0) {
390  crm_perror(LOG_ERR, "Could not sync %s", name);
391  }
392  if (closedir(directory) < 0) {
393  crm_perror(LOG_ERR, "Could not close %s after fsync", name);
394  }
395 }
396 
408 char *
409 crm_read_contents(const char *filename)
410 {
411  char *contents = NULL;
412  FILE *fp;
413  int length, read_len;
414 
415  errno = 0; /* enable caller to distinguish error from empty file */
416 
417  fp = fopen(filename, "r");
418  if (fp == NULL) {
419  return NULL;
420  }
421 
422  fseek(fp, 0L, SEEK_END);
423  length = ftell(fp);
424 
425  if (length > 0) {
426  contents = calloc(length + 1, sizeof(char));
427  if (contents == NULL) {
428  fclose(fp);
429  return NULL;
430  }
431 
432  crm_trace("Reading %d bytes from %s", length, filename);
433  rewind(fp);
434  read_len = fread(contents, 1, length, fp); /* Coverity: False positive */
435  if (read_len != length) {
436  free(contents);
437  contents = NULL;
438  }
439  }
440 
441  fclose(fp);
442  return contents;
443 }
444 
454 int
455 crm_write_sync(int fd, const char *contents)
456 {
457  int rc = 0;
458  FILE *fp = fdopen(fd, "w");
459 
460  if (fp == NULL) {
461  return -1;
462  }
463  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
464  rc = -1;
465  }
466  if (fflush(fp) != 0) {
467  rc = -1;
468  }
469  if (fsync(fileno(fp)) < 0) {
470  rc = -1;
471  }
472  fclose(fp);
473  return rc;
474 }
475 
484 int
486 {
487  int flag = fcntl(fd, F_GETFL);
488 
489  if (flag < 0) {
490  return -errno;
491  }
492  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
493  return -errno;
494  }
495  return pcmk_ok;
496 }
497 
498 const char *
500 {
501  const char *dir = getenv("TMPDIR");
502 
503  return (dir && (*dir == '/'))? dir : "/tmp";
504 }
505 
516 void
518 {
519  DIR *dir;
520  struct rlimit rlim;
521  rlim_t max_fd;
522  int min_fd = (all? 0 : (STDERR_FILENO + 1));
523 
524  /* Find the current process's (soft) limit for open files. getrlimit()
525  * should always work, but have a fallback just in case.
526  */
527  if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
528  max_fd = rlim.rlim_cur - 1;
529  } else {
530  long conf_max = sysconf(_SC_OPEN_MAX);
531 
532  max_fd = (conf_max > 0)? conf_max : 1024;
533  }
534 
535  /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
536  * all open files for the current process, named as the file descriptor.
537  * Use this if available, because it's more efficient than a shotgun
538  * approach to closing descriptors.
539  */
540 #if SUPPORT_PROCFS
541  dir = opendir("/proc/self/fd");
542  if (dir == NULL) {
543  dir = opendir("/dev/fd");
544  }
545 #else
546  dir = opendir("/dev/fd");
547 #endif
548  if (dir != NULL) {
549  struct dirent *entry;
550  int dir_fd = dirfd(dir);
551 
552  while ((entry = readdir(dir)) != NULL) {
553  int lpc = atoi(entry->d_name);
554 
555  /* How could one of these entries be higher than max_fd, you ask?
556  * It isn't possible in normal operation, but when run under
557  * valgrind, valgrind can open high-numbered file descriptors for
558  * its own use that are higher than the process's soft limit.
559  * These will show up in the fd directory but aren't closable.
560  */
561  if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
562  close(lpc);
563  }
564  }
565  closedir(dir);
566  return;
567  }
568 
569  /* If no fd directory is available, iterate over all possible descriptors.
570  * This is less efficient due to the overhead of many system calls.
571  */
572  for (int lpc = max_fd; lpc >= min_fd; lpc--) {
573  close(lpc);
574  }
575 }
crm_set_nonblocking
int crm_set_nonblocking(int fd)
Definition: io.c:485
pcmk_strerror
const char * pcmk_strerror(int rc)
Definition: results.c:188
CRM_CHECK
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:157
crm_notice
#define crm_notice(fmt, args...)
Definition: logging.h:243
crm_err
#define crm_err(fmt, args...)
Definition: logging.h:241
crm_trace
#define crm_trace(fmt, args...)
Definition: logging.h:247
get_last_sequence
int get_last_sequence(const char *directory, const char *series)
Definition: io.c:101
crm_info
#define crm_info(fmt, args...)
Definition: logging.h:244
CRM_XS
#define CRM_XS
Definition: logging.h:34
crm_get_tmpdir
const char * crm_get_tmpdir()
Definition: io.c:499
crm_strdup_printf
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
crm_write_sync
int crm_write_sync(int fd, const char *contents)
Definition: io.c:455
crm_debug
#define crm_debug(fmt, args...)
Definition: logging.h:246
generate_series_filename
char * generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
Definition: io.c:78
pcmk__close_fds_in_child
void pcmk__close_fds_in_child(bool all)
Definition: io.c:517
crm_build_path
void crm_build_path(const char *path_c, mode_t mode)
Create a directory, including any parent directories needed.
Definition: io.c:42
CRM_DAEMON_GROUP
#define CRM_DAEMON_GROUP
Definition: config.h:29
write_last_sequence
void write_last_sequence(const char *directory, const char *series, int sequence, int max)
Definition: io.c:167
crm_parse_int
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:114
crm_perror
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:219
crm_read_contents
char * crm_read_contents(const char *filename)
Definition: io.c:409
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:42
CRM_DAEMON_USER
#define CRM_DAEMON_USER
Definition: config.h:32
crm_chown_last_sequence
int crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:216
crm_internal.h
util.h
Utility functions.
pcmk__daemon_can_write
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:304
crm.h
A dumping ground.
crm_sync_directory
void crm_sync_directory(const char *name)
Definition: io.c:372
pcmk_ok
#define pcmk_ok
Definition: results.h:57