pacemaker  1.1.19-c3c624ea3d
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 
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <fcntl.h>
23 #include <dirent.h>
24 #include <pwd.h>
25 #include <grp.h>
26 
27 #include <crm/crm.h>
28 #include <crm/common/util.h>
29 
38 void
39 crm_build_path(const char *path_c, mode_t mode)
40 {
41  int offset = 1, len = 0;
42  char *path = strdup(path_c);
43 
44  CRM_CHECK(path != NULL, return);
45  for (len = strlen(path); offset < len; offset++) {
46  if (path[offset] == '/') {
47  path[offset] = 0;
48  if (mkdir(path, mode) < 0 && errno != EEXIST) {
49  crm_perror(LOG_ERR, "Could not create directory '%s'", path);
50  break;
51  }
52  path[offset] = '/';
53  }
54  }
55  if (mkdir(path, mode) < 0 && errno != EEXIST) {
56  crm_perror(LOG_ERR, "Could not create directory '%s'", path);
57  }
58 
59  free(path);
60 }
61 
74 char *
75 generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
76 {
77  int len = 40;
78  char *filename = NULL;
79  const char *ext = "raw";
80 
81  CRM_CHECK(directory != NULL, return NULL);
82  CRM_CHECK(series != NULL, return NULL);
83 
84 #if !HAVE_BZLIB_H
85  bzip = FALSE;
86 #endif
87 
88  len += strlen(directory);
89  len += strlen(series);
90  filename = malloc(len);
91  CRM_CHECK(filename != NULL, return NULL);
92 
93  if (bzip) {
94  ext = "bz2";
95  }
96  sprintf(filename, "%s/%s-%d.%s", directory, series, sequence, ext);
97 
98  return filename;
99 }
100 
110 int
111 get_last_sequence(const char *directory, const char *series)
112 {
113  FILE *file_strm = NULL;
114  int start = 0, length = 0, read_len = 0;
115  char *series_file = NULL;
116  char *buffer = NULL;
117  int seq = 0;
118  int len = 36;
119 
120  CRM_CHECK(directory != NULL, return 0);
121  CRM_CHECK(series != NULL, return 0);
122 
123  len += strlen(directory);
124  len += strlen(series);
125  series_file = malloc(len);
126  CRM_CHECK(series_file != NULL, return 0);
127  sprintf(series_file, "%s/%s.last", directory, series);
128 
129  file_strm = fopen(series_file, "r");
130  if (file_strm == NULL) {
131  crm_debug("Series file %s does not exist", series_file);
132  free(series_file);
133  return 0;
134  }
135 
136  /* see how big the file is */
137  start = ftell(file_strm);
138  fseek(file_strm, 0L, SEEK_END);
139  length = ftell(file_strm);
140  fseek(file_strm, 0L, start);
141 
142  CRM_ASSERT(length >= 0);
143  CRM_ASSERT(start == ftell(file_strm));
144 
145  if (length <= 0) {
146  crm_info("%s was not valid", series_file);
147  free(buffer);
148  buffer = NULL;
149 
150  } else {
151  crm_trace("Reading %d bytes from file", length);
152  buffer = calloc(1, (length + 1));
153  read_len = fread(buffer, 1, length, file_strm);
154  if (read_len != length) {
155  crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len);
156  free(buffer);
157  buffer = NULL;
158  }
159  }
160 
161  seq = crm_parse_int(buffer, "0");
162  fclose(file_strm);
163 
164  crm_trace("Found %d in %s", seq, series_file);
165 
166  free(series_file);
167  free(buffer);
168  return seq;
169 }
170 
182 void
183 write_last_sequence(const char *directory, const char *series, int sequence, int max)
184 {
185  int rc = 0;
186  int len = 36;
187  FILE *file_strm = NULL;
188  char *series_file = NULL;
189 
190  CRM_CHECK(directory != NULL, return);
191  CRM_CHECK(series != NULL, return);
192 
193  if (max == 0) {
194  return;
195  }
196  if (max > 0 && sequence >= max) {
197  sequence = 0;
198  }
199 
200  len += strlen(directory);
201  len += strlen(series);
202  series_file = malloc(len);
203 
204  if (series_file) {
205  sprintf(series_file, "%s/%s.last", directory, series);
206  file_strm = fopen(series_file, "w");
207  }
208 
209  if (file_strm != NULL) {
210  rc = fprintf(file_strm, "%d", sequence);
211  if (rc < 0) {
212  crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
213  }
214 
215  } else {
216  crm_err("Cannot open series file %s for writing", series_file);
217  }
218 
219  if (file_strm != NULL) {
220  fflush(file_strm);
221  fclose(file_strm);
222  }
223 
224  crm_trace("Wrote %d to %s", sequence, series_file);
225  free(series_file);
226 }
227 
239 int
240 crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
241 {
242  char *series_file = NULL;
243  int rc;
244 
245  CRM_CHECK((directory != NULL) && (series != NULL), errno = EINVAL; return -1);
246 
247  series_file = crm_strdup_printf("%s/%s.last", directory, series);
248  CRM_CHECK(series_file != NULL, return -1);
249 
250  rc = chown(series_file, uid, gid);
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",
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",
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_concat(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_strerror(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_strerror(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%ol",
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
396 crm_sync_directory(const char *name)
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 
432 char *
433 crm_read_contents(const char *filename)
434 {
435  char *contents = NULL;
436  FILE *fp;
437  int length, read_len;
438 
439  errno = 0; /* enable caller to distinguish error from empty file */
440 
441  fp = fopen(filename, "r");
442  if (fp == NULL) {
443  return NULL;
444  }
445 
446  fseek(fp, 0L, SEEK_END);
447  length = ftell(fp);
448 
449  if (length > 0) {
450  contents = calloc(length + 1, sizeof(char));
451  if (contents == NULL) {
452  fclose(fp);
453  return NULL;
454  }
455 
456  crm_trace("Reading %d bytes from %s", length, filename);
457  rewind(fp);
458  read_len = fread(contents, 1, length, fp); /* Coverity: False positive */
459  if (read_len != length) {
460  free(contents);
461  contents = NULL;
462  }
463  }
464 
465  fclose(fp);
466  return contents;
467 }
468 
478 int
479 crm_write_sync(int fd, const char *contents)
480 {
481  int rc = 0;
482  FILE *fp = fdopen(fd, "w");
483 
484  if (fp == NULL) {
485  return -1;
486  }
487  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
488  rc = -1;
489  }
490  if (fflush(fp) != 0) {
491  rc = -1;
492  }
493  if (fsync(fileno(fp)) < 0) {
494  rc = -1;
495  }
496  fclose(fp);
497  return rc;
498 }
499 
508 int
510 {
511  int flag = fcntl(fd, F_GETFL);
512 
513  if (flag < 0) {
514  return -errno;
515  }
516  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
517  return -errno;
518  }
519  return pcmk_ok;
520 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:164
A dumping ground.
void write_last_sequence(const char *directory, const char *series, int sequence, int max)
Definition: io.c:183
#define crm_notice(fmt, args...)
Definition: logging.h:250
const char * pcmk_strerror(int rc)
Definition: logging.c:1139
#define pcmk_ok
Definition: error.h:42
int crm_parse_int(const char *text, const char *default_text)
Definition: strings.c:125
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:328
int crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:240
#define CRM_DAEMON_GROUP
Definition: config.h:44
#define crm_debug(fmt, args...)
Definition: logging.h:253
Utility functions.
void crm_build_path(const char *path_c, mode_t mode)
Create a directory, including any parent directories needed.
Definition: io.c:39
#define crm_trace(fmt, args...)
Definition: logging.h:254
int get_last_sequence(const char *directory, const char *series)
Definition: io.c:111
#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:479
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:226
#define crm_err(fmt, args...)
Definition: logging.h:248
#define CRM_ASSERT(expr)
Definition: error.h:35
void crm_sync_directory(const char *name)
Definition: io.c:396
char * crm_read_contents(const char *filename)
Definition: io.c:433
char * crm_concat(const char *prefix, const char *suffix, char join)
Definition: strings.c:32
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
int crm_set_nonblocking(int fd)
Definition: io.c:509
#define crm_info(fmt, args...)
Definition: logging.h:251
char * generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
Definition: io.c:75