pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2025 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#include <sys/param.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <sys/resource.h>
16
17#include <stdio.h>
18#include <unistd.h>
19#include <string.h>
20#include <stdlib.h>
21#include <fcntl.h>
22#include <dirent.h>
23#include <errno.h>
24#include <limits.h>
25#include <pwd.h>
26#include <grp.h>
27
28#include <crm/crm.h>
29#include <crm/common/util.h>
30
40int
41pcmk__build_path(const char *path_c, mode_t mode)
42{
43 int offset = 1, len = 0;
44 int rc = pcmk_rc_ok;
45 char *path = strdup(path_c);
46
47 CRM_CHECK(path != NULL, return -ENOMEM);
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 rc = errno;
53 goto done;
54 }
55 path[offset] = '/';
56 }
57 }
58 if ((mkdir(path, mode) < 0) && (errno != EEXIST)) {
59 rc = errno;
60 }
61done:
62 free(path);
63 return rc;
64}
65
78int
79pcmk__real_path(const char *path, char **resolved_path)
80{
81 CRM_CHECK((path != NULL) && (resolved_path != NULL), return EINVAL);
82
83#if _POSIX_VERSION >= 200809L
84 /* Recent C libraries can dynamically allocate memory as needed */
85 *resolved_path = realpath(path, NULL);
86 return (*resolved_path == NULL)? errno : pcmk_rc_ok;
87
88#elif defined(PATH_MAX)
89 /* Older implementations require pre-allocated memory */
90 /* (this is less desirable because PATH_MAX may be huge or not defined) */
91 *resolved_path = malloc(PATH_MAX);
92 if ((*resolved_path == NULL) || (realpath(path, *resolved_path) == NULL)) {
93 return errno;
94 }
95 return pcmk_rc_ok;
96#else
97 *resolved_path = NULL;
98 return ENOTSUP;
99#endif
100}
101
114char *
115pcmk__series_filename(const char *directory, const char *series,
116 unsigned int sequence, bool bzip)
117{
118 pcmk__assert((directory != NULL) && (series != NULL));
119 return crm_strdup_printf("%s/%s-%u.%s", directory, series, sequence,
120 (bzip? "bz2" : "raw"));
121}
122
133int
134pcmk__read_series_sequence(const char *directory, const char *series,
135 unsigned int *seq)
136{
137 int rc;
138 FILE *fp = NULL;
139 char *series_file = NULL;
140
141 if ((directory == NULL) || (series == NULL) || (seq == NULL)) {
142 return EINVAL;
143 }
144
145 series_file = crm_strdup_printf("%s/%s.last", directory, series);
146 fp = fopen(series_file, "r");
147 if (fp == NULL) {
148 rc = errno;
149 crm_debug("Could not open series file %s: %s",
150 series_file, strerror(rc));
151 free(series_file);
152 return rc;
153 }
154 errno = 0;
155 if (fscanf(fp, "%u", seq) != 1) {
156 rc = (errno == 0)? ENODATA : errno;
157 crm_debug("Could not read sequence number from series file %s: %s",
158 series_file, pcmk_rc_str(rc));
159 fclose(fp);
160 return rc;
161 }
162 fclose(fp);
163 crm_trace("Found last sequence number %u in series file %s",
164 *seq, series_file);
165 free(series_file);
166 return pcmk_rc_ok;
167}
168
180void
181pcmk__write_series_sequence(const char *directory, const char *series,
182 unsigned int sequence, int max)
183{
184 int rc = 0;
185 FILE *file_strm = NULL;
186 char *series_file = NULL;
187
188 CRM_CHECK(directory != NULL, return);
189 CRM_CHECK(series != NULL, return);
190
191 if (max == 0) {
192 return;
193 }
194 if (max > 0 && sequence >= max) {
195 sequence = 0;
196 }
197
198 series_file = crm_strdup_printf("%s/%s.last", directory, series);
199 file_strm = fopen(series_file, "w");
200 if (file_strm != NULL) {
201 rc = fprintf(file_strm, "%u", sequence);
202 if (rc < 0) {
203 crm_perror(LOG_ERR, "Cannot write to series file %s", series_file);
204 }
205
206 } else {
207 crm_err("Cannot open series file %s for writing", series_file);
208 }
209
210 if (file_strm != NULL) {
211 fflush(file_strm);
212 fclose(file_strm);
213 }
214
215 crm_trace("Wrote %d to %s", sequence, series_file);
216 free(series_file);
217}
218
231int
232pcmk__chown_series_sequence(const char *directory, const char *series,
233 uid_t uid, gid_t gid)
234{
235 char *series_file = NULL;
236 int rc = pcmk_rc_ok;
237
238 if ((directory == NULL) || (series == NULL)) {
239 return EINVAL;
240 }
241 series_file = crm_strdup_printf("%s/%s.last", directory, series);
242 if (chown(series_file, uid, gid) < 0) {
243 rc = errno;
244 }
245 free(series_file);
246 return rc;
247}
248
249static bool
250pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
251{
252 struct passwd *sys_user = NULL;
253
254 errno = 0;
255 sys_user = getpwnam(CRM_DAEMON_USER);
256 if (sys_user == NULL) {
257 crm_notice("Could not find user %s: %s",
259 return FALSE;
260 }
261 if (target_stat->st_uid != sys_user->pw_uid) {
262 crm_notice("%s is not owned by user %s " QB_XS " uid %d != %d",
263 target_name, CRM_DAEMON_USER, sys_user->pw_uid,
264 target_stat->st_uid);
265 return FALSE;
266 }
267 if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
268 crm_notice("%s is not readable and writable by user %s "
269 QB_XS " st_mode=0%lo",
270 target_name, CRM_DAEMON_USER,
271 (unsigned long) target_stat->st_mode);
272 return FALSE;
273 }
274 return TRUE;
275}
276
277static bool
278pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
279{
280 struct group *sys_grp = NULL;
281
282 errno = 0;
283 sys_grp = getgrnam(CRM_DAEMON_GROUP);
284 if (sys_grp == NULL) {
285 crm_notice("Could not find group %s: %s",
287 return FALSE;
288 }
289
290 if (target_stat->st_gid != sys_grp->gr_gid) {
291 crm_notice("%s is not owned by group %s " QB_XS " uid %d != %d",
292 target_name, CRM_DAEMON_GROUP,
293 sys_grp->gr_gid, target_stat->st_gid);
294 return FALSE;
295 }
296
297 if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
298 crm_notice("%s is not readable and writable by group %s "
299 QB_XS " st_mode=0%lo",
300 target_name, CRM_DAEMON_GROUP,
301 (unsigned long) target_stat->st_mode);
302 return FALSE;
303 }
304 return TRUE;
305}
306
321bool
322pcmk__daemon_can_write(const char *dir, const char *file)
323{
324 int s_res = 0;
325 struct stat buf;
326 char *full_file = NULL;
327 const char *target = NULL;
328
329 // Caller must supply directory
330 pcmk__assert(dir != NULL);
331
332 // If file is given, check whether it exists as a regular file
333 if (file != NULL) {
334 full_file = crm_strdup_printf("%s/%s", dir, file);
335 target = full_file;
336
337 s_res = stat(full_file, &buf);
338 if (s_res < 0) {
339 crm_notice("%s not found: %s", target, pcmk_rc_str(errno));
340 free(full_file);
341 full_file = NULL;
342 target = NULL;
343
344 } else if (S_ISREG(buf.st_mode) == FALSE) {
345 crm_err("%s must be a regular file " QB_XS " st_mode=0%lo",
346 target, (unsigned long) buf.st_mode);
347 free(full_file);
348 return false;
349 }
350 }
351
352 // If file is not given, ensure dir exists as directory
353 if (target == NULL) {
354 target = dir;
355 s_res = stat(dir, &buf);
356 if (s_res < 0) {
357 crm_err("%s not found: %s", dir, pcmk_rc_str(errno));
358 return false;
359
360 } else if (S_ISDIR(buf.st_mode) == FALSE) {
361 crm_err("%s must be a directory " QB_XS " st_mode=0%lo",
362 dir, (unsigned long) buf.st_mode);
363 return false;
364 }
365 }
366
367 if (!pcmk__daemon_user_can_write(target, &buf)
368 && !pcmk__daemon_group_can_write(target, &buf)) {
369
370 crm_err("%s must be owned and writable by either user %s or group %s "
371 QB_XS " st_mode=0%lo",
373 (unsigned long) buf.st_mode);
374 free(full_file);
375 return false;
376 }
377
378 free(full_file);
379 return true;
380}
381
389void
391{
392 int fd;
393 DIR *directory;
394
395 directory = opendir(name);
396 if (directory == NULL) {
397 crm_perror(LOG_ERR, "Could not open %s for syncing", name);
398 return;
399 }
400
401 fd = dirfd(directory);
402 if (fd < 0) {
403 crm_perror(LOG_ERR, "Could not obtain file descriptor for %s", name);
404 return;
405 }
406
407 if (fsync(fd) < 0) {
408 crm_perror(LOG_ERR, "Could not sync %s", name);
409 }
410 if (closedir(directory) < 0) {
411 crm_perror(LOG_ERR, "Could not close %s after fsync", name);
412 }
413}
414
425int
426pcmk__file_contents(const char *filename, char **contents)
427{
428 FILE *fp;
429 int length, read_len;
430 int rc = pcmk_rc_ok;
431
432 if ((filename == NULL) || (contents == NULL)) {
433 return EINVAL;
434 }
435
436 fp = fopen(filename, "r");
437 if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
438 rc = errno;
439 goto bail;
440 }
441
442 length = ftell(fp);
443 if (length < 0) {
444 rc = errno;
445 goto bail;
446 }
447
448 if (length == 0) {
449 *contents = NULL;
450 } else {
451 *contents = calloc(length + 1, sizeof(char));
452 if (*contents == NULL) {
453 rc = errno;
454 goto bail;
455 }
456
457 errno = 0;
458
459 rewind(fp);
460 if (errno != 0) {
461 rc = errno;
462 goto bail;
463 }
464
465 read_len = fread(*contents, 1, length, fp);
466 if (read_len != length) {
467 free(*contents);
468 *contents = NULL;
469 rc = EIO;
470 } else {
471 /* Coverity thinks *contents isn't null-terminated. It doesn't
472 * understand calloc().
473 */
474 (*contents)[length] = '\0';
475 }
476 }
477
478bail:
479 if (fp != NULL) {
480 fclose(fp);
481 }
482 return rc;
483}
484
494int
495pcmk__write_sync(int fd, const char *contents)
496{
497 int rc = 0;
498 FILE *fp = fdopen(fd, "w");
499
500 if (fp == NULL) {
501 return errno;
502 }
503 if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
504 rc = EIO;
505 }
506 if (fflush(fp) != 0) {
507 rc = errno;
508 }
509 if (fsync(fileno(fp)) < 0) {
510 rc = errno;
511 }
512 fclose(fp);
513 return rc;
514}
515
524int
526{
527 int flag = fcntl(fd, F_GETFL);
528
529 if (flag < 0) {
530 return errno;
531 }
532 if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
533 return errno;
534 }
535 return pcmk_rc_ok;
536}
537
547const char *
549{
550 const char *dir = getenv("TMPDIR");
551
552 return (dir && (*dir == '/'))? dir : "/tmp";
553}
554
565void
567{
568 DIR *dir;
569 struct rlimit rlim;
570 rlim_t max_fd;
571 int min_fd = (all? 0 : (STDERR_FILENO + 1));
572
573 /* Find the current process's (soft) limit for open files. getrlimit()
574 * should always work, but have a fallback just in case.
575 */
576 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
577 max_fd = rlim.rlim_cur - 1;
578 } else {
579 long conf_max = sysconf(_SC_OPEN_MAX);
580
581 max_fd = (conf_max > 0)? conf_max : 1024;
582 }
583
584 /* First try /proc. If that returns NULL (either because opening the
585 * directory failed, or because procfs isn't supported on this platform),
586 * fall back to /dev/fd.
587 */
588 dir = pcmk__procfs_fd_dir();
589 if (dir == NULL) {
590 dir = opendir("/dev/fd");
591 }
592
593 if (dir != NULL) {
594 struct dirent *entry;
595 int dir_fd = dirfd(dir);
596
597 while ((entry = readdir(dir)) != NULL) {
598 int lpc = atoi(entry->d_name);
599
600 /* How could one of these entries be higher than max_fd, you ask?
601 * It isn't possible in normal operation, but when run under
602 * valgrind, valgrind can open high-numbered file descriptors for
603 * its own use that are higher than the process's soft limit.
604 * These will show up in the fd directory but aren't closable.
605 */
606 if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
607 close(lpc);
608 }
609 }
610 closedir(dir);
611 return;
612 }
613
614 /* If no fd directory is available, iterate over all possible descriptors.
615 * This is less efficient due to the overhead of many system calls.
616 */
617 for (int lpc = max_fd; lpc >= min_fd; lpc--) {
618 close(lpc);
619 }
620}
621
630char *
631pcmk__full_path(const char *filename, const char *dirname)
632{
633 pcmk__assert(filename != NULL);
634
635 if (filename[0] == '/') {
636 return pcmk__str_copy(filename);
637 }
638 pcmk__assert(dirname != NULL);
639 return crm_strdup_printf("%s/%s", dirname, filename);
640}
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
DIR * pcmk__procfs_fd_dir(void)
Definition procfs.c:259
Utility functions.
#define CRM_DAEMON_USER
Definition config.h:27
#define CRM_DAEMON_GROUP
Definition config.h:24
A dumping ground.
void pcmk__write_series_sequence(const char *directory, const char *series, unsigned int sequence, int max)
Definition io.c:181
int pcmk__real_path(const char *path, char **resolved_path)
Definition io.c:79
char * pcmk__series_filename(const char *directory, const char *series, unsigned int sequence, bool bzip)
Definition io.c:115
int pcmk__chown_series_sequence(const char *directory, const char *series, uid_t uid, gid_t gid)
Definition io.c:232
void pcmk__sync_directory(const char *name)
Definition io.c:390
char * pcmk__full_path(const char *filename, const char *dirname)
Duplicate a file path, inserting a prefix if not absolute.
Definition io.c:631
bool pcmk__daemon_can_write(const char *dir, const char *file)
Definition io.c:322
void pcmk__close_fds_in_child(bool all)
Definition io.c:566
int pcmk__file_contents(const char *filename, char **contents)
Definition io.c:426
int pcmk__read_series_sequence(const char *directory, const char *series, unsigned int *seq)
Definition io.c:134
const char * pcmk__get_tmpdir(void)
Definition io.c:548
int pcmk__write_sync(int fd, const char *contents)
Definition io.c:495
int pcmk__build_path(const char *path_c, mode_t mode)
Definition io.c:41
int pcmk__set_nonblocking(int fd)
Definition io.c:525
#define crm_notice(fmt, args...)
Definition logging.h:363
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:299
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_trace(fmt, args...)
Definition logging.h:370
const char * target
Definition pcmk_fence.c:31
#define ENODATA
Definition portability.h:61
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ pcmk_rc_ok
Definition results.h:159
#define pcmk__assert(expr)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk__str_copy(str)