pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
io.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 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
237int
238pcmk__chown_series_sequence(const char *directory, const char *series,
239 uid_t uid, gid_t gid)
240{
241 char *series_file = NULL;
242 int rc = pcmk_rc_ok;
243
244 if ((directory == NULL) || (series == NULL)) {
245 return EINVAL;
246 }
247 series_file = crm_strdup_printf("%s/%s.last", directory, series);
248 if (chown(series_file, uid, gid) < 0) {
249 rc = errno;
250 }
251 free(series_file);
252 return rc;
253}
254
255static bool
256pcmk__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
283static bool
284pcmk__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
327bool
328pcmk__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_strdup_printf("%s/%s", 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_rc_str(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_rc_str(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%lo",
379 (unsigned long) buf.st_mode);
380 free(full_file);
381 return false;
382 }
383
384 free(full_file);
385 return true;
386}
387
395void
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
431int
432pcmk__file_contents(const char *filename, char **contents)
433{
434 FILE *fp;
435 int length, read_len;
436 int rc = pcmk_rc_ok;
437
438 if ((filename == NULL) || (contents == NULL)) {
439 return EINVAL;
440 }
441
442 fp = fopen(filename, "r");
443 if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
444 rc = errno;
445 goto bail;
446 }
447
448 length = ftell(fp);
449 if (length < 0) {
450 rc = errno;
451 goto bail;
452 }
453
454 if (length == 0) {
455 *contents = NULL;
456 } else {
457 *contents = calloc(length + 1, sizeof(char));
458 if (*contents == NULL) {
459 rc = errno;
460 goto bail;
461 }
462 rewind(fp);
463 read_len = fread(*contents, 1, length, fp); /* Coverity: False positive */
464 if (read_len != length) {
465 free(*contents);
466 *contents = NULL;
467 rc = EIO;
468 }
469 }
470
471bail:
472 if (fp != NULL) {
473 fclose(fp);
474 }
475 return rc;
476}
477
487int
488pcmk__write_sync(int fd, const char *contents)
489{
490 int rc = 0;
491 FILE *fp = fdopen(fd, "w");
492
493 if (fp == NULL) {
494 return errno;
495 }
496 if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
497 rc = EIO;
498 }
499 if (fflush(fp) != 0) {
500 rc = errno;
501 }
502 if (fsync(fileno(fp)) < 0) {
503 rc = errno;
504 }
505 fclose(fp);
506 return rc;
507}
508
517int
519{
520 int flag = fcntl(fd, F_GETFL);
521
522 if (flag < 0) {
523 return errno;
524 }
525 if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
526 return errno;
527 }
528 return pcmk_rc_ok;
529}
530
540const char *
542{
543 const char *dir = getenv("TMPDIR");
544
545 return (dir && (*dir == '/'))? dir : "/tmp";
546}
547
558void
560{
561 DIR *dir;
562 struct rlimit rlim;
563 rlim_t max_fd;
564 int min_fd = (all? 0 : (STDERR_FILENO + 1));
565
566 /* Find the current process's (soft) limit for open files. getrlimit()
567 * should always work, but have a fallback just in case.
568 */
569 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
570 max_fd = rlim.rlim_cur - 1;
571 } else {
572 long conf_max = sysconf(_SC_OPEN_MAX);
573
574 max_fd = (conf_max > 0)? conf_max : 1024;
575 }
576
577 /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
578 * all open files for the current process, named as the file descriptor.
579 * Use this if available, because it's more efficient than a shotgun
580 * approach to closing descriptors.
581 */
582#if HAVE_LINUX_PROCFS
583 dir = opendir("/proc/self/fd");
584 if (dir == NULL) {
585 dir = opendir("/dev/fd");
586 }
587#else
588 dir = opendir("/dev/fd");
589#endif // HAVE_LINUX_PROCFS
590 if (dir != NULL) {
591 struct dirent *entry;
592 int dir_fd = dirfd(dir);
593
594 while ((entry = readdir(dir)) != NULL) {
595 int lpc = atoi(entry->d_name);
596
597 /* How could one of these entries be higher than max_fd, you ask?
598 * It isn't possible in normal operation, but when run under
599 * valgrind, valgrind can open high-numbered file descriptors for
600 * its own use that are higher than the process's soft limit.
601 * These will show up in the fd directory but aren't closable.
602 */
603 if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
604 close(lpc);
605 }
606 }
607 closedir(dir);
608 return;
609 }
610
611 /* If no fd directory is available, iterate over all possible descriptors.
612 * This is less efficient due to the overhead of many system calls.
613 */
614 for (int lpc = max_fd; lpc >= min_fd; lpc--) {
615 close(lpc);
616 }
617}
618
627char *
628pcmk__full_path(const char *filename, const char *dirname)
629{
630 char *path = NULL;
631
632 CRM_ASSERT(filename != NULL);
633
634 if (filename[0] == '/') {
635 path = strdup(filename);
636 CRM_ASSERT(path != NULL);
637
638 } else {
639 CRM_ASSERT(dirname != NULL);
640 path = crm_strdup_printf("%s/%s", dirname, filename);
641 }
642
643 return path;
644}
645
646// Deprecated functions kept only for backward API compatibility
647// LCOV_EXCL_START
648
650
651void
652crm_build_path(const char *path_c, mode_t mode)
653{
654 int rc = pcmk__build_path(path_c, mode);
655
656 if (rc != pcmk_rc_ok) {
657 crm_err("Could not create directory '%s': %s",
658 path_c, pcmk_rc_str(rc));
659 }
660}
661
662// LCOV_EXCL_STOP
663// End deprecated API
const char * path
Definition: cib.c:26
const char * name
Definition: cib.c:24
Utility functions.
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
Deprecated Pacemaker utilities.
#define CRM_DAEMON_USER
Definition: config.h:30
#define CRM_DAEMON_GROUP
Definition: config.h:27
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:238
void pcmk__sync_directory(const char *name)
Definition: io.c:396
char * pcmk__full_path(const char *filename, const char *dirname)
Duplicate a file path, inserting a prefix if not absolute.
Definition: io.c:628
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition: io.c:328
void pcmk__close_fds_in_child(bool all)
Definition: io.c:559
int pcmk__file_contents(const char *filename, char **contents)
Definition: io.c:432
void crm_build_path(const char *path_c, mode_t mode)
Definition: io.c:652
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition: io.c:140
const char * pcmk__get_tmpdir(void)
Definition: io.c:541
int pcmk__write_sync(int fd, const char *contents)
Definition: io.c:488
int pcmk__build_path(const char *path_c, mode_t mode)
Definition: io.c:45
int pcmk__set_nonblocking(int fd)
Definition: io.c:518
char * pcmk__series_filename(const char *directory, const char *series, int sequence, bool bzip)
Definition: io.c:121
#define CRM_XS
Definition: logging.h:55
#define crm_notice(fmt, args...)
Definition: logging.h:361
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:310
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
#define crm_trace(fmt, args...)
Definition: logging.h:365
const char * target
Definition: pcmk_fence.c:29
char * strerror(int errnum)
#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:476
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_unknown_format
Definition: results.h:142