pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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
44int
45pcmk__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 }
67done:
68 free(path);
69 return rc;
70}
71
84int
85pcmk__real_path(const char *path, char **resolved_path)
86{
87 CRM_CHECK((path != NULL) && (resolved_path != NULL), return EINVAL);
88
89#if _POSIX_VERSION >= 200809L
90 /* Recent C libraries can dynamically allocate memory as needed */
91 *resolved_path = realpath(path, NULL);
92 return (*resolved_path == NULL)? errno : pcmk_rc_ok;
93
94#elif defined(PATH_MAX)
95 /* Older implementations require pre-allocated memory */
96 /* (this is less desirable because PATH_MAX may be huge or not defined) */
97 *resolved_path = malloc(PATH_MAX);
98 if ((*resolved_path == NULL) || (realpath(path, *resolved_path) == NULL)) {
99 return errno;
100 }
101 return pcmk_rc_ok;
102#else
103 *resolved_path = NULL;
104 return ENOTSUP;
105#endif
106}
107
120char *
121pcmk__series_filename(const char *directory, const char *series, int sequence,
122 bool bzip)
123{
124 CRM_ASSERT((directory != NULL) && (series != NULL));
125 return crm_strdup_printf("%s/%s-%d.%s", directory, series, sequence,
126 (bzip? "bz2" : "raw"));
127}
128
139int
140pcmk__read_series_sequence(const char *directory, const char *series,
141 unsigned int *seq)
142{
143 int rc;
144 FILE *fp = NULL;
145 char *series_file = NULL;
146
147 if ((directory == NULL) || (series == NULL) || (seq == NULL)) {
148 return EINVAL;
149 }
150
151 series_file = crm_strdup_printf("%s/%s.last", directory, series);
152 fp = fopen(series_file, "r");
153 if (fp == NULL) {
154 rc = errno;
155 crm_debug("Could not open series file %s: %s",
156 series_file, strerror(rc));
157 free(series_file);
158 return rc;
159 }
160 errno = 0;
161 if (fscanf(fp, "%u", seq) != 1) {
162 rc = (errno == 0)? pcmk_rc_unknown_format : errno;
163 crm_debug("Could not read sequence number from series file %s: %s",
164 series_file, pcmk_rc_str(rc));
165 fclose(fp);
166 return rc;
167 }
168 fclose(fp);
169 crm_trace("Found last sequence number %u in series file %s",
170 *seq, series_file);
171 free(series_file);
172 return pcmk_rc_ok;
173}
174
186void
187pcmk__write_series_sequence(const char *directory, const char *series,
188 unsigned int sequence, int max)
189{
190 int rc = 0;
191 FILE *file_strm = NULL;
192 char *series_file = NULL;
193
194 CRM_CHECK(directory != NULL, return);
195 CRM_CHECK(series != NULL, return);
196
197 if (max == 0) {
198 return;
199 }
200 if (max > 0 && sequence >= max) {
201 sequence = 0;
202 }
203
204 series_file = crm_strdup_printf("%s/%s.last", directory, series);
205 file_strm = fopen(series_file, "w");
206 if (file_strm != NULL) {
207 rc = fprintf(file_strm, "%u", sequence);
208 if (rc < 0) {
209 crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
210 }
211
212 } else {
213 crm_err("Cannot open series file %s for writing", series_file);
214 }
215
216 if (file_strm != NULL) {
217 fflush(file_strm);
218 fclose(file_strm);
219 }
220
221 crm_trace("Wrote %d to %s", sequence, series_file);
222 free(series_file);
223}
224
236int
237pcmk__chown_series_sequence(const char *directory, const char *series,
238 uid_t uid, gid_t gid)
239{
240 char *series_file = NULL;
241 int rc = pcmk_rc_ok;
242
243 if ((directory == NULL) || (series == NULL)) {
244 return EINVAL;
245 }
246 series_file = crm_strdup_printf("%s/%s.last", directory, series);
247 if (chown(series_file, uid, gid) < 0) {
248 rc = errno;
249 }
250 free(series_file);
251 return rc;
252}
253
254static bool
255pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
256{
257 struct passwd *sys_user = NULL;
258
259 errno = 0;
260 sys_user = getpwnam(CRM_DAEMON_USER);
261 if (sys_user == NULL) {
262 crm_notice("Could not find user %s: %s",
264 return FALSE;
265 }
266 if (target_stat->st_uid != sys_user->pw_uid) {
267 crm_notice("%s is not owned by user %s " CRM_XS " uid %d != %d",
268 target_name, CRM_DAEMON_USER, sys_user->pw_uid,
269 target_stat->st_uid);
270 return FALSE;
271 }
272 if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
273 crm_notice("%s is not readable and writable by user %s "
274 CRM_XS " st_mode=0%lo",
275 target_name, CRM_DAEMON_USER,
276 (unsigned long) target_stat->st_mode);
277 return FALSE;
278 }
279 return TRUE;
280}
281
282static bool
283pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
284{
285 struct group *sys_grp = NULL;
286
287 errno = 0;
288 sys_grp = getgrnam(CRM_DAEMON_GROUP);
289 if (sys_grp == NULL) {
290 crm_notice("Could not find group %s: %s",
292 return FALSE;
293 }
294
295 if (target_stat->st_gid != sys_grp->gr_gid) {
296 crm_notice("%s is not owned by group %s " CRM_XS " uid %d != %d",
297 target_name, CRM_DAEMON_GROUP,
298 sys_grp->gr_gid, target_stat->st_gid);
299 return FALSE;
300 }
301
302 if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
303 crm_notice("%s is not readable and writable by group %s "
304 CRM_XS " st_mode=0%lo",
305 target_name, CRM_DAEMON_GROUP,
306 (unsigned long) target_stat->st_mode);
307 return FALSE;
308 }
309 return TRUE;
310}
311
326bool
327pcmk__daemon_can_write(const char *dir, const char *file)
328{
329 int s_res = 0;
330 struct stat buf;
331 char *full_file = NULL;
332 const char *target = NULL;
333
334 // Caller must supply directory
335 CRM_ASSERT(dir != NULL);
336
337 // If file is given, check whether it exists as a regular file
338 if (file != NULL) {
339 full_file = crm_strdup_printf("%s/%s", dir, file);
340 target = full_file;
341
342 s_res = stat(full_file, &buf);
343 if (s_res < 0) {
344 crm_notice("%s not found: %s", target, pcmk_strerror(errno));
345 free(full_file);
346 full_file = NULL;
347 target = NULL;
348
349 } else if (S_ISREG(buf.st_mode) == FALSE) {
350 crm_err("%s must be a regular file " CRM_XS " st_mode=0%lo",
351 target, (unsigned long) buf.st_mode);
352 free(full_file);
353 return false;
354 }
355 }
356
357 // If file is not given, ensure dir exists as directory
358 if (target == NULL) {
359 target = dir;
360 s_res = stat(dir, &buf);
361 if (s_res < 0) {
362 crm_err("%s not found: %s", dir, pcmk_strerror(errno));
363 return false;
364
365 } else if (S_ISDIR(buf.st_mode) == FALSE) {
366 crm_err("%s must be a directory " CRM_XS " st_mode=0%lo",
367 dir, (unsigned long) buf.st_mode);
368 return false;
369 }
370 }
371
372 if (!pcmk__daemon_user_can_write(target, &buf)
373 && !pcmk__daemon_group_can_write(target, &buf)) {
374
375 crm_err("%s must be owned and writable by either user %s or group %s "
376 CRM_XS " st_mode=0%lo",
378 (unsigned long) buf.st_mode);
379 free(full_file);
380 return false;
381 }
382
383 free(full_file);
384 return true;
385}
386
394void
396{
397 int fd;
398 DIR *directory;
399
400 directory = opendir(name);
401 if (directory == NULL) {
402 crm_perror(LOG_ERR, "Could not open %s for syncing", name);
403 return;
404 }
405
406 fd = dirfd(directory);
407 if (fd < 0) {
408 crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
409 return;
410 }
411
412 if (fsync(fd) < 0) {
413 crm_perror(LOG_ERR, "Could not sync %s", name);
414 }
415 if (closedir(directory) < 0) {
416 crm_perror(LOG_ERR, "Could not close %s after fsync", name);
417 }
418}
419
430int
431pcmk__file_contents(const char *filename, char **contents)
432{
433 FILE *fp;
434 int length, read_len;
435 int rc = pcmk_rc_ok;
436
437 if ((filename == NULL) || (contents == NULL)) {
438 return EINVAL;
439 }
440
441 fp = fopen(filename, "r");
442 if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
443 rc = errno;
444 goto bail;
445 }
446
447 length = ftell(fp);
448 if (length < 0) {
449 rc = errno;
450 goto bail;
451 }
452
453 if (length == 0) {
454 *contents = NULL;
455 } else {
456 *contents = calloc(length + 1, sizeof(char));
457 if (*contents == NULL) {
458 rc = errno;
459 goto bail;
460 }
461 rewind(fp);
462 read_len = fread(*contents, 1, length, fp); /* Coverity: False positive */
463 if (read_len != length) {
464 free(*contents);
465 *contents = NULL;
466 rc = EIO;
467 }
468 }
469
470bail:
471 if (fp != NULL) {
472 fclose(fp);
473 }
474 return rc;
475}
476
486int
487pcmk__write_sync(int fd, const char *contents)
488{
489 int rc = 0;
490 FILE *fp = fdopen(fd, "w");
491
492 if (fp == NULL) {
493 return errno;
494 }
495 if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
496 rc = EIO;
497 }
498 if (fflush(fp) != 0) {
499 rc = errno;
500 }
501 if (fsync(fileno(fp)) < 0) {
502 rc = errno;
503 }
504 fclose(fp);
505 return rc;
506}
507
516int
518{
519 int flag = fcntl(fd, F_GETFL);
520
521 if (flag < 0) {
522 return errno;
523 }
524 if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
525 return errno;
526 }
527 return pcmk_rc_ok;
528}
529
539const char *
541{
542 const char *dir = getenv("TMPDIR");
543
544 return (dir && (*dir == '/'))? dir : "/tmp";
545}
546
557void
559{
560 DIR *dir;
561 struct rlimit rlim;
562 rlim_t max_fd;
563 int min_fd = (all? 0 : (STDERR_FILENO + 1));
564
565 /* Find the current process's (soft) limit for open files. getrlimit()
566 * should always work, but have a fallback just in case.
567 */
568 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
569 max_fd = rlim.rlim_cur - 1;
570 } else {
571 long conf_max = sysconf(_SC_OPEN_MAX);
572
573 max_fd = (conf_max > 0)? conf_max : 1024;
574 }
575
576 /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
577 * all open files for the current process, named as the file descriptor.
578 * Use this if available, because it's more efficient than a shotgun
579 * approach to closing descriptors.
580 */
581#if SUPPORT_PROCFS
582 dir = opendir("/proc/self/fd");
583 if (dir == NULL) {
584 dir = opendir("/dev/fd");
585 }
586#else
587 dir = opendir("/dev/fd");
588#endif
589 if (dir != NULL) {
590 struct dirent *entry;
591 int dir_fd = dirfd(dir);
592
593 while ((entry = readdir(dir)) != NULL) {
594 int lpc = atoi(entry->d_name);
595
596 /* How could one of these entries be higher than max_fd, you ask?
597 * It isn't possible in normal operation, but when run under
598 * valgrind, valgrind can open high-numbered file descriptors for
599 * its own use that are higher than the process's soft limit.
600 * These will show up in the fd directory but aren't closable.
601 */
602 if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
603 close(lpc);
604 }
605 }
606 closedir(dir);
607 return;
608 }
609
610 /* If no fd directory is available, iterate over all possible descriptors.
611 * This is less efficient due to the overhead of many system calls.
612 */
613 for (int lpc = max_fd; lpc >= min_fd; lpc--) {
614 close(lpc);
615 }
616}
617
618// Deprecated functions kept only for backward API compatibility
619
621
622void
623crm_build_path(const char *path_c, mode_t mode)
624{
625 int rc = pcmk__build_path(path_c, mode);
626
627 if (rc != pcmk_rc_ok) {
628 crm_err("Could not create directory '%s': %s",
629 path_c, pcmk_rc_str(rc));
630 }
631}
632
633// End deprecated API
Utility functions.
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define CRM_DAEMON_USER
Definition config.h:32
#define CRM_DAEMON_GROUP
Definition config.h:29
A dumping ground.
void pcmk__write_series_sequence(const char *directory, const char *series, unsigned int sequence, int max)
Definition io.c:187
int pcmk__real_path(const char *path, char **resolved_path)
Definition io.c:85
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition io.c:237
void pcmk__sync_directory(const char *name)
Definition io.c:395
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition io.c:327
void pcmk__close_fds_in_child(bool all)
Definition io.c:558
int pcmk__file_contents(const char *filename, char **contents)
Definition io.c:431
void crm_build_path(const char *path_c, mode_t mode)
Definition io.c:623
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition io.c:140
int pcmk__write_sync(int fd, const char *contents)
Definition io.c:487
int pcmk__build_path(const char *path_c, mode_t mode)
Definition io.c:45
int pcmk__set_nonblocking(int fd)
Definition io.c:517
const char * pcmk__get_tmpdir()
Definition io.c:540
char * pcmk__series_filename(const char *directory, const char *series, int sequence, bool bzip)
Definition io.c:121
#define CRM_XS
Definition logging.h:54
#define crm_notice(fmt, args...)
Definition logging.h:352
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:301
#define CRM_CHECK(expr, failure_action)
Definition logging.h:218
#define crm_debug(fmt, args...)
Definition logging.h:355
#define crm_err(fmt, args...)
Definition logging.h:350
#define crm_trace(fmt, args...)
Definition logging.h:356
char * name
Definition pcmk_fence.c:31
int rc
Definition pcmk_fence.c:35
const char * target
Definition pcmk_fence.c:29
char * strerror(int errnum)
const char * pcmk_strerror(int rc)
Definition results.c:58
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:420
@ pcmk_rc_ok
Definition results.h:142
@ pcmk_rc_unknown_format
Definition results.h:136
Deprecated Pacemaker utilities.