pacemaker  2.0.2-debe490
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-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  const char *ext = "raw";
78 
79  CRM_CHECK(directory != NULL, return NULL);
80  CRM_CHECK(series != NULL, return NULL);
81 
82 #if !HAVE_BZLIB_H
83  bzip = FALSE;
84 #endif
85 
86  if (bzip) {
87  ext = "bz2";
88  }
89  return crm_strdup_printf("%s/%s-%d.%s", directory, series, sequence, ext);
90 }
91 
101 int
102 get_last_sequence(const char *directory, const char *series)
103 {
104  FILE *file_strm = NULL;
105  int start = 0, length = 0, read_len = 0;
106  char *series_file = NULL;
107  char *buffer = NULL;
108  int seq = 0;
109 
110  CRM_CHECK(directory != NULL, return 0);
111  CRM_CHECK(series != NULL, return 0);
112 
113  series_file = crm_strdup_printf("%s/%s.last", directory, series);
114  file_strm = fopen(series_file, "r");
115  if (file_strm == NULL) {
116  crm_debug("Series file %s does not exist", series_file);
117  free(series_file);
118  return 0;
119  }
120 
121  /* see how big the file is */
122  start = ftell(file_strm);
123  fseek(file_strm, 0L, SEEK_END);
124  length = ftell(file_strm);
125  fseek(file_strm, 0L, start);
126 
127  CRM_ASSERT(length >= 0);
128  CRM_ASSERT(start == ftell(file_strm));
129 
130  if (length <= 0) {
131  crm_info("%s was not valid", series_file);
132  free(buffer);
133  buffer = NULL;
134 
135  } else {
136  crm_trace("Reading %d bytes from file", length);
137  buffer = calloc(1, (length + 1));
138  read_len = fread(buffer, 1, length, file_strm);
139  if (read_len != length) {
140  crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len);
141  free(buffer);
142  buffer = NULL;
143  }
144  }
145 
146  seq = crm_parse_int(buffer, "0");
147  fclose(file_strm);
148 
149  crm_trace("Found %d in %s", seq, series_file);
150 
151  free(series_file);
152  free(buffer);
153  return seq;
154 }
155 
167 void
168 write_last_sequence(const char *directory, const char *series, int sequence, int max)
169 {
170  int rc = 0;
171  FILE *file_strm = NULL;
172  char *series_file = NULL;
173 
174  CRM_CHECK(directory != NULL, return);
175  CRM_CHECK(series != NULL, return);
176 
177  if (max == 0) {
178  return;
179  }
180  if (max > 0 && sequence >= max) {
181  sequence = 0;
182  }
183 
184  series_file = crm_strdup_printf("%s/%s.last", directory, series);
185  file_strm = fopen(series_file, "w");
186  if (file_strm != NULL) {
187  rc = fprintf(file_strm, "%d", sequence);
188  if (rc < 0) {
189  crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
190  }
191 
192  } else {
193  crm_err("Cannot open series file %s for writing", series_file);
194  }
195 
196  if (file_strm != NULL) {
197  fflush(file_strm);
198  fclose(file_strm);
199  }
200 
201  crm_trace("Wrote %d to %s", sequence, series_file);
202  free(series_file);
203 }
204 
216 int
217 crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
218 {
219  char *series_file = NULL;
220  int rc;
221 
222  CRM_CHECK((directory != NULL) && (series != NULL), errno = EINVAL; return -1);
223 
224  series_file = crm_strdup_printf("%s/%s.last", directory, series);
225  CRM_CHECK(series_file != NULL, return -1);
226 
227  rc = chown(series_file, uid, gid);
228  free(series_file);
229  return rc;
230 }
231 
232 static bool
233 pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
234 {
235  struct passwd *sys_user = NULL;
236 
237  errno = 0;
238  sys_user = getpwnam(CRM_DAEMON_USER);
239  if (sys_user == NULL) {
240  crm_notice("Could not find user %s: %s",
242  return FALSE;
243  }
244  if (target_stat->st_uid != sys_user->pw_uid) {
245  crm_notice("%s is not owned by user %s " CRM_XS " uid %d != %d",
246  target_name, CRM_DAEMON_USER, sys_user->pw_uid,
247  target_stat->st_uid);
248  return FALSE;
249  }
250  if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
251  crm_notice("%s is not readable and writable by user %s "
252  CRM_XS " st_mode=0%lo",
253  target_name, CRM_DAEMON_USER,
254  (unsigned long) target_stat->st_mode);
255  return FALSE;
256  }
257  return TRUE;
258 }
259 
260 static bool
261 pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
262 {
263  struct group *sys_grp = NULL;
264 
265  errno = 0;
266  sys_grp = getgrnam(CRM_DAEMON_GROUP);
267  if (sys_grp == NULL) {
268  crm_notice("Could not find group %s: %s",
270  return FALSE;
271  }
272 
273  if (target_stat->st_gid != sys_grp->gr_gid) {
274  crm_notice("%s is not owned by group %s " CRM_XS " uid %d != %d",
275  target_name, CRM_DAEMON_GROUP,
276  sys_grp->gr_gid, target_stat->st_gid);
277  return FALSE;
278  }
279 
280  if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
281  crm_notice("%s is not readable and writable by group %s "
282  CRM_XS " st_mode=0%lo",
283  target_name, CRM_DAEMON_GROUP,
284  (unsigned long) target_stat->st_mode);
285  return FALSE;
286  }
287  return TRUE;
288 }
289 
304 bool
305 pcmk__daemon_can_write(const char *dir, const char *file)
306 {
307  int s_res = 0;
308  struct stat buf;
309  char *full_file = NULL;
310  const char *target = NULL;
311 
312  // Caller must supply directory
313  CRM_ASSERT(dir != NULL);
314 
315  // If file is given, check whether it exists as a regular file
316  if (file != NULL) {
317  full_file = crm_concat(dir, file, '/');
318  target = full_file;
319 
320  s_res = stat(full_file, &buf);
321  if (s_res < 0) {
322  crm_notice("%s not found: %s", target, pcmk_strerror(errno));
323  free(full_file);
324  full_file = NULL;
325  target = NULL;
326 
327  } else if (S_ISREG(buf.st_mode) == FALSE) {
328  crm_err("%s must be a regular file " CRM_XS " st_mode=0%lo",
329  target, (unsigned long) buf.st_mode);
330  free(full_file);
331  return FALSE;
332  }
333  }
334 
335  // If file is not given, ensure dir exists as directory
336  if (target == NULL) {
337  target = dir;
338  s_res = stat(dir, &buf);
339  if (s_res < 0) {
340  crm_err("%s not found: %s", dir, pcmk_strerror(errno));
341  return FALSE;
342 
343  } else if (S_ISDIR(buf.st_mode) == FALSE) {
344  crm_err("%s must be a directory " CRM_XS " st_mode=0%lo",
345  dir, (unsigned long) buf.st_mode);
346  return FALSE;
347  }
348  }
349 
350  if (!pcmk__daemon_user_can_write(target, &buf)
351  && !pcmk__daemon_group_can_write(target, &buf)) {
352 
353  crm_err("%s must be owned and writable by either user %s or group %s "
354  CRM_XS " st_mode=0%lo",
356  (unsigned long) buf.st_mode);
357  free(full_file);
358  return FALSE;
359  }
360 
361  free(full_file);
362  return TRUE;
363 }
364 
372 void
373 crm_sync_directory(const char *name)
374 {
375  int fd;
376  DIR *directory;
377 
378  directory = opendir(name);
379  if (directory == NULL) {
380  crm_perror(LOG_ERR, "Could not open %s for syncing", name);
381  return;
382  }
383 
384  fd = dirfd(directory);
385  if (fd < 0) {
386  crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
387  return;
388  }
389 
390  if (fsync(fd) < 0) {
391  crm_perror(LOG_ERR, "Could not sync %s", name);
392  }
393  if (closedir(directory) < 0) {
394  crm_perror(LOG_ERR, "Could not close %s after fsync", name);
395  }
396 }
397 
409 char *
410 crm_read_contents(const char *filename)
411 {
412  char *contents = NULL;
413  FILE *fp;
414  int length, read_len;
415 
416  errno = 0; /* enable caller to distinguish error from empty file */
417 
418  fp = fopen(filename, "r");
419  if (fp == NULL) {
420  return NULL;
421  }
422 
423  fseek(fp, 0L, SEEK_END);
424  length = ftell(fp);
425 
426  if (length > 0) {
427  contents = calloc(length + 1, sizeof(char));
428  if (contents == NULL) {
429  fclose(fp);
430  return NULL;
431  }
432 
433  crm_trace("Reading %d bytes from %s", length, filename);
434  rewind(fp);
435  read_len = fread(contents, 1, length, fp); /* Coverity: False positive */
436  if (read_len != length) {
437  free(contents);
438  contents = NULL;
439  }
440  }
441 
442  fclose(fp);
443  return contents;
444 }
445 
455 int
456 crm_write_sync(int fd, const char *contents)
457 {
458  int rc = 0;
459  FILE *fp = fdopen(fd, "w");
460 
461  if (fp == NULL) {
462  return -1;
463  }
464  if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
465  rc = -1;
466  }
467  if (fflush(fp) != 0) {
468  rc = -1;
469  }
470  if (fsync(fileno(fp)) < 0) {
471  rc = -1;
472  }
473  fclose(fp);
474  return rc;
475 }
476 
485 int
487 {
488  int flag = fcntl(fd, F_GETFL);
489 
490  if (flag < 0) {
491  return -errno;
492  }
493  if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
494  return -errno;
495  }
496  return pcmk_ok;
497 }
498 
499 const char *
501 {
502  const char *dir = getenv("TMPDIR");
503 
504  return (dir && (*dir == '/'))? dir : "/tmp";
505 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:156
const char * crm_get_tmpdir(void)
Definition: io.c:500
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:242
const char * pcmk_strerror(int rc)
Definition: results.c:188
void crm_build_path(const char *path_c, mode_t mode)
Create a directory, including any parent directories needed.
Definition: io.c:39
int get_last_sequence(const char *directory, const char *series)
Definition: io.c:102
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:110
char * generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip)
Definition: io.c:75
char * crm_read_contents(const char *filename)
Definition: io.c:410
#define CRM_DAEMON_GROUP
Definition: config.h:29
#define crm_debug(fmt, args...)
Definition: logging.h:245
Utility functions.
#define crm_trace(fmt, args...)
Definition: logging.h:246
int crm_set_nonblocking(int fd)
Definition: io.c:486
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:305
#define CRM_DAEMON_USER
Definition: config.h:32
void write_last_sequence(const char *directory, const char *series, int sequence, int max)
Definition: io.c:168
#define CRM_XS
Definition: logging.h:34
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:218
#define crm_err(fmt, args...)
Definition: logging.h:240
#define CRM_ASSERT(expr)
Definition: results.h:42
int crm_write_sync(int fd, const char *contents)
Definition: io.c:456
int crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition: io.c:217
#define pcmk_ok
Definition: results.h:57
void crm_sync_directory(const char *name)
Definition: io.c:373
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define crm_info(fmt, args...)
Definition: logging.h:243