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 General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <dirent.h>                 // dirent, scandir
13   	#include <errno.h>                  // errno, EACCES, ENODATA
14   	#include <signal.h>                 // SIGPIPE
15   	#include <stdbool.h>
16   	#include <stddef.h>                 // NULL
17   	#include <stdio.h>                  // rename
18   	#include <stdlib.h>                 // free, mkdtemp
19   	#include <string.h>                 // strerror, strrchr, etc.
20   	#include <sys/stat.h>               // stat, umask, etc.
21   	#include <sys/types.h>              // pid_t
22   	#include <time.h>                   // time_t
23   	#include <unistd.h>                 // _exit, fork
24   	
25   	#include <glib.h>                   // g_*, G_*
26   	#include <libxml/tree.h>            // xmlNode
27   	#include <qb/qbdefs.h>              // QB_FALSE, QB_TRUE
28   	#include <qb/qblog.h>               // qb_log_*
29   	
30   	#include <crm/cib/internal.h>       // cib_file_*
31   	#include <crm/cib/util.h>           // createEmptyCib
32   	#include <crm/common/internal.h>    // pcmk__assert_asprintf, PCMK__XE_*, etc.
33   	#include <crm/common/logging.h>     // CRM_CHECK
34   	#include <crm/common/mainloop.h>    // mainloop_add_signal
35   	#include <crm/common/results.h>     // pcmk_legacy2rc, pcmk_rc_*
36   	#include <crm/common/util.h>        // pcmk_common_cleanup
37   	#include <crm/common/xml.h>         // PCMK_XA_*, PCMK_XE_*
38   	
39   	#include "pacemaker-based.h"
40   	
41   	static bool writes_enabled = true;
42   	static crm_trigger_t *write_trigger = NULL;
43   	
44   	/*!
45   	 * \internal
46   	 * \brief Process the exit status of a child forked from \c write_cib_async()
47   	 *
48   	 * \param[in] child      Mainloop child data
49   	 * \param[in] core       If set to 1, the child process dumped core
50   	 * \param[in] signo      Signal that the child process exited with
51   	 * \param[in] exit_code  Child process's exit code
52   	 */
53   	static void
54   	write_cib_cb(mainloop_child_t *child, int core, int signo, int exit_code)
55   	{
56   	    const char *error = "Could not write CIB to disk";
57   	
58   	    if ((exit_code != 0) && writes_enabled) {
59   	        writes_enabled = false;
60   	        error = "Disabling CIB disk writes after failure";
61   	    }
62   	
63   	    if ((signo == 0) && (exit_code == 0)) {
64   	        pcmk__trace("Disk write [%lld] succeeded", (long long) child->pid);
65   	
66   	    } else if (signo == 0) {
67   	        pcmk__err("%s: process %lld exited with code %d", error,
68   	                  (long long) child->pid, exit_code);
69   	
70   	    } else {
71   	        pcmk__err("%s: process %lld terminated with signal %d (%s)%s",
72   	                  error, (long long) child->pid, signo, strsignal(signo),
73   	                  ((core != 0)? " and dumped core" : ""));
74   	    }
75   	
76   	    mainloop_trigger_complete(write_trigger);
77   	}
78   	
79   	/*!
80   	 * \internal
81   	 * \brief Write the CIB to disk in a forked child
82   	 *
83   	 * This avoids blocking in the parent. The child writes synchronously. The
84   	 * parent tracks the child via the mainloop and runs a callback when the child
85   	 * exits.
86   	 *
87   	 * \param[in] user_data  Ignored
88   	 */
89   	static int
90   	write_cib_async(gpointer user_data)
91   	{
92   	    int rc = pcmk_rc_ok;
93   	    pid_t pid = 0;
94   	    int blackbox_state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
95   	
96   	    /* Disable blackbox logging before the fork to avoid two processes writing
97   	     * to the same shared memory. The disable should not be done in the child,
98   	     * because this would close shared memory files in the parent.
99   	     *
100  	     * @TODO How? What is meant by this last sentence?
101  	     */
102  	    qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
103  	
104  	    pid = fork();
105  	    if (pid < 0) {
106  	        pcmk__err("Disabling disk writes after fork failure: %s",
107  	                  strerror(errno));
108  	        writes_enabled = false;
109  	
110  	        // Remove trigger from main loop
111  	        return 0;
112  	    }
113  	
114  	    if (pid > 0) {
115  	        // Parent
116  	        mainloop_child_add(pid, 0, "disk-writer", NULL, write_cib_cb);
117  	
118  	        if (blackbox_state == QB_LOG_STATE_ENABLED) {
119  	            qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
120  	        }
121  	
122  	        /* Mark job as running and keep trigger. write_cib_cb() will mark it as
123  	         * complete.
124  	         */
125  	        return -1;
126  	    }
127  	
128  	    /* Write the CIB. Note that this modifies the_cib, but this child is about
129  	     * to exit. The parent's copy of the_cib won't be affected.
130  	     */
131  	    rc = cib_file_write_with_digest(the_cib, cib_root, "cib.xml");
132  	    rc = pcmk_legacy2rc(rc);
133  	
134  	    pcmk_common_cleanup();
135  	
136  	    /* A nonzero exit code will cause further writes to be disabled. Use _exit()
137  	     * because exit() could affect the parent adversely.
138  	     *
139  	     * @TODO Investigate whether _exit() instead of exit() is really necessary.
140  	     * This goes back to commit 58cb43dc, which states that exit() may close
141  	     * things it shoudn't close. There is no explanation of what these things
142  	     * might be. The exit(2) man page states that exit() calls atexit/on_exit
143  	     * handlers and flushes open stdio streams. The exit(3) man page states that
144  	     * file created with tmpfile() are removed. But neither Pacemaker nor libqb
145  	     * uses atexit or on_exit, and it's not clear why we'd be worried about
146  	     * stdio streams.
147  	     */
148  	    switch (rc) {
149  	        case pcmk_rc_ok:
150  	            _exit(CRM_EX_OK);
151  	
152  	        case pcmk_rc_cib_modified:
153  	            _exit(CRM_EX_DIGEST);
154  	
155  	        case pcmk_rc_cib_backup:
156  	        case pcmk_rc_cib_save:
157  	            _exit(CRM_EX_CANTCREAT);
158  	
159  	        default:
160  	            _exit(CRM_EX_ERROR);
161  	    }
162  	}
163  	
164  	/*!
165  	 * \internal
166  	 * \brief Enable CIB writes to disk (signal handler)
167  	 *
168  	 * \param[in] nsig  Ignored
169  	 */
170  	void
171  	based_enable_writes(int nsig)
172  	{
173  	    pcmk__info("(Re)enabling disk writes");
174  	    writes_enabled = true;
175  	}
176  	
177  	/*!
178  	 * \internal
179  	 * \brief Initialize data structures for \c pacemaker-based I/O
180  	 */
181  	void
182  	based_io_init(void)
183  	{
184  	    writes_enabled = !stand_alone;
185  	    if (writes_enabled
186  	        && pcmk__env_option_enabled(PCMK__SERVER_BASED,
187  	                                    PCMK__ENV_VALGRIND_ENABLED)) {
188  	
189  	        writes_enabled = false;
190  	        pcmk__err("*** Disabling disk writes to avoid confusing Valgrind ***");
191  	    }
192  	
193  	    /* @TODO Should we be setting this up if we've explicitly disabled writes
194  	     * already?
195  	     */
196  	    mainloop_add_signal(SIGPIPE, based_enable_writes);
197  	
198  	    write_trigger = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_async, NULL);
199  	}
200  	
201  	/*!
202  	 * \internal
203  	 * \brief Rename a CIB or digest file after digest mismatch
204  	 *
205  	 * This is just a wrapper for logging an error. The caller should disable writes
206  	 * on error.
207  	 *
208  	 * \param[in] old_path  Original file path
209  	 * \param[in] new_path  New file path
210  	 *
211  	 * \return Standard Pacemaker return code
212  	 */
213  	static int
214  	rename_one(const char *old_path, const char *new_path)
215  	{
216  	    int rc = rename(old_path, new_path);
217  	
218  	    if (rc == 0) {
219  	        return pcmk_rc_ok;
220  	    }
221  	
222  	    rc = errno;
223  	    pcmk__err("Failed to rename %s to %s after digest mismatch: %s. Disabling "
224  	              "disk writes.", old_path, new_path, strerror(rc));
225  	    return rc;
226  	}
227  	
228  	#define CIBFILE "cib.xml"
229  	
230  	/*!
231  	 * \internal
232  	 * \brief Archive the current CIB file in \c cib_root with its saved digest file
233  	 *
234  	 * When a CIB file's calculated digest doesn't match its saved one, we archive
235  	 * both the CIB file and its digest (".sig") file. This way the contents can be
236  	 * inspected for troubleshooting purposes.
237  	 *
238  	 * A subdirectory with a unique name is created in \c cib_root, using the
239  	 * \c mkdtemp() template \c "cib.auto.XXXXXX". Then \c CIB_FILE and
240  	 * <tt>CIB_FILE ".sig"</tt> are moved to that directory.
241  	 *
242  	 * \param[in] old_cibfile_path  Original path of CIB file
243  	 * \param[in] old_sigfile_path  Original path of digest file
244  	 */
245  	static void
246  	archive_on_digest_mismatch(const char *old_cibfile_path,
247  	                           const char *old_sigfile_path)
248  	{
249  	    char *new_dir = pcmk__assert_asprintf("%s/cib.auto.XXXXXX", cib_root);
250  	    char *new_cibfile_path = NULL;
251  	    char *new_sigfile_path = NULL;
252  	
253  	    umask(S_IWGRP | S_IWOTH | S_IROTH);
254  	
255  	    if (mkdtemp(new_dir) == NULL) {
256  	        pcmk__err("Failed to create directory to archive %s and %s after "
257  	                  "digest mismatch: %s. Disabling disk writes.",
258  	                  old_cibfile_path, old_sigfile_path, strerror(errno));
259  	        writes_enabled = false;
260  	        goto done;
261  	    }
262  	
263  	    new_cibfile_path = pcmk__assert_asprintf("%s/%s", new_dir, CIBFILE);
264  	    new_sigfile_path = pcmk__assert_asprintf("%s.sig", new_cibfile_path);
265  	
266  	    if ((rename_one(old_cibfile_path, new_cibfile_path) != pcmk_rc_ok)
267  	        || (rename_one(old_sigfile_path, new_sigfile_path) != pcmk_rc_ok)) {
268  	
269  	        writes_enabled = false;
270  	        goto done;
271  	    }
272  	
273  	    pcmk__err("Archived %s and %s in %s after digest mismatch",
274  	              old_cibfile_path, old_sigfile_path, new_dir);
275  	
276  	done:
277  	    free(new_dir);
278  	    free(new_cibfile_path);
279  	    free(new_sigfile_path);
280  	}
281  	
282  	/*!
283  	 * \internal
284  	 * \brief Read CIB XML from \c CIBFILE in the \c cib_root directory
285  	 *
286  	 * \return CIB XML parsed from \c CIBFILE in \c cib_root , or \c NULL if the
287  	 *         file was not found or if parsing failed
288  	 *
289  	 * \note The caller is responsible for freeing the return value using
290  	 *       \c pcmk__xml_free().
291  	 */
292  	static xmlNode *
293  	read_current_cib(void)
294  	{
295  	    char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, CIBFILE);
296  	    char *sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path);
297  	    const char *sigfile = strrchr(sigfile_path, '/') + 1;
298  	
299  	    xmlNode *cib_xml = NULL;
300  	    int rc = pcmk_rc_ok;
301  	
302  	    if (!pcmk__daemon_can_write(cib_root, CIBFILE)
303  	        || !pcmk__daemon_can_write(cib_root, sigfile)) {
304  	
305  	        cib_status = EACCES;
306  	        goto done;
307  	    }
308  	
309  	    cib_status = pcmk_rc_ok;
310  	
311  	    rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml);
312  	    rc = pcmk_legacy2rc(rc);
313  	
314  	    if (rc == pcmk_rc_ok) {
315  	        pcmk__info("Loaded CIB from %s (with digest %s)", cibfile_path,
316  	                   sigfile_path);
317  	        goto done;
318  	    }
319  	
320  	    pcmk__warn("Continuing but NOT using CIB from %s (with digest %s): %s",
321  	               cibfile_path, sigfile_path, pcmk_rc_str(rc));
322  	
323  	    if (rc == pcmk_rc_cib_modified) {
324  	        // Archive the original files so the contents are not lost
325  	        archive_on_digest_mismatch(cibfile_path, sigfile_path);
326  	    }
327  	
328  	done:
329  	    free(cibfile_path);
330  	    free(sigfile_path);
331  	    return cib_xml;
332  	}
333  	
334  	/*!
335  	 * \internal
336  	 * \brief \c scandir() filter for backup CIB files in \c cib_root
337  	 *
338  	 * \param[in] entry  Directory entry
339  	 *
340  	 * \retval 1 if the entry is a regular file whose name begins with \c "cib-" and
341  	 *           does not end with ".sig"
342  	 * \retval 0 otherwise
343  	 */
344  	static int
345  	backup_cib_filter(const struct dirent *entry)
346  	{
347  	    char *path = pcmk__assert_asprintf("%s/%s", cib_root, entry->d_name);
348  	    struct stat sb;
349  	    int rc = stat(path, &sb);
350  	
351  	    free(path);
352  	
353  	    if (rc != 0) {
354  	        pcmk__warn("Filtering %s/%s during scan for backup CIB: stat() failed: "
355  	                   "%s", cib_root, entry->d_name, strerror(errno));
356  	        return 0;
357  	    }
358  	
359  	    return S_ISREG(sb.st_mode)
360  	           && g_str_has_prefix(entry->d_name, "cib-")
361  	           && !g_str_has_suffix(entry->d_name, ".sig");
362  	}
363  	
364  	/*!
365  	 * \internal
366  	 * \brief Get a file's last change time (\c ctime)
367  	 *
368  	 * The file is assumed to be a backup CIB file in the \c cib_root directory.
369  	 *
370  	 * \param[in] file  Base name of file
371  	 *
372  	 * \return Last change time of \p file, or 0 on \c stat() failure
373  	 */
374  	static time_t
375  	get_backup_cib_ctime(const char *file)
376  	{
377  	    char *path = pcmk__assert_asprintf("%s/%s", cib_root, file);
378  	    struct stat sb;
379  	    int rc = stat(path, &sb);
380  	
381  	    free(path);
382  	
383  	    if (rc != 0) {
384  	        pcmk__warn("Failed to stat() %s/%s while sorting backup CIBs: %s",
385  	                   cib_root, file, strerror(errno));
386  	        return 0;
387  	    }
388  	
389  	    return sb.st_ctime;
390  	}
391  	
392  	/*!
393  	 * \internal
394  	 * \brief Compare directory entries based on their last change times
395  	 *
396  	 * The entries are assumed to be CIB files in the \c cib_root directory.
397  	 *
398  	 * \param[in] entry1  First directory entry to compare
399  	 * \param[in] entry2  Second directory entry to compare
400  	 *
401  	 * \retval -1 if \p entry1 was changed more recently than \p entry2
402  	 * \retval  0 if \p entry1 was last changed at the same timestamp as \p entry2
403  	 * \retval  1 if \p entry1 was changed less recently than \p entry2
404  	 */
405  	static int
406  	compare_backup_cibs(const struct dirent **entry1, const struct dirent **entry2)
407  	{
408  	    time_t ctime1 = get_backup_cib_ctime((*entry1)->d_name);
409  	    time_t ctime2 = get_backup_cib_ctime((*entry2)->d_name);
410  	
411  	    if (ctime1 > ctime2) {
412  	        pcmk__trace("%s/%s (%lld) newer than %s/%s (%lld)",
413  	                    cib_root, (*entry1)->d_name, (long long) ctime1,
414  	                    cib_root, (*entry2)->d_name, (long long) ctime2);
415  	        return -1;
416  	    }
417  	
418  	    if (ctime1 < ctime2) {
419  	        pcmk__trace("%s/%s (%lld) older than %s/%s (%lld)",
420  	                    cib_root, (*entry1)->d_name, (long long) ctime1,
421  	                    cib_root, (*entry2)->d_name, (long long) ctime2);
422  	        return 1;
423  	    }
424  	
425  	    pcmk__trace("%s/%s (%lld) same age as %s/%s (%lld)",
426  	                cib_root, (*entry1)->d_name, (long long) ctime1,
427  	                cib_root, (*entry2)->d_name, (long long) ctime2);
428  	    return 0;
429  	}
430  	
431  	/*!
432  	 * \internal
433  	 * \brief Read CIB XML from the last valid backup file in \c cib_root
434  	 *
435  	 * \return CIB XML parsed from the last valid backup file, or \c NULL if none
436  	 *         was found
437  	 */
438  	static xmlNode *
439  	read_backup_cib(void)
440  	{
441  	    xmlNode *cib_xml = NULL;
442  	    struct dirent **namelist = NULL;
443  	    int num_files = scandir(cib_root, &namelist, backup_cib_filter,
444  	                            compare_backup_cibs);
445  	
446  	    if (num_files < 0) {
447  	        pcmk__err("Could not check for CIB backups in %s: %s", cib_root,
448  	                  pcmk_rc_str(errno));
449  	        goto done;
450  	    }
451  	
452  	    for (int i = 0; i < num_files; i++) {
453  	        const char *cibfile = namelist[i]->d_name;
454  	        char *cibfile_path = pcmk__assert_asprintf("%s/%s", cib_root, cibfile);
455  	        char *sigfile_path = pcmk__assert_asprintf("%s.sig", cibfile_path);
456  	
457  	        int rc = cib_file_read_and_verify(cibfile_path, sigfile_path, &cib_xml);
458  	
459  	        rc = pcmk_legacy2rc(rc);
460  	
461  	        if (rc == pcmk_rc_ok) {
462  	            pcmk__notice("Loaded CIB from last valid backup %s (with digest "
463  	                         "%s)", cibfile_path, sigfile_path);
464  	        } else {
465  	            pcmk__warn("Not using next most recent CIB backup from %s (with "
466  	                       "digest %s): %s", cibfile_path, sigfile_path,
467  	                       pcmk_rc_str(rc));
468  	        }
469  	
470  	        free(cibfile_path);
471  	        free(sigfile_path);
472  	
473  	        if (rc == pcmk_rc_ok) {
474  	            break;
475  	        }
476  	    }
477  	
478  	done:
479  	    for (int i = 0; i < num_files; i++) {
480  	        free(namelist[i]);
481  	    }
482  	    free(namelist);
483  	
484  	    return cib_xml;
485  	}
486  	
487  	/*!
488  	 * \internal
489  	 * \brief Set the CIB XML's \c PCMK_XE_STATUS element to empty if appropriate
490  	 *
491  	 * Delete the current \c PCMK_XE_STATUS element if not running in stand-alone
492  	 * mode. Then create an empty \c PCMK_XE_STATUS child if either of the following
493  	 * is true:
494  	 * * not running in stand-alone mode
495  	 * * running in stand-alone mode with no \c PCMK_XE_STATUS element
496  	 *
497  	 * \param[in,out] cib_xml  CIB XML
498  	 */
499  	static void
500  	set_empty_status(xmlNode *cib_xml)
501  	{
502  	    xmlNode *status = pcmk__xe_first_child(cib_xml, PCMK_XE_STATUS, NULL, NULL);
503  	
(1) Event path: Condition "!stand_alone", taking true branch.
504  	    if (!stand_alone) {
CID (unavailable; MK=8a6526783325e097e0be9b22aa0fbe7c) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(2) Event assign_union_field: The union field "in" of "_pp" is written.
(3) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
505  	        g_clear_pointer(&status, pcmk__xml_free);
506  	    }
507  	
508  	    if (status == NULL) {
509  	        pcmk__xe_create(cib_xml, PCMK_XE_STATUS);
510  	    }
511  	}
512  	
513  	/*!
514  	 * \internal
515  	 * \brief Set the given CIB version attribute to 0 if it's not already set
516  	 *
517  	 * \param[in,out] cib_xml       CIB XML
518  	 * \param[in]     version_attr  Version attribute
519  	 */
520  	static void
521  	set_default_if_unset(xmlNode *cib_xml, const char *version_attr)
522  	{
523  	    if (pcmk__xe_get(cib_xml, version_attr) != NULL) {
524  	        return;
525  	    }
526  	
527  	    pcmk__warn("Defaulting missing %s to 0, but cluster may get confused about "
528  	               "which node's configuration is most recent", version_attr);
529  	    pcmk__xe_set_int(cib_xml, version_attr, 0);
530  	}
531  	
532  	/*!
533  	 * \internal
534  	 * \brief Read the most recent CIB from a file in \c cib_root
535  	 *
536  	 * This function first tries to read the CIB from a file called \c "cib.xml" in
537  	 * the \c cib_root directory.
538  	 *
539  	 * If that fails or there is a digest mismatch, it tries all the backup CIB
540  	 * files in \c cib_root, in order from most recently changed to least, moving to
541  	 * the next backup file on failure or digest mismatch.
542  	 *
543  	 * If no valid CIB file is found, this function generates an empty CIB.
544  	 *
545  	 * \return The most current CIB XML available, or an empty CIB if none is
546  	 *         available (guaranteed not to be \c NULL)
547  	 *
548  	 * \note The caller is responsible for freeing the return value using
549  	 *       \c pcmk__xml_free().
550  	 */
551  	xmlNode *
552  	based_read_cib(void)
553  	{
554  	    static const char *version_attrs[] = {
555  	        PCMK_XA_ADMIN_EPOCH,
556  	        PCMK_XA_EPOCH,
557  	        PCMK_XA_NUM_UPDATES,
558  	    };
559  	
560  	    xmlNode *cib_xml = read_current_cib();
561  	
562  	    if (cib_xml == NULL) {
563  	        cib_xml = read_backup_cib();
564  	    }
565  	
566  	    if (cib_xml == NULL) {
567  	        cib_xml = createEmptyCib(0);
568  	        pcmk__warn("Continuing with an empty configuration");
569  	    }
570  	
571  	    set_empty_status(cib_xml);
572  	
573  	    /* Default the three version attributes to 0 if unset. The schema requires
574  	     * them to be set, so:
575  	     * * It's not possible for them to be unset if schema validation was enabled
576  	     *   when the CIB file was generated, or if it was generated by Pacemaker
577  	     *   and then unmodified.
578  	     * * We need to set these defaults before schema validation happens.
579  	     */
580  	    for (int i = 0; i < PCMK__NELEM(version_attrs); i++) {
581  	        set_default_if_unset(cib_xml, version_attrs[i]);
582  	    }
583  	
584  	    // The DC should set appropriate value for PCMK_XA_DC_UUID
585  	    pcmk__xe_remove_attr(cib_xml, PCMK_XA_DC_UUID);
586  	
587  	    if (!stand_alone) {
588  	        pcmk__log_xml_trace(cib_xml, "on-disk");
589  	    }
590  	
591  	    if (!pcmk__configured_schema_validates(cib_xml)) {
592  	        cib_status = pcmk_rc_schema_validation;
593  	    }
594  	
595  	    return cib_xml;
596  	}
597  	
598  	/*!
599  	 * \internal
600  	 * \brief Activate new CIB XML
601  	 *
602  	 * This function frees the existing \c the_cib and points it to \p new_cib.
603  	 *
604  	 * \param[in] new_cib  CIB XML to activate (must not be \c NULL or equal to
605  	 *                     \c the_cib)
606  	 * \param[in] to_disk  If \c true and if the CIB status is OK and writes are
607  	 *                     enabled, trigger the new CIB to be written to disk
608  	 * \param[in] op       Operation that triggered the activation (for logging
609  	 *                     only)
610  	 *
611  	 * \return Standard Pacemaker return code
612  	 *
613  	 * \note This function takes ownership of \p new_cib by assigning it to
614  	 *       \c the_cib. The caller should not free it.
615  	 */
616  	int
617  	based_activate_cib(xmlNode *new_cib, bool to_disk, const char *op)
618  	{
619  	    CRM_CHECK((new_cib != NULL) && (new_cib != the_cib), return ENODATA);
620  	
621  	    pcmk__xml_free(the_cib);
622  	    the_cib = new_cib;
623  	
624  	    if (to_disk && writes_enabled && (cib_status == pcmk_rc_ok)) {
625  	        pcmk__debug("Triggering CIB write for %s op", op);
626  	        mainloop_set_trigger(write_trigger);
627  	    }
628  	
629  	    return pcmk_rc_ok;
630  	}
631