1    	/*
2    	 * Copyright 2004-2026 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 <dirent.h>
13   	#include <errno.h>
14   	#include <fcntl.h>
15   	#include <limits.h>
16   	#include <stdbool.h>            // bool, true, false
17   	#include <stdio.h>
18   	#include <stdlib.h>
19   	#include <string.h>
20   	#include <sys/param.h>
21   	#include <sys/resource.h>
22   	#include <sys/stat.h>
23   	#include <sys/types.h>
24   	#include <unistd.h>
25   	
26   	#include <crm/crm.h>
27   	#include <crm/common/util.h>
28   	
29   	/*!
30   	 * \internal
31   	 * \brief Create a directory, including any parent directories needed
32   	 *
33   	 * \param[in] path_c Pathname of the directory to create
34   	 * \param[in] mode Permissions to be used (with current umask) when creating
35   	 *
36   	 * \return Standard Pacemaker return code
37   	 */
38   	int
39   	pcmk__build_path(const char *path_c, mode_t mode)
40   	{
41   	    int offset = 1, len = 0;
42   	    int rc = pcmk_rc_ok;
43   	    char *path = strdup(path_c);
44   	
45   	    CRM_CHECK(path != NULL, return -ENOMEM);
46   	    for (len = strlen(path); offset < len; offset++) {
47   	        if (path[offset] == '/') {
48   	            path[offset] = 0;
49   	            if ((mkdir(path, mode) < 0) && (errno != EEXIST)) {
50   	                rc = errno;
51   	                goto done;
52   	            }
53   	            path[offset] = '/';
54   	        }
55   	    }
56   	    if ((mkdir(path, mode) < 0) && (errno != EEXIST)) {
57   	        rc = errno;
58   	    }
59   	done:
60   	    free(path);
61   	    return rc;
62   	}
63   	
64   	/*!
65   	 * \internal
66   	 * \brief Return canonicalized form of a path name
67   	 *
68   	 * \param[in]  path           Pathname to canonicalize
69   	 * \param[out] resolved_path  Where to store canonicalized pathname
70   	 *
71   	 * \return Standard Pacemaker return code
72   	 * \note The caller is responsible for freeing \p resolved_path on success.
73   	 * \note This function exists because not all C library versions of
74   	 *       realpath(path, resolved_path) support a NULL resolved_path.
75   	 */
76   	int
77   	pcmk__real_path(const char *path, char **resolved_path)
78   	{
79   	    CRM_CHECK((path != NULL) && (resolved_path != NULL), return EINVAL);
80   	
81   	#if _POSIX_VERSION >= 200809L
82   	    /* Recent C libraries can dynamically allocate memory as needed */
83   	    *resolved_path = realpath(path, NULL);
84   	    return (*resolved_path == NULL)? errno : pcmk_rc_ok;
85   	
86   	#elif defined(PATH_MAX)
87   	    /* Older implementations require pre-allocated memory */
88   	    /* (this is less desirable because PATH_MAX may be huge or not defined) */
89   	    *resolved_path = malloc(PATH_MAX);
90   	    if ((*resolved_path == NULL) || (realpath(path, *resolved_path) == NULL)) {
91   	        return errno;
92   	    }
93   	    return pcmk_rc_ok;
94   	#else
95   	    *resolved_path = NULL;
96   	    return ENOTSUP;
97   	#endif
98   	}
99   	
100  	/*!
101  	 * \internal
102  	 * \brief Create a file name using a sequence number
103  	 *
104  	 * \param[in] directory  Directory that contains the file series
105  	 * \param[in] series     Start of file name
106  	 * \param[in] sequence   Sequence number
107  	 * \param[in] bzip       Whether to use ".bz2" instead of ".raw" as extension
108  	 *
109  	 * \return Newly allocated file path (asserts on error, so always non-NULL)
110  	 * \note The caller is responsible for freeing the return value.
111  	 */
112  	char *
113  	pcmk__series_filename(const char *directory, const char *series,
114  	                      unsigned int sequence, bool bzip)
115  	{
116  	    pcmk__assert((directory != NULL) && (series != NULL));
117  	    return pcmk__assert_asprintf("%s/%s-%u.%s", directory, series, sequence,
118  	                                 (bzip? "bz2" : "raw"));
119  	}
120  	
121  	/*!
122  	 * \internal
123  	 * \brief Read sequence number stored in a file series' .last file
124  	 *
125  	 * \param[in]  directory  Directory that contains the file series
126  	 * \param[in]  series     Start of file name
127  	 * \param[out] seq        Where to store the sequence number
128  	 *
129  	 * \return Standard Pacemaker return code
130  	 */
131  	int
132  	pcmk__read_series_sequence(const char *directory, const char *series,
133  	                           unsigned int *seq)
134  	{
135  	    int rc;
136  	    FILE *fp = NULL;
137  	    char *series_file = NULL;
138  	
139  	    if ((directory == NULL) || (series == NULL) || (seq == NULL)) {
140  	        return EINVAL;
141  	    }
142  	
143  	    series_file = pcmk__assert_asprintf("%s/%s.last", directory, series);
144  	    fp = fopen(series_file, "r");
145  	    if (fp == NULL) {
146  	        rc = errno;
147  	        pcmk__debug("Could not open series file %s: %s", series_file,
148  	                    strerror(rc));
149  	        free(series_file);
150  	        return rc;
151  	    }
152  	    errno = 0;
153  	    if (fscanf(fp, "%u", seq) != 1) {
154  	        rc = (errno == 0)? ENODATA : errno;
155  	        pcmk__debug("Could not read sequence number from series file %s: %s",
156  	                    series_file, pcmk_rc_str(rc));
157  	        fclose(fp);
158  	        free(series_file);
159  	        return rc;
160  	    }
161  	    fclose(fp);
162  	    pcmk__trace("Found last sequence number %u in series file %s", *seq,
163  	                series_file);
164  	    free(series_file);
165  	    return pcmk_rc_ok;
166  	}
167  	
168  	/*!
169  	 * \internal
170  	 * \brief Write sequence number to a file series' .last file
171  	 *
172  	 * \param[in] directory  Directory that contains the file series
173  	 * \param[in] series     Start of file name
174  	 * \param[in] sequence   Sequence number to write
175  	 * \param[in] max        Maximum sequence value, after which it is reset to 0
176  	 *
177  	 * \note This function logs some errors but does not return any to the caller
178  	 */
179  	void
180  	pcmk__write_series_sequence(const char *directory, const char *series,
181  	                            unsigned int sequence, int max)
182  	{
183  	    FILE *file_strm = NULL;
184  	    char *series_file = NULL;
185  	
186  	    CRM_CHECK(directory != NULL, return);
187  	    CRM_CHECK(series != NULL, return);
188  	
189  	    if (max == 0) {
190  	        return;
191  	    }
192  	    if (max > 0 && sequence >= max) {
193  	        sequence = 0;
194  	    }
195  	
196  	    series_file = pcmk__assert_asprintf("%s/%s.last", directory, series);
197  	    file_strm = fopen(series_file, "w");
198  	    if (file_strm != NULL) {
199  	        int rc = fprintf(file_strm, "%u", sequence);
200  	
201  	        if (rc < 0) {
202  	            pcmk__err("Cannot write to series file %s", series_file);
203  	        }
204  	        fflush(file_strm);
205  	        fclose(file_strm);
206  	
207  	    } else {
208  	        pcmk__err("Cannot open series file %s for writing", series_file);
209  	    }
210  	
211  	    free(series_file);
212  	}
213  	
214  	/*!
215  	 * \internal
216  	 * \brief Change the owner and group of a file series' .last file
217  	 *
218  	 * \param[in] directory  Directory that contains series
219  	 * \param[in] series     Series to change
220  	 * \param[in] uid        User ID of desired file owner
221  	 * \param[in] gid        Group ID of desired file group
222  	 *
223  	 * \return Standard Pacemaker return code
224  	 * \note The caller must have the appropriate privileges.
225  	 */
226  	int
227  	pcmk__chown_series_sequence(const char *directory, const char *series,
228  	                            uid_t uid, gid_t gid)
229  	{
230  	    char *series_file = NULL;
231  	    int rc = pcmk_rc_ok;
232  	
233  	    if ((directory == NULL) || (series == NULL)) {
234  	        return EINVAL;
235  	    }
236  	    series_file = pcmk__assert_asprintf("%s/%s.last", directory, series);
237  	    if (chown(series_file, uid, gid) < 0) {
238  	        rc = errno;
239  	    }
240  	    free(series_file);
241  	    return rc;
242  	}
243  	
244  	static bool
245  	pcmk__daemon_user_can_write(const char *target_name, struct stat *target_stat)
246  	{
247  	    uid_t daemon_uid = 0;
248  	    int rc = pcmk__daemon_user(&daemon_uid, NULL);
249  	
250  	    if (rc != pcmk_rc_ok) {
251  	        pcmk__notice("Could not find user " CRM_DAEMON_USER ": %s",
252  	                     pcmk_rc_str(rc));
253  	        return false;
254  	    }
255  	    if (target_stat->st_uid != daemon_uid) {
256  	        pcmk__notice("%s is not owned by user " CRM_DAEMON_USER " "
257  	                     QB_XS " uid %lld != %lld",
258  	                     target_name, (long long) daemon_uid,
259  	                     (long long) target_stat->st_uid);
260  	        return false;
261  	    }
262  	    if ((target_stat->st_mode & (S_IRUSR | S_IWUSR)) == 0) {
263  	        pcmk__notice("%s is not readable and writable by user %s "
264  	                     QB_XS " st_mode=0%lo",
265  	                     target_name, CRM_DAEMON_USER,
266  	                     (unsigned long) target_stat->st_mode);
267  	        return false;
268  	    }
269  	    return true;
270  	}
271  	
272  	static bool
273  	pcmk__daemon_group_can_write(const char *target_name, struct stat *target_stat)
274  	{
275  	    gid_t daemon_gid = 0;
276  	    int rc = pcmk__daemon_user(NULL, &daemon_gid);
277  	
278  	    if (rc != pcmk_rc_ok) {
279  	        pcmk__notice("Could not find group " CRM_DAEMON_GROUP ": %s",
280  	                     pcmk_rc_str(rc));
281  	        return false;
282  	    }
283  	
284  	    if (target_stat->st_gid != daemon_gid) {
285  	        pcmk__notice("%s is not owned by group " CRM_DAEMON_GROUP " "
286  	                     QB_XS " gid %lld != %lld",
287  	                     target_name, (long long) daemon_gid,
288  	                     (long long) target_stat->st_gid);
289  	        return false;
290  	    }
291  	
292  	    if ((target_stat->st_mode & (S_IRGRP | S_IWGRP)) == 0) {
293  	        pcmk__notice("%s is not readable and writable by group %s "
294  	                     QB_XS " st_mode=0%lo",
295  	                     target_name, CRM_DAEMON_GROUP,
296  	                     (unsigned long) target_stat->st_mode);
297  	        return false;
298  	    }
299  	    return true;
300  	}
301  	
302  	/*!
303  	 * \internal
304  	 * \brief Check whether a directory or file is writable by the cluster daemon
305  	 *
306  	 * Return true if either the cluster daemon user or cluster daemon group has
307  	 * write permission on a specified file or directory.
308  	 *
309  	 * \param[in] dir      Directory to check (this argument must be specified, and
310  	 *                     the directory must exist)
311  	 * \param[in] file     File to check (only the directory will be checked if this
312  	 *                     argument is not specified or the file does not exist)
313  	 *
314  	 * \return true if target is writable by cluster daemon, false otherwise
315  	 */
316  	bool
317  	pcmk__daemon_can_write(const char *dir, const char *file)
318  	{
319  	    int s_res = 0;
320  	    struct stat buf;
321  	    char *full_file = NULL;
322  	    const char *target = NULL;
323  	
324  	    // Caller must supply directory
(1) Event path: Condition "!(dir != NULL)", taking false branch.
325  	    pcmk__assert(dir != NULL);
326  	
327  	    // If file is given, check whether it exists as a regular file
(2) Event path: Condition "file != NULL", taking true branch.
328  	    if (file != NULL) {
329  	        full_file = pcmk__assert_asprintf("%s/%s", dir, file);
330  	        target = full_file;
331  	
332  	        s_res = stat(full_file, &buf);
(3) Event path: Condition "s_res < 0", taking true branch.
333  	        if (s_res < 0) {
334  	            pcmk__notice("%s not found: %s", target, pcmk_rc_str(errno));
CID (unavailable; MK=5fe25905a062752db0bd5c19cb5f6a40) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(4) Event assign_union_field: The union field "in" of "_pp" is written.
(5) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
335  	            g_clear_pointer(&full_file, &free);
336  	            target = NULL;
337  	
338  	        } else if (!S_ISREG(buf.st_mode)) {
339  	            pcmk__err("%s must be a regular file " QB_XS " st_mode=0%lo",
340  	                      target, (unsigned long) buf.st_mode);
341  	            free(full_file);
342  	            return false;
343  	        }
344  	    }
345  	
346  	    // If file is not given, ensure dir exists as directory
347  	    if (target == NULL) {
348  	        target = dir;
349  	        s_res = stat(dir, &buf);
350  	        if (s_res < 0) {
351  	            pcmk__err("%s not found: %s", dir, pcmk_rc_str(errno));
352  	            return false;
353  	
354  	        } else if (!S_ISDIR(buf.st_mode)) {
355  	            pcmk__err("%s must be a directory " QB_XS " st_mode=0%lo", dir,
356  	                      (unsigned long) buf.st_mode);
357  	            return false;
358  	        }
359  	    }
360  	
361  	    if (!pcmk__daemon_user_can_write(target, &buf)
362  	        && !pcmk__daemon_group_can_write(target, &buf)) {
363  	
364  	        pcmk__err("%s must be owned and writable by either user "
365  	                  CRM_DAEMON_USER " or group " CRM_DAEMON_GROUP " "
366  	                  QB_XS " st_mode=0%lo",
367  	                  target, (unsigned long) buf.st_mode);
368  	        free(full_file);
369  	        return false;
370  	    }
371  	
372  	    free(full_file);
373  	    return true;
374  	}
375  	
376  	/*!
377  	 * \internal
378  	 * \brief Flush and sync a directory to disk
379  	 *
380  	 * \param[in] name Directory to flush and sync
381  	 * \note This function logs errors but does not return them to the caller
382  	 */
383  	void
384  	pcmk__sync_directory(const char *name)
385  	{
386  	    int fd;
387  	    DIR *directory;
388  	
389  	    directory = opendir(name);
390  	    if (directory == NULL) {
391  	        pcmk__err("Could not open %s for syncing: %s", name, strerror(errno));
392  	        return;
393  	    }
394  	
395  	    fd = dirfd(directory);
396  	    if (fd < 0) {
397  	        pcmk__err("Could not obtain file descriptor for %s: %s", name,
398  	                  strerror(errno));
399  	        return;
400  	    }
401  	
402  	    if (fsync(fd) < 0) {
403  	        pcmk__err("Could not sync %s: %s", name, strerror(errno));
404  	    }
405  	    if (closedir(directory) < 0) {
406  	        pcmk__err("Could not close %s after fsync: %s", name, strerror(errno));
407  	    }
408  	}
409  	
410  	/*!
411  	 * \internal
412  	 * \brief Read the contents of a file
413  	 *
414  	 * \param[in]  filename  Name of file to read
415  	 * \param[out] contents  Where to store file contents
416  	 *
417  	 * \return Standard Pacemaker return code
418  	 * \note On success, the caller is responsible for freeing contents.
419  	 */
420  	int
421  	pcmk__file_contents(const char *filename, char **contents)
422  	{
423  	    FILE *fp;
424  	    int length, read_len;
425  	    int rc = pcmk_rc_ok;
426  	
427  	    if ((filename == NULL) || (contents == NULL)) {
428  	        return EINVAL;
429  	    }
430  	
431  	    fp = fopen(filename, "r");
432  	    if ((fp == NULL) || (fseek(fp, 0L, SEEK_END) < 0)) {
433  	        rc = errno;
434  	        goto bail;
435  	    }
436  	
437  	    length = ftell(fp);
438  	    if (length < 0) {
439  	        rc = errno;
440  	        goto bail;
441  	    }
442  	
443  	    if (length == 0) {
444  	        *contents = NULL;
445  	    } else {
446  	        *contents = calloc(length + 1, sizeof(char));
447  	        if (*contents == NULL) {
448  	            rc = errno;
449  	            goto bail;
450  	        }
451  	
452  	        errno = 0;
453  	
454  	        rewind(fp);
455  	        if (errno != 0) {
456  	            rc = errno;
457  	            goto bail;
458  	        }
459  	
460  	        read_len = fread(*contents, 1, length, fp);
461  	        if (read_len != length) {
462  	            g_clear_pointer(contents, &free);
463  	            rc = EIO;
464  	        } else {
465  	            /* Coverity thinks *contents isn't null-terminated. It doesn't
466  	             * understand calloc().
467  	             */
468  	            (*contents)[length] = '\0';
469  	        }
470  	    }
471  	
472  	bail:
473  	    if (fp != NULL) {
474  	        fclose(fp);
475  	    }
476  	    return rc;
477  	}
478  	
479  	/*!
480  	 * \internal
481  	 * \brief Write text to a file, flush and sync it to disk, then close the file
482  	 *
483  	 * \param[in] fd        File descriptor opened for writing
484  	 * \param[in] contents  String to write to file
485  	 *
486  	 * \return Standard Pacemaker return code
487  	 */
488  	int
489  	pcmk__write_sync(int fd, const char *contents)
490  	{
491  	    int rc = 0;
492  	    FILE *fp = fdopen(fd, "w");
493  	
494  	    if (fp == NULL) {
495  	        return errno;
496  	    }
497  	    if ((contents != NULL) && (fprintf(fp, "%s", contents) < 0)) {
498  	        rc = EIO;
499  	    }
500  	    if (fflush(fp) != 0) {
501  	        rc = errno;
502  	    }
503  	    if (fsync(fileno(fp)) < 0) {
504  	        rc = errno;
505  	    }
506  	    fclose(fp);
507  	    return rc;
508  	}
509  	
510  	/*!
511  	 * \internal
512  	 * \brief Set a file descriptor to non-blocking
513  	 *
514  	 * \param[in] fd  File descriptor to use
515  	 *
516  	 * \return Standard Pacemaker return code
517  	 */
518  	int
519  	pcmk__set_nonblocking(int fd)
520  	{
521  	    int flag = fcntl(fd, F_GETFL);
522  	
523  	    if (flag < 0) {
524  	        return errno;
525  	    }
526  	    if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
527  	        return errno;
528  	    }
529  	    return pcmk_rc_ok;
530  	}
531  	
532  	/*!
533  	 * \internal
534  	 * \brief Get directory name for temporary files
535  	 *
536  	 * Return the value of the TMPDIR environment variable if it is set to a
537  	 * full path, otherwise return "/tmp".
538  	 *
539  	 * \return Name of directory to be used for temporary files
540  	 */
541  	const char *
542  	pcmk__get_tmpdir(void)
543  	{
544  	    const char *dir = getenv("TMPDIR");
545  	
546  	    return (dir && (*dir == '/'))? dir : "/tmp";
547  	}
548  	
549  	/*!
550  	 * \internal
551  	 * \brief Close open file descriptors except standard streams
552  	 *
553  	 * Close all file descriptors (except stdin, stdout, and stderr), which is a
554  	 * best practice for a new child process forked for the purpose of executing an
555  	 * external program.
556  	 */
557  	void
558  	pcmk__close_fds_in_child(void)
559  	{
560  	    DIR *dir;
561  	    struct rlimit rlim;
562  	    rlim_t max_fd;
563  	    const int min_fd = 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  	    /* First try /proc.  If that returns NULL (either because opening the
577  	     * directory failed, or because procfs isn't supported on this platform),
578  	     * fall back to /dev/fd.
579  	     */
580  	    dir = pcmk__procfs_fd_dir();
581  	    if (dir == NULL) {
582  	        dir = opendir("/dev/fd");
583  	    }
584  	
585  	    if (dir != NULL) {
586  	        struct dirent *entry;
587  	        int dir_fd = dirfd(dir);
588  	
589  	        while ((entry = readdir(dir)) != NULL) {
590  	            int lpc = atoi(entry->d_name);
591  	
592  	            /* How could one of these entries be higher than max_fd, you ask?
593  	             * It isn't possible in normal operation, but when run under
594  	             * valgrind, valgrind can open high-numbered file descriptors for
595  	             * its own use that are higher than the process's soft limit.
596  	             * These will show up in the fd directory but aren't closable.
597  	             */
598  	            if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
599  	                close(lpc);
600  	            }
601  	        }
602  	        closedir(dir);
603  	        return;
604  	    }
605  	
606  	    /* If no fd directory is available, iterate over all possible descriptors.
607  	     * This is less efficient due to the overhead of many system calls.
608  	     */
609  	    for (int lpc = max_fd; lpc >= min_fd; lpc--) {
610  	        close(lpc);
611  	    }
612  	}
613  	
614  	/*!
615  	 * \brief Duplicate a file path, inserting a prefix if not absolute
616  	 *
617  	 * \param[in] filename  File path to duplicate
618  	 * \param[in] dirname   If filename is not absolute, prefix to add
619  	 *
620  	 * \return Newly allocated memory with full path (guaranteed non-NULL)
621  	 */
622  	char *
623  	pcmk__full_path(const char *filename, const char *dirname)
624  	{
625  	    pcmk__assert(filename != NULL);
626  	
627  	    if (filename[0] == '/') {
628  	        return pcmk__str_copy(filename);
629  	    }
630  	    pcmk__assert(dirname != NULL);
631  	    return pcmk__assert_asprintf("%s/%s", dirname, filename);
632  	}
633