1    	/*
2    	 * Copyright 2004-2023 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 General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <stdio.h>
13   	#include <unistd.h>
14   	#include <string.h>
15   	#include <stdlib.h>
16   	#include <errno.h>
17   	#include <fcntl.h>
18   	#include <dirent.h>
19   	
20   	#include <sys/param.h>
21   	#include <sys/types.h>
22   	#include <sys/wait.h>
23   	#include <sys/stat.h>
24   	
25   	#include <glib.h>
26   	#include <libxml/tree.h>
27   	
28   	#include <crm/crm.h>
29   	
30   	#include <crm/cib.h>
31   	#include <crm/common/util.h>
32   	#include <crm/msg_xml.h>
33   	#include <crm/common/xml.h>
34   	#include <crm/cib/internal.h>
35   	#include <crm/cluster.h>
36   	
37   	#include <pacemaker-based.h>
38   	
39   	crm_trigger_t *cib_writer = NULL;
40   	
41   	int write_cib_contents(gpointer p);
42   	
43   	static void
44   	cib_rename(const char *old)
45   	{
46   	    int new_fd;
47   	    char *new = crm_strdup_printf("%s/cib.auto.XXXXXX", cib_root);
48   	
49   	    umask(S_IWGRP | S_IWOTH | S_IROTH);
(1) Event open_fn: Returning handle opened by "mkstemp".
(2) Event var_assign: Assigning: "new_fd" = handle returned from "mkstemp(new)".
Also see events: [off_by_one][remediation][leaked_handle]
50   	    new_fd = mkstemp(new);
51   	
(3) Event path: Condition "new_fd < 0", taking false branch.
(4) Event path: Condition "rename(old, new) < 0", taking false branch.
52   	    if ((new_fd < 0) || (rename(old, new) < 0)) {
53   	        crm_err("Couldn't archive unusable file %s (disabling disk writes and continuing)",
54   	                old);
55   	        cib_writes_enabled = FALSE;
56   	    } else {
57   	        crm_err("Archived unusable file %s as %s", old, new);
58   	    }
59   	
(5) Event path: Condition "new_fd > 0", taking false branch.
(6) Event off_by_one: Testing whether handle "new_fd" is strictly greater than zero is suspicious. "new_fd" leaks when it is zero.
(7) Event remediation: Did you intend to include equality with zero?
Also see events: [open_fn][var_assign][leaked_handle]
60   	    if (new_fd > 0) {
61   	        close(new_fd);
62   	    }
63   	    free(new);
CID (unavailable; MK=0759a52ab30e0689bf71ca81a527dcd5) (#1 of 1): Resource leak (RESOURCE_LEAK):
(8) Event leaked_handle: Handle variable "new_fd" going out of scope leaks the handle.
Also see events: [open_fn][var_assign][off_by_one][remediation]
64   	}
65   	
66   	/*
67   	 * It is the callers responsibility to free the output of this function
68   	 */
69   	
70   	static xmlNode *
71   	retrieveCib(const char *filename, const char *sigfile)
72   	{
73   	    xmlNode *root = NULL;
74   	
75   	    crm_info("Reading cluster configuration file %s (digest: %s)",
76   	             filename, sigfile);
77   	    switch (cib_file_read_and_verify(filename, sigfile, &root)) {
78   	        case -pcmk_err_cib_corrupt:
79   	            crm_warn("Continuing but %s will NOT be used.", filename);
80   	            break;
81   	
82   	        case -pcmk_err_cib_modified:
83   	            /* Archive the original files so the contents are not lost */
84   	            crm_warn("Continuing but %s will NOT be used.", filename);
85   	            cib_rename(filename);
86   	            cib_rename(sigfile);
87   	            break;
88   	    }
89   	    return root;
90   	}
91   	
92   	/*
93   	 * for OSs without support for direntry->d_type, like Solaris
94   	 */
95   	#ifndef DT_UNKNOWN
96   	# define DT_UNKNOWN     0
97   	# define DT_FIFO        1
98   	# define DT_CHR         2
99   	# define DT_DIR         4
100  	# define DT_BLK         6
101  	# define DT_REG         8
102  	# define DT_LNK         10
103  	# define DT_SOCK        12
104  	# define DT_WHT         14
105  	#endif /*DT_UNKNOWN*/
106  	
107  	static int cib_archive_filter(const struct dirent * a)
108  	{
109  	    int rc = 0;
110  	    /* Looking for regular files (d_type = 8) starting with 'cib-' and not ending in .sig */
111  	    struct stat s;
112  	    char *a_path = crm_strdup_printf("%s/%s", cib_root, a->d_name);
113  	
114  	    if(stat(a_path, &s) != 0) {
115  	        rc = errno;
116  	        crm_trace("%s - stat failed: %s (%d)", a->d_name, pcmk_rc_str(rc), rc);
117  	        rc = 0;
118  	
119  	    } else if ((s.st_mode & S_IFREG) != S_IFREG) {
120  	        unsigned char dtype;
121  	#ifdef HAVE_STRUCT_DIRENT_D_TYPE
122  	        dtype = a->d_type;
123  	#else
124  	        switch (s.st_mode & S_IFMT) {
125  	            case S_IFREG:  dtype = DT_REG;      break;
126  	            case S_IFDIR:  dtype = DT_DIR;      break;
127  	            case S_IFCHR:  dtype = DT_CHR;      break;
128  	            case S_IFBLK:  dtype = DT_BLK;      break;
129  	            case S_IFLNK:  dtype = DT_LNK;      break;
130  	            case S_IFIFO:  dtype = DT_FIFO;     break;
131  	            case S_IFSOCK: dtype = DT_SOCK;     break;
132  	            default:       dtype = DT_UNKNOWN;  break;
133  	        }
134  	#endif
135  	         crm_trace("%s - wrong type (%d)", a->d_name, dtype);
136  	
137  	    } else if(strstr(a->d_name, "cib-") != a->d_name) {
138  	        crm_trace("%s - wrong prefix", a->d_name);
139  	
140  	    } else if (pcmk__ends_with_ext(a->d_name, ".sig")) {
141  	        crm_trace("%s - wrong suffix", a->d_name);
142  	
143  	    } else {
144  	        crm_debug("%s - candidate", a->d_name);
145  	        rc = 1;
146  	    }
147  	
148  	    free(a_path);
149  	    return rc;
150  	}
151  	
152  	static int cib_archive_sort(const struct dirent ** a, const struct dirent **b)
153  	{
154  	    /* Order by creation date - most recently created file first */
155  	    int rc = 0;
156  	    struct stat buf;
157  	
158  	    time_t a_age = 0;
159  	    time_t b_age = 0;
160  	
161  	    char *a_path = crm_strdup_printf("%s/%s", cib_root, a[0]->d_name);
162  	    char *b_path = crm_strdup_printf("%s/%s", cib_root, b[0]->d_name);
163  	
164  	    if(stat(a_path, &buf) == 0) {
165  	        a_age = buf.st_ctime;
166  	    }
167  	    if(stat(b_path, &buf) == 0) {
168  	        b_age = buf.st_ctime;
169  	    }
170  	
171  	    free(a_path);
172  	    free(b_path);
173  	
174  	    if(a_age > b_age) {
175  	        rc = 1;
176  	    } else if(a_age < b_age) {
177  	        rc = -1;
178  	    }
179  	
180  	    crm_trace("%s (%lu) vs. %s (%lu) : %d",
181  		a[0]->d_name, (unsigned long)a_age,
182  		b[0]->d_name, (unsigned long)b_age, rc);
183  	    return rc;
184  	}
185  	
186  	xmlNode *
187  	readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
188  	{
189  	    struct dirent **namelist = NULL;
190  	
191  	    int lpc = 0;
192  	    char *sigfile = NULL;
193  	    char *sigfilepath = NULL;
194  	    char *filename = NULL;
195  	    const char *name = NULL;
196  	    const char *value = NULL;
197  	    const char *validation = NULL;
198  	    const char *use_valgrind = pcmk__env_option(PCMK__ENV_VALGRIND_ENABLED);
199  	
200  	    xmlNode *root = NULL;
201  	    xmlNode *status = NULL;
202  	
203  	    sigfile = crm_strdup_printf("%s.sig", file);
204  	    if (pcmk__daemon_can_write(dir, file) == FALSE
205  	            || pcmk__daemon_can_write(dir, sigfile) == FALSE) {
206  	        cib_status = -EACCES;
207  	        return NULL;
208  	    }
209  	
210  	    filename = crm_strdup_printf("%s/%s", dir, file);
211  	    sigfilepath = crm_strdup_printf("%s/%s", dir, sigfile);
212  	    free(sigfile);
213  	
214  	    cib_status = pcmk_ok;
215  	    root = retrieveCib(filename, sigfilepath);
216  	    free(filename);
217  	    free(sigfilepath);
218  	
219  	    if (root == NULL) {
220  	        crm_warn("Primary configuration corrupt or unusable, trying backups in %s", cib_root);
221  	        lpc = scandir(cib_root, &namelist, cib_archive_filter, cib_archive_sort);
222  	        if (lpc < 0) {
223  	            crm_err("scandir(%s) failed: %s", cib_root, pcmk_rc_str(errno));
224  	        }
225  	    }
226  	
227  	    while (root == NULL && lpc > 1) {
228  	        crm_debug("Testing %d candidates", lpc);
229  	
230  	        lpc--;
231  	
232  	        filename = crm_strdup_printf("%s/%s", cib_root, namelist[lpc]->d_name);
233  	        sigfile = crm_strdup_printf("%s.sig", filename);
234  	
235  	        crm_info("Reading cluster configuration file %s (digest: %s)",
236  	                 filename, sigfile);
237  	        if (cib_file_read_and_verify(filename, sigfile, &root) < 0) {
238  	            crm_warn("Continuing but %s will NOT be used.", filename);
239  	        } else {
240  	            crm_notice("Continuing with last valid configuration archive: %s", filename);
241  	        }
242  	
243  	        free(namelist[lpc]);
244  	        free(filename);
245  	        free(sigfile);
246  	    }
247  	    free(namelist);
248  	
249  	    if (root == NULL) {
250  	        root = createEmptyCib(0);
251  	        crm_warn("Continuing with an empty configuration.");
252  	    }
253  	
254  	    if (cib_writes_enabled && use_valgrind &&
255  	        (crm_is_true(use_valgrind) || strstr(use_valgrind, "pacemaker-based"))) {
256  	
257  	        cib_writes_enabled = FALSE;
258  	        crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
259  	    }
260  	
261  	    status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE);
262  	    if (discard_status && status != NULL) {
263  	        /* strip out the status section if there is one */
264  	        free_xml(status);
265  	        status = NULL;
266  	    }
267  	    if (status == NULL) {
268  	        create_xml_node(root, XML_CIB_TAG_STATUS);
269  	    }
270  	
271  	    /* Do this before schema validation happens */
272  	
273  	    /* fill in some defaults */
274  	    name = XML_ATTR_GENERATION_ADMIN;
275  	    value = crm_element_value(root, name);
276  	    if (value == NULL) {
277  	        crm_warn("No value for %s was specified in the configuration.", name);
278  	        crm_warn("The recommended course of action is to shutdown,"
279  	                 " run crm_verify and fix any errors it reports.");
280  	        crm_warn("We will default to zero and continue but may get"
281  	                 " confused about which configuration to use if"
282  	                 " multiple nodes are powered up at the same time.");
283  	        crm_xml_add_int(root, name, 0);
284  	    }
285  	
286  	    name = XML_ATTR_GENERATION;
287  	    value = crm_element_value(root, name);
288  	    if (value == NULL) {
289  	        crm_xml_add_int(root, name, 0);
290  	    }
291  	
292  	    name = XML_ATTR_NUMUPDATES;
293  	    value = crm_element_value(root, name);
294  	    if (value == NULL) {
295  	        crm_xml_add_int(root, name, 0);
296  	    }
297  	
298  	    // Unset (DC should set appropriate value)
299  	    xml_remove_prop(root, XML_ATTR_DC_UUID);
300  	
301  	    if (discard_status) {
302  	        crm_log_xml_trace(root, "[on-disk]");
303  	    }
304  	
305  	    validation = crm_element_value(root, XML_ATTR_VALIDATION);
306  	    if (validate_xml(root, NULL, TRUE) == FALSE) {
307  	        crm_err("CIB does not validate with %s",
308  	                pcmk__s(validation, "no schema specified"));
309  	        cib_status = -pcmk_err_schema_validation;
310  	
311  	    } else if (validation == NULL) {
312  	        int version = 0;
313  	
314  	        update_validation(&root, &version, 0, FALSE, FALSE);
315  	        if (version > 0) {
316  	            crm_notice("Enabling %s validation on"
317  	                       " the existing (sane) configuration", get_schema_name(version));
318  	        } else {
319  	            crm_err("CIB does not validate with any known schema");
320  	            cib_status = -pcmk_err_schema_validation;
321  	        }
322  	    }
323  	
324  	    return root;
325  	}
326  	
327  	gboolean
328  	uninitializeCib(void)
329  	{
330  	    xmlNode *tmp_cib = the_cib;
331  	
332  	    if (tmp_cib == NULL) {
333  	        crm_debug("The CIB has already been deallocated.");
334  	        return FALSE;
335  	    }
336  	
337  	    the_cib = NULL;
338  	
339  	    crm_debug("Deallocating the CIB.");
340  	
341  	    free_xml(tmp_cib);
342  	
343  	    crm_debug("The CIB has been deallocated.");
344  	
345  	    return TRUE;
346  	}
347  	
348  	/*
349  	 * This method will free the old CIB pointer on success and the new one
350  	 * on failure.
351  	 */
352  	int
353  	activateCibXml(xmlNode * new_cib, gboolean to_disk, const char *op)
354  	{
355  	    if (new_cib) {
356  	        xmlNode *saved_cib = the_cib;
357  	
358  	        CRM_ASSERT(new_cib != saved_cib);
359  	        the_cib = new_cib;
360  	        free_xml(saved_cib);
361  	        if (cib_writes_enabled && cib_status == pcmk_ok && to_disk) {
362  	            crm_debug("Triggering CIB write for %s op", op);
363  	            mainloop_set_trigger(cib_writer);
364  	        }
365  	        return pcmk_ok;
366  	    }
367  	
368  	    crm_err("Ignoring invalid CIB");
369  	    if (the_cib) {
370  	        crm_warn("Reverting to last known CIB");
371  	    } else {
372  	        crm_crit("Could not write out new CIB and no saved version to revert to");
373  	    }
374  	    return -ENODATA;
375  	}
376  	
377  	static void
378  	cib_diskwrite_complete(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
379  	{
380  	    const char *errmsg = "Could not write CIB to disk";
381  	
382  	    if ((exitcode != 0) && cib_writes_enabled) {
383  	        cib_writes_enabled = FALSE;
384  	        errmsg = "Disabling CIB disk writes after failure";
385  	    }
386  	
387  	    if ((signo == 0) && (exitcode == 0)) {
388  	        crm_trace("Disk write [%d] succeeded", (int) pid);
389  	
390  	    } else if (signo == 0) {
391  	        crm_err("%s: process %d exited %d", errmsg, (int) pid, exitcode);
392  	
393  	    } else {
394  	        crm_err("%s: process %d terminated with signal %d (%s)%s",
395  	                errmsg, (int) pid, signo, strsignal(signo),
396  	                (core? " and dumped core" : ""));
397  	    }
398  	
399  	    mainloop_trigger_complete(cib_writer);
400  	}
401  	
402  	int
403  	write_cib_contents(gpointer p)
404  	{
405  	    int exit_rc = pcmk_ok;
406  	    xmlNode *cib_local = NULL;
407  	
408  	    /* Make a copy of the CIB to write (possibly in a forked child) */
409  	    if (p) {
410  	        /* Synchronous write out */
411  	        cib_local = copy_xml(p);
412  	
413  	    } else {
414  	        int pid = 0;
415  	        int bb_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
416  	
417  	        /* Turn it off before the fork() to avoid:
418  	         * - 2 processes writing to the same shared mem
419  	         * - the child needing to disable it
420  	         *   (which would close it from underneath the parent)
421  	         * This way, the shared mem files are already closed
422  	         */
423  	        qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
424  	
425  	        pid = fork();
426  	        if (pid < 0) {
427  	            crm_err("Disabling disk writes after fork failure: %s", pcmk_rc_str(errno));
428  	            cib_writes_enabled = FALSE;
429  	            return FALSE;
430  	        }
431  	
432  	        if (pid) {
433  	            /* Parent */
434  	            mainloop_child_add(pid, 0, "disk-writer", NULL, cib_diskwrite_complete);
435  	            if (bb_state == QB_LOG_STATE_ENABLED) {
436  	                /* Re-enable now that it it safe */
437  	                qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
438  	            }
439  	
440  	            return -1;          /* -1 means 'still work to do' */
441  	        }
442  	
443  	        /* Asynchronous write-out after a fork() */
444  	
445  	        /* In theory, we can scribble on the_cib here and not affect the parent,
446  	         * but let's be safe anyway.
447  	         */
448  	        cib_local = copy_xml(the_cib);
449  	    }
450  	
451  	    /* Write the CIB */
452  	    exit_rc = cib_file_write_with_digest(cib_local, cib_root, "cib.xml");
453  	
454  	    /* A nonzero exit code will cause further writes to be disabled */
455  	    free_xml(cib_local);
456  	    if (p == NULL) {
457  	        crm_exit_t exit_code = CRM_EX_OK;
458  	
459  	        switch (exit_rc) {
460  	            case pcmk_ok:
461  	                exit_code = CRM_EX_OK;
462  	                break;
463  	            case pcmk_err_cib_modified:
464  	                exit_code = CRM_EX_DIGEST; // Existing CIB doesn't match digest
465  	                break;
466  	            case pcmk_err_cib_backup: // Existing CIB couldn't be backed up
467  	            case pcmk_err_cib_save:   // New CIB couldn't be saved
468  	                exit_code = CRM_EX_CANTCREAT;
469  	                break;
470  	            default:
471  	                exit_code = CRM_EX_ERROR;
472  	                break;
473  	        }
474  	
475  	        /* Use _exit() because exit() could affect the parent adversely */
476  	        _exit(exit_code);
477  	    }
478  	    return exit_rc;
479  	}
480