1    	/*
2    	 * Original copyright 2004 International Business Machines
3    	 * Later changes copyright 2008-2026 the Pacemaker project contributors
4    	 *
5    	 * The version control history for this file may have further details.
6    	 *
7    	 * This source code is licensed under the GNU Lesser General Public License
8    	 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
9    	 */
10   	#include <crm_internal.h>
11   	#include <unistd.h>
12   	#include <stdbool.h>
13   	#include <stdlib.h>
14   	#include <stdio.h>
15   	#include <stdarg.h>
16   	#include <string.h>
17   	#include <sys/utsname.h>
18   	
19   	#include <glib.h>
20   	
21   	#include <crm/crm.h>
22   	#include <crm/cib/internal.h>
23   	#include <crm/common/xml.h>
24   	
25   	gboolean
26   	cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
27   	{
28   	    *epoch = -1;
29   	    *updates = -1;
30   	    *admin_epoch = -1;
31   	
32   	    if (cib == NULL) {
33   	        return FALSE;
34   	    }
35   	
36   	    pcmk__xe_get_int(cib, PCMK_XA_EPOCH, epoch);
37   	    pcmk__xe_get_int(cib, PCMK_XA_NUM_UPDATES, updates);
38   	    pcmk__xe_get_int(cib, PCMK_XA_ADMIN_EPOCH, admin_epoch);
39   	    return TRUE;
40   	}
41   	
42   	/*!
43   	 * \internal
44   	 * \brief Get the XML patchset from a CIB diff notification
45   	 *
46   	 * \param[in]  msg       CIB diff notification
47   	 * \param[out] patchset  Where to store XML patchset
48   	 *
49   	 * \return Standard Pacemaker return code
50   	 */
51   	int
52   	cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
53   	{
54   	    int rc = pcmk_err_generic;
55   	    xmlNode *wrapper = NULL;
56   	
57   	    pcmk__assert(patchset != NULL);
58   	    *patchset = NULL;
59   	
60   	    if (msg == NULL) {
61   	        pcmk__err("CIB diff notification received with no XML");
62   	        return ENOMSG;
63   	    }
64   	
65   	    if ((pcmk__xe_get_int(msg, PCMK__XA_CIB_RC, &rc) != pcmk_rc_ok)
66   	        || (rc != pcmk_ok)) {
67   	
68   	        pcmk__warn("Ignore failed CIB update: %s " QB_XS " rc=%d",
69   	                   pcmk_strerror(rc), rc);
70   	        pcmk__log_xml_debug(msg, "failed");
71   	        return pcmk_legacy2rc(rc);
72   	    }
73   	
74   	    wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, NULL, NULL);
75   	    *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
76   	
77   	    if (*patchset == NULL) {
78   	        pcmk__err("CIB diff notification received with no patchset");
79   	        return ENOMSG;
80   	    }
81   	    return pcmk_rc_ok;
82   	}
83   	
84   	/*!
85   	 * \brief Create XML for a new (empty) CIB
86   	 *
87   	 * \param[in] cib_epoch  What to use as \c PCMK_XA_EPOCH CIB attribute
88   	 *
89   	 * \return Newly created XML for empty CIB
90   	 *
91   	 * \note It is the caller's responsibility to free the result with
92   	 *       \c pcmk__xml_free().
93   	 */
94   	xmlNode *
95   	createEmptyCib(int cib_epoch)
96   	{
97   	    xmlNode *cib_root = NULL, *config = NULL;
98   	
99   	    cib_root = pcmk__xe_create(NULL, PCMK_XE_CIB);
100  	    pcmk__xe_set(cib_root, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
101  	    pcmk__xe_set(cib_root, PCMK_XA_VALIDATE_WITH, pcmk__highest_schema_name());
102  	
103  	    pcmk__xe_set_int(cib_root, PCMK_XA_ADMIN_EPOCH, 0);
104  	    pcmk__xe_set_int(cib_root, PCMK_XA_EPOCH, cib_epoch);
105  	    pcmk__xe_set_int(cib_root, PCMK_XA_NUM_UPDATES, 0);
106  	
107  	    config = pcmk__xe_create(cib_root, PCMK_XE_CONFIGURATION);
108  	    pcmk__xe_create(cib_root, PCMK_XE_STATUS);
109  	
110  	    pcmk__xe_create(config, PCMK_XE_CRM_CONFIG);
111  	    pcmk__xe_create(config, PCMK_XE_NODES);
112  	    pcmk__xe_create(config, PCMK_XE_RESOURCES);
113  	    pcmk__xe_create(config, PCMK_XE_CONSTRAINTS);
114  	
115  	#if PCMK__RESOURCE_STICKINESS_DEFAULT != 0
116  	    {
117  	        xmlNode *rsc_defaults = pcmk__xe_create(config, PCMK_XE_RSC_DEFAULTS);
118  	        xmlNode *meta = pcmk__xe_create(rsc_defaults, PCMK_XE_META_ATTRIBUTES);
119  	        xmlNode *nvpair = pcmk__xe_create(meta, PCMK_XE_NVPAIR);
120  	
121  	        pcmk__xe_set(meta, PCMK_XA_ID, "build-resource-defaults");
122  	        pcmk__xe_set(nvpair, PCMK_XA_ID,
123  	                     "build-" PCMK_META_RESOURCE_STICKINESS);
124  	        pcmk__xe_set(nvpair, PCMK_XA_NAME, PCMK_META_RESOURCE_STICKINESS);
125  	        pcmk__xe_set_int(nvpair, PCMK_XA_VALUE,
126  	                         PCMK__RESOURCE_STICKINESS_DEFAULT);
127  	    }
128  	#endif
129  	    return cib_root;
130  	}
131  	
132  	static void
133  	read_config(GHashTable *options, xmlNode *current_cib)
134  	{
135  	    crm_time_t *now = NULL;
136  	    pcmk_rule_input_t rule_input = { 0, };
137  	    xmlNode *config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG);
138  	
139  	    if (config == NULL) {
140  	        return;
141  	    }
142  	
143  	    now = crm_time_new(NULL);
144  	    rule_input.now = now;
145  	
146  	    pcmk_unpack_nvpair_blocks(config, PCMK_XE_CLUSTER_PROPERTY_SET,
147  	                              PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input,
148  	                              options, NULL);
149  	    crm_time_free(now);
150  	}
151  	
152  	static bool
153  	cib_acl_enabled(xmlNode *xml, const char *user)
154  	{
155  	    const char *value = NULL;
156  	    GHashTable *options = NULL;
157  	    bool rc = false;
158  	
159  	    if ((xml == NULL) || !pcmk_acl_required(user)) {
160  	        return false;
161  	    }
162  	
163  	    options = pcmk__strkey_table(free, free);
164  	    read_config(options, xml);
165  	    value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL);
166  	
167  	    rc = pcmk__is_true(value);
168  	    g_hash_table_destroy(options);
169  	    return rc;
170  	}
171  	
172  	int
173  	cib__perform_query(const char *op, uint32_t call_options, cib__op_fn_t fn,
174  	                   const char *section, xmlNode *req, xmlNode *input,
175  	                   xmlNode **current_cib, xmlNode **output)
176  	{
177  	    int rc = pcmk_rc_ok;
178  	    const char *user = NULL;
179  	
180  	    xmlNode *cib = NULL;
181  	    xmlNode *cib_filtered = NULL;
182  	
183  	    pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL)
184  	                 && (current_cib != NULL) && (*current_cib != NULL)
185  	                 && (output != NULL) && (*output == NULL));
186  	
187  	    user = pcmk__xe_get(req, PCMK__XA_CIB_USER);
188  	    cib = *current_cib;
189  	
190  	    if (cib_acl_enabled(*current_cib, user)
191  	        && xml_acl_filtered_copy(user, *current_cib, *current_cib,
192  	                                 &cib_filtered)) {
193  	
194  	        if (cib_filtered == NULL) {
195  	            pcmk__debug("Pre-filtered the entire cib");
196  	            return EACCES;
197  	        }
198  	        cib = cib_filtered;
199  	        pcmk__log_xml_trace(cib, "filtered");
200  	    }
201  	
202  	    pcmk__trace("Processing %s for section '%s', user '%s'", op,
203  	                pcmk__s(section, "(null)"), pcmk__s(user, "(null)"));
204  	    pcmk__log_xml_trace(req, "request");
205  	
206  	    rc = fn(op, call_options, section, req, input, &cib, output);
207  	
208  	    if (*output == NULL) {
209  	        // Do nothing
210  	
211  	    } else if (cib_filtered == *output) {
212  	        // Let them have this copy
213  	        cib_filtered = NULL;
214  	
215  	    } else if (*output == *current_cib) {
216  	        // They already know not to free it
217  	
218  	    } else if ((cib_filtered != NULL)
219  	               && ((*output)->doc == cib_filtered->doc)) {
220  	        // We're about to free the document of which *output is a part
221  	        *output = pcmk__xml_copy(NULL, *output);
222  	
223  	    } else if ((*output)->doc == (*current_cib)->doc) {
224  	        // Give them a copy they can free
225  	        *output = pcmk__xml_copy(NULL, *output);
226  	    }
227  	
228  	    pcmk__xml_free(cib_filtered);
229  	    return rc;
230  	}
231  	
232  	/*!
233  	 * \internal
234  	 * \brief Determine whether to perform operations on a scratch copy of the CIB
235  	 *
236  	 * \param[in] op            CIB operation
237  	 * \param[in] section       CIB section
238  	 * \param[in] call_options  CIB call options
239  	 *
240  	 * \return \p true if we should make a copy of the CIB, or \p false otherwise
241  	 */
242  	static bool
243  	should_copy_cib(const char *op, const char *section, int call_options)
244  	{
245  	    if (pcmk__is_set(call_options, cib_dryrun)) {
246  	        // cib_dryrun implies a scratch copy by definition; no side effects
247  	        return true;
248  	    }
249  	
250  	    if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
251  	        /* Commit-transaction must make a copy for atomicity. We must revert to
252  	         * the original CIB if the entire transaction cannot be applied
253  	         * successfully.
254  	         */
255  	        return true;
256  	    }
257  	
258  	    if (pcmk__is_set(call_options, cib_transaction)) {
259  	        /* If cib_transaction is set, then we're in the process of committing a
260  	         * transaction. The commit-transaction request already made a scratch
261  	         * copy, and we're accumulating changes in that copy.
262  	         */
263  	        return false;
264  	    }
265  	
266  	    if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_none)) {
267  	        /* Copying large CIBs accounts for a huge percentage of our CIB usage,
268  	         * and this avoids some of it.
269  	         *
270  	         * @TODO: Is this safe? See discussion at
271  	         * https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
272  	         */
273  	        return false;
274  	    }
275  	
276  	    // Default behavior is to operate on a scratch copy
277  	    return true;
278  	}
279  	
280  	/*!
281  	 * \internal
282  	 * \brief Validate that a new CIB's feature set is not newer than ours
283  	 *
284  	 * Return an error if the new CIB's feature set is newer than ours.
285  	 *
286  	 * \param[in] new_cib  Result CIB after performing operation
287  	 *
288  	 * \return Standard Pacemaker return code
289  	 */
290  	static int
291  	check_new_feature_set(const xmlNode *new_cib)
292  	{
293  	    const char *new_version = pcmk__xe_get(new_cib, PCMK_XA_CRM_FEATURE_SET);
294  	    int rc = pcmk__check_feature_set(new_version);
295  	
296  	    if (rc == pcmk_rc_ok) {
297  	        return pcmk_rc_ok;
298  	    }
299  	
300  	    pcmk__err("Discarding update with feature set %s greater than our own (%s)",
301  	              new_version, CRM_FEATURE_SET);
302  	    return rc;
303  	}
304  	
305  	/*!
306  	 * \internal
307  	 * \brief Validate that a new CIB has a newer version attribute than an old CIB
308  	 *
309  	 * Return an error if the value of the given attribute is higher in the old CIB
310  	 * than in the new CIB.
311  	 *
312  	 * \param[in] attr     Name of version attribute to check
313  	 * \param[in] old_cib  \c PCMK_XE_CIB element before performing operation
314  	 * \param[in] new_cib  \c PCMK_XE_CIB element from result of operation
315  	 * \param[in] request  CIB request
316  	 * \param[in] input    Input data for CIB request
317  	 *
318  	 * \return Standard Pacemaker return code
319  	 *
320  	 * \note \p old_cib only has to contain the top-level \c PCMK_XE_CIB element. It
321  	 *       might not be a full CIB.
322  	 */
323  	static int
324  	check_cib_version_attr(const char *attr, const xmlNode *old_cib,
325  	                       const xmlNode *new_cib, const xmlNode *request,
326  	                       const xmlNode *input)
327  	{
328  	    const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP);
329  	    int old_version = 0;
330  	    int new_version = 0;
331  	
332  	    pcmk__xe_get_int(old_cib, attr, &old_version);
333  	    pcmk__xe_get_int(new_cib, attr, &new_version);
334  	
335  	    if (old_version < new_version) {
336  	        return pcmk_rc_ok;
337  	    }
338  	
339  	    if (old_version == new_version) {
340  	        return pcmk_rc_undetermined;
341  	    }
342  	
343  	    pcmk__err("%s went backwards in %s request: %d -> %d", attr, op,
344  	              old_version, new_version);
345  	    pcmk__log_xml_warn(request, "bad-request");
346  	    pcmk__log_xml_warn(input, "bad-input");
347  	
348  	    return pcmk_rc_old_data;
349  	}
350  	
351  	/*!
352  	 * \internal
353  	 * \brief Validate that a new CIB has newer versions than an old CIB
354  	 *
355  	 * Return an error if:
356  	 * - \c PCMK_XA_ADMIN_EPOCH is newer in the old CIB than in the new CIB; or
357  	 * - The \c PCMK_XA_ADMIN_EPOCH attributes are equal and \c PCMK_XA_EPOCH is
358  	 *   newer in the old CIB than in the new CIB.
359  	 *
360  	 * \param[in] old_cib  \c PCMK_XE_CIB element before performing operation
361  	 * \param[in] new_cib  \c PCMK_XE_CIB element from result of operation
362  	 * \param[in] request  CIB request
363  	 * \param[in] input    Input data for CIB request
364  	 *
365  	 * \return Standard Pacemaker return code
366  	 *
367  	 * \note \p old_cib only has to contain the top-level \c PCMK_XE_CIB element. It
368  	 *       might not be a full CIB.
369  	 */
370  	static int
371  	check_cib_versions(const xmlNode *old_cib, const xmlNode *new_cib,
372  	                   const xmlNode *request, const xmlNode *input)
373  	{
374  	    int rc = check_cib_version_attr(PCMK_XA_ADMIN_EPOCH, old_cib, new_cib,
375  	                                    request, input);
376  	
377  	    if (rc != pcmk_rc_undetermined) {
378  	        return rc;
379  	    }
380  	
381  	    // @TODO Why aren't we checking PCMK_XA_NUM_UPDATES if epochs are equal?
382  	    rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request,
383  	                                input);
384  	    if (rc == pcmk_rc_undetermined) {
385  	        rc = pcmk_rc_ok;
386  	    }
387  	
388  	    return rc;
389  	}
390  	
391  	/*!
392  	 * \internal
393  	 * \brief Set values for update origin host, client, and user in new CIB
394  	 *
395  	 * \param[in,out] new_cib  Result CIB after performing operation
396  	 * \param[in]     request  CIB request (source of origin info)
397  	 *
398  	 * \return Standard Pacemaker return code
399  	 */
400  	static int
401  	set_update_origin(xmlNode *new_cib, const xmlNode *request)
402  	{
403  	    const char *origin = pcmk__xe_get(request, PCMK__XA_SRC);
404  	    const char *client = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME);
405  	    const char *user = pcmk__xe_get(request, PCMK__XA_CIB_USER);
406  	    const char *schema = pcmk__xe_get(new_cib, PCMK_XA_VALIDATE_WITH);
407  	
408  	    if (schema == NULL) {
409  	        return pcmk_rc_cib_corrupt;
410  	    }
411  	
412  	    pcmk__xe_add_last_written(new_cib);
413  	    pcmk__warn_if_schema_deprecated(schema);
414  	
415  	    // pacemaker-1.2 is the earliest schema version that allow these attributes
416  	    if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") < 0) {
417  	        return pcmk_rc_ok;
418  	    }
419  	
420  	    if (origin != NULL) {
421  	        pcmk__xe_set(new_cib, PCMK_XA_UPDATE_ORIGIN, origin);
422  	    } else {
423  	        pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_ORIGIN);
424  	    }
425  	
426  	    if (client != NULL) {
427  	        pcmk__xe_set(new_cib, PCMK_XA_UPDATE_CLIENT, client);
428  	    } else {
429  	        pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_CLIENT);
430  	    }
431  	
432  	    if (user != NULL) {
433  	        pcmk__xe_set(new_cib, PCMK_XA_UPDATE_USER, user);
434  	    } else {
435  	        pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_USER);
436  	    }
437  	
438  	    return pcmk_rc_ok;
439  	}
440  	
441  	int
442  	cib_perform_op(enum cib_variant variant, const char *op, uint32_t call_options,
443  	               cib__op_fn_t fn, const char *section, xmlNode *req,
444  	               xmlNode *input, bool manage_counters, bool *config_changed,
445  	               xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff,
446  	               xmlNode **output)
447  	{
448  	    int rc = pcmk_rc_ok;
449  	
450  	    /* PCMK_XE_CIB element containing version numbers from before the operation.
451  	     * This may or may not point to a full CIB XML tree. Do not free, as this
452  	     * will be used as an alias for another pointer.
453  	     */
454  	    xmlNode *old_versions = NULL;
455  	
456  	    xmlNode *top = NULL;
457  	    xmlNode *working_cib = NULL;
458  	
459  	    const char *user = NULL;
460  	    bool enable_acl = false;
461  	
462  	    pcmk__assert((op != NULL) && (fn != NULL) && (req != NULL)
463  	                 && (config_changed != NULL) && (!*config_changed)
464  	                 && (current_cib != NULL) && (*current_cib != NULL)
465  	                 && (result_cib != NULL) && (*result_cib == NULL)
466  	                 && (diff != NULL) && (*diff == NULL)
467  	                 && (output != NULL) && (*output == NULL));
468  	
469  	    user = pcmk__xe_get(req, PCMK__XA_CIB_USER);
470  	    enable_acl = cib_acl_enabled(*current_cib, user);
471  	
472  	    if (!should_copy_cib(op, section, call_options)) {
473  	        // Make a copy of the top-level element to store version details
474  	        top = pcmk__xe_create(NULL, (const char *) (*current_cib)->name);
475  	        pcmk__xe_copy_attrs(top, *current_cib, pcmk__xaf_none);
476  	        old_versions = top;
477  	
478  	        pcmk__xml_commit_changes((*current_cib)->doc);
479  	        pcmk__xml_doc_set_flags((*current_cib)->doc, pcmk__xf_tracking);
480  	        if (enable_acl) {
481  	            pcmk__enable_acls((*current_cib)->doc, (*current_cib)->doc, user);
482  	        }
483  	
484  	        pcmk__trace("Processing %s for section '%s', user '%s'", op,
485  	                    pcmk__s(section, "(null)"), pcmk__s(user, "(null)"));
486  	        pcmk__log_xml_trace(req, "request");
487  	
488  	        rc = fn(op, call_options, section, req, input, current_cib, output);
489  	
490  	        /* Set working_cib to *current_cib after fn(), in case *current_cib
491  	         * points somewhere else now (for example, after a erase or full-CIB
492  	         * replace op).
493  	         */
494  	        working_cib = *current_cib;
495  	
496  	        /* @TODO Enable tracking and ACLs and calculate changes? If working_cib
497  	         * and *current_cib point to a new object, then change tracking and
498  	         * unpacked ACLs didn't carry over to it.
499  	         */
500  	
501  	    } else {
502  	        working_cib = pcmk__xml_copy(NULL, *current_cib);
503  	        old_versions = *current_cib;
504  	
505  	        pcmk__xml_doc_set_flags(working_cib->doc, pcmk__xf_tracking);
506  	        if (enable_acl) {
507  	            pcmk__enable_acls((*current_cib)->doc, working_cib->doc, user);
508  	        }
509  	
510  	        pcmk__trace("Processing %s for section '%s', user '%s'", op,
511  	                    pcmk__s(section, "(null)"), pcmk__s(user, "(null)"));
512  	        pcmk__log_xml_trace(req, "request");
513  	
514  	        rc = fn(op, call_options, section, req, input, &working_cib, output);
515  	
516  	        /* @TODO This appears to be a hack to determine whether working_cib
517  	         * points to a new object now, without saving the old pointer (which may
518  	         * be invalid now) for comparison. Confirm this, and check more clearly.
519  	         */
520  	        if (!pcmk__xml_doc_all_flags_set(working_cib->doc, pcmk__xf_tracking)) {
521  	            pcmk__trace("Inferring changes after %s op", op);
522  	            pcmk__xml_commit_changes(working_cib->doc);
523  	            if (enable_acl) {
524  	                pcmk__enable_acls((*current_cib)->doc, working_cib->doc, user);
525  	            }
526  	            pcmk__xml_mark_changes(*current_cib, working_cib);
527  	        }
528  	
529  	        pcmk__assert(*current_cib != working_cib);
530  	    }
531  	
532  	    // Allow ourselves to make any additional necessary changes
533  	    xml_acl_disable(working_cib);
534  	
535  	    if (rc != pcmk_rc_ok) {
536  	        goto done;
537  	    }
538  	
539  	    if (working_cib == NULL) {
540  	        rc = EINVAL;
541  	        goto done;
542  	    }
543  	
544  	    if (xml_acl_denied(working_cib)) {
545  	        pcmk__trace("ACL rejected part or all of the proposed changes");
546  	        rc = EACCES;
547  	        goto done;
548  	    }
549  	
550  	    /* If the CIB is from a file, we don't need to check that the feature set is
551  	     * supported.  All we care about in that case is the schema version, which
552  	     * is checked elsewhere.
553  	     */
554  	    if (variant != cib_file) {
555  	        rc = check_new_feature_set(working_cib);
556  	        if (rc != pcmk_rc_ok) {
557  	            goto done;
558  	        }
559  	    }
560  	
561  	    rc = check_cib_versions(old_versions, working_cib, req, input);
562  	
563  	    pcmk__strip_xml_text(working_cib);
564  	
565  	    /* If we didn't make a copy, the diff will only be accurate for the
566  	     * top-level PCMK_XE_CIB element
567  	     */
568  	    *diff = xml_create_patchset(0, old_versions, working_cib, config_changed,
569  	                                manage_counters);
570  	
571  	    /* pcmk__xml_commit_changes() resets document private data, so call it even
572  	     * if there were no changes.
573  	     */
574  	    pcmk__xml_commit_changes(working_cib->doc);
575  	
576  	    if (*diff == NULL) {
577  	        goto done;
578  	    }
579  	
580  	    pcmk__log_xml_patchset(LOG_INFO, *diff);
581  	
582  	    /* working_cib must not be modified after this point, except for the
583  	     * attributes for which pcmk__xa_filterable() returns true
584  	     */
585  	
586  	    if (*config_changed && !pcmk__is_set(call_options, cib_no_mtime)) {
587  	        rc = set_update_origin(working_cib, req);
588  	        if (rc != pcmk_rc_ok) {
589  	            goto done;
590  	        }
591  	    }
592  	
593  	    // Skip validation for status-only updates, since we allow anything there
594  	    if ((rc == pcmk_rc_ok)
595  	        && !pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)
596  	        && !pcmk__configured_schema_validates(working_cib)) {
597  	
598  	        rc = pcmk_rc_schema_validation;
599  	    }
600  	
601  	done:
602  	    *result_cib = working_cib;
603  	
604  	    /* @TODO This may not work correctly when !should_copy_cib(), since we don't
605  	     * keep the original CIB.
606  	     */
607  	    if ((rc != pcmk_rc_ok) && cib_acl_enabled(old_versions, user)
608  	        && xml_acl_filtered_copy(user, old_versions, working_cib, result_cib)) {
609  	
610  	        if (*result_cib == NULL) {
611  	            pcmk__debug("Pre-filtered the entire cib result");
612  	        }
613  	        pcmk__xml_free(working_cib);
614  	    }
615  	
616  	    pcmk__xml_free(top);
617  	    pcmk__trace("Done");
618  	    return rc;
619  	}
620  	
621  	int
622  	cib__create_op(cib_t *cib, const char *op, const char *host,
623  	               const char *section, xmlNode *data, int call_options,
624  	               const char *user_name, const char *client_name,
625  	               xmlNode **op_msg)
626  	{
627  	    CRM_CHECK((cib != NULL) && (op_msg != NULL), return EPROTO);
628  	
629  	    *op_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND);
630  	
631  	    cib->call_id++;
632  	    if (cib->call_id < 1) {
633  	        cib->call_id = 1;
634  	    }
635  	
636  	    pcmk__xe_set(*op_msg, PCMK__XA_T, PCMK__VALUE_CIB);
637  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_OP, op);
638  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_HOST, host);
639  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_SECTION, section);
640  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_USER, user_name);
641  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_CLIENTNAME, client_name);
642  	    pcmk__xe_set_int(*op_msg, PCMK__XA_CIB_CALLID, cib->call_id);
643  	
644  	    pcmk__trace("Sending call options: %.8lx, %d", (long) call_options,
645  	                call_options);
646  	    pcmk__xe_set_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options);
647  	
648  	    if (data != NULL) {
649  	        xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA);
650  	
651  	        pcmk__xml_copy(wrapper, data);
652  	    }
653  	
654  	    return pcmk_rc_ok;
655  	}
656  	
657  	/*!
658  	 * \internal
659  	 * \brief Check whether a CIB request is supported in a transaction
660  	 *
661  	 * \param[in] request  CIB request
662  	 *
663  	 * \return Standard Pacemaker return code
664  	 */
665  	static int
666  	validate_transaction_request(const xmlNode *request)
667  	{
668  	    const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP);
669  	    const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST);
670  	    const cib__operation_t *operation = NULL;
671  	    int rc = cib__get_operation(op, &operation);
672  	
673  	    if (rc != pcmk_rc_ok) {
674  	        // cib__get_operation() logs error
675  	        return rc;
676  	    }
677  	
678  	    if (!pcmk__is_set(operation->flags, cib__op_attr_transaction)) {
679  	        pcmk__err("Operation %s is not supported in CIB transactions", op);
680  	        return EOPNOTSUPP;
681  	    }
682  	
683  	    if (host != NULL) {
684  	        pcmk__err("Operation targeting a specific node (%s) is not supported "
685  	                  "in a CIB transaction",
686  	                  host);
687  	        return EOPNOTSUPP;
688  	    }
689  	    return pcmk_rc_ok;
690  	}
691  	
692  	/*!
693  	 * \internal
694  	 * \brief Append a CIB request to a CIB transaction
695  	 *
696  	 * \param[in,out] cib      CIB client whose transaction to extend
697  	 * \param[in,out] request  Request to add to transaction
698  	 *
699  	 * \return Standard Pacemaker return code
700  	 */
701  	int
702  	cib__extend_transaction(cib_t *cib, xmlNode *request)
703  	{
704  	    const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP);
705  	    const char *client_id = NULL;
706  	    int rc = pcmk_rc_ok;
707  	
708  	    pcmk__assert((cib != NULL) && (request != NULL));
709  	
710  	    rc = validate_transaction_request(request);
711  	
712  	    if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
713  	        rc = pcmk_rc_no_transaction;
714  	    }
715  	
716  	    if (rc == pcmk_rc_ok) {
717  	        pcmk__xml_copy(cib->transaction, request);
718  	        return pcmk_rc_ok;
719  	    }
720  	
721  	    cib->cmds->client_id(cib, NULL, &client_id);
722  	
723  	    pcmk__err("Failed to add '%s' operation to transaction for client %s: %s",
724  	              op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
725  	    pcmk__log_xml_info(request, "failed");
726  	
727  	    return rc;
728  	}
729  	
730  	void
731  	cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
732  	{
733  	    xmlNode *output = NULL;
734  	    cib_callback_client_t *blob = NULL;
735  	
736  	    if (msg != NULL) {
737  	        xmlNode *wrapper = NULL;
738  	
739  	        pcmk__xe_get_int(msg, PCMK__XA_CIB_RC, &rc);
740  	        pcmk__xe_get_int(msg, PCMK__XA_CIB_CALLID, &call_id);
741  	        wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL);
742  	        output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
743  	    }
744  	
745  	    blob = cib__lookup_id(call_id);
746  	
747  	    if (blob == NULL) {
748  	        pcmk__trace("No callback found for call %d", call_id);
749  	    }
750  	
751  	    if (cib == NULL) {
752  	        pcmk__debug("No cib object supplied");
753  	    }
754  	
755  	    if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
756  	        pcmk__trace("Invoking callback %s for call %d",
757  	                    pcmk__s(blob->id, "without ID"), call_id);
758  	        blob->callback(msg, call_id, rc, output, blob->user_data);
759  	
760  	    } else if ((cib != NULL) && (rc != pcmk_ok)) {
761  	        pcmk__warn("CIB command failed: %s", pcmk_strerror(rc));
762  	        pcmk__log_xml_debug(msg, "Failed CIB Update");
763  	    }
764  	
765  	    /* This may free user_data, so do it after the callback */
766  	    if (blob) {
767  	        remove_cib_op_callback(call_id, FALSE);
768  	    }
769  	
770  	    pcmk__trace("OP callback activated for %d", call_id);
771  	}
772  	
773  	void
774  	cib_native_notify(gpointer data, gpointer user_data)
775  	{
776  	    xmlNode *msg = user_data;
777  	    cib_notify_client_t *entry = data;
778  	    const char *event = NULL;
779  	
780  	    if (msg == NULL) {
781  	        pcmk__warn("Skipping callback - NULL message");
782  	        return;
783  	    }
784  	
785  	    event = pcmk__xe_get(msg, PCMK__XA_SUBT);
786  	
787  	    if (entry == NULL) {
788  	        pcmk__warn("Skipping callback - NULL callback client");
789  	        return;
790  	
791  	    } else if (entry->callback == NULL) {
792  	        pcmk__warn("Skipping callback - NULL callback");
793  	        return;
794  	
795  	    } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
796  	        pcmk__trace("Skipping callback - event mismatch %p/%s vs. %s", entry,
797  	                    entry->event, event);
798  	        return;
799  	    }
800  	
801  	    pcmk__trace("Invoking callback for %p/%s event...", entry, event);
802  	    entry->callback(event, msg);
803  	    pcmk__trace("Callback invoked...");
804  	}
805  	
806  	int
807  	cib_internal_op(cib_t * cib, const char *op, const char *host,
808  	                const char *section, xmlNode * data,
809  	                xmlNode ** output_data, int call_options, const char *user_name)
810  	{
811  	    /* Note: *output_data gets set only for create and query requests. There are
812  	     * a lot of opportunities to clean up, clarify, check/enforce things, etc.
813  	     */
814  	    int (*delegate)(cib_t *cib, const char *op, const char *host,
815  	                    const char *section, xmlNode *data, xmlNode **output_data,
816  	                    int call_options, const char *user_name) = NULL;
817  	
818  	    if (cib == NULL) {
819  	        return -EINVAL;
820  	    }
821  	
822  	    delegate = cib->delegate_fn;
823  	    if (delegate == NULL) {
824  	        return -EPROTONOSUPPORT;
825  	    }
826  	    if (user_name == NULL) {
827  	        user_name = getenv("CIB_user");
828  	    }
829  	    return delegate(cib, op, host, section, data, output_data, call_options, user_name);
830  	}
831  	
832  	/*!
833  	 * \brief Apply a CIB update patch to a given CIB
834  	 *
835  	 * \param[in]  event   CIB update patch
836  	 * \param[in]  input   CIB to patch
837  	 * \param[out] output  Resulting CIB after patch
838  	 * \param[in]  level   Log the patch at this log level (unless LOG_CRIT)
839  	 *
840  	 * \return Legacy Pacemaker return code
841  	 * \note sbd calls this function
842  	 */
843  	int
844  	cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
845  	                      int level)
846  	{
847  	    int rc = pcmk_err_generic;
848  	
849  	    xmlNode *wrapper = NULL;
850  	    xmlNode *diff = NULL;
851  	
(1) Event path: Condition "event != NULL", taking true branch.
(2) Event path: Condition "input != NULL", taking true branch.
(3) Event path: Condition "output != NULL", taking true branch.
852  	    pcmk__assert((event != NULL) && (input != NULL) && (output != NULL));
853  	
854  	    pcmk__xe_get_int(event, PCMK__XA_CIB_RC, &rc);
855  	    wrapper = pcmk__xe_first_child(event, PCMK__XE_CIB_UPDATE_RESULT, NULL,
856  	                                   NULL);
857  	    diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
858  	
(4) Event path: Condition "rc < 0", taking false branch.
(5) Event path: Condition "diff == NULL", taking false branch.
859  	    if ((rc < pcmk_ok) || (diff == NULL)) {
860  	        return rc;
861  	    }
862  	
(6) Event path: Condition "level > 2", taking true branch.
863  	    if (level > LOG_CRIT) {
(7) Event path: Switch case value "255".
(8) Event path: Breaking from switch.
864  	        pcmk__log_xml_patchset(level, diff);
865  	    }
866  	
(9) Event path: Condition "input == NULL", taking false branch.
867  	    if (input == NULL) {
868  	        return rc;
869  	    }
870  	
(10) Event path: Condition "*output != input", taking true branch.
871  	    if (*output != input) {
872  	        pcmk__xml_free(*output);
873  	        *output = pcmk__xml_copy(NULL, input);
874  	    }
875  	
876  	    rc = cib__process_apply_patch(NULL, cib_none, NULL, event, diff, output,
877  	                                  NULL);
878  	    rc = pcmk_rc2legacy(rc);
(11) Event path: Condition "rc == 0", taking false branch.
879  	    if (rc == pcmk_ok) {
880  	        return pcmk_ok;
881  	    }
882  	
(12) Event path: Switch case default.
(13) Event path: Condition "trace_cs == NULL", taking true branch.
(14) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(15) Event path: Breaking from switch.
883  	    pcmk__debug("Update didn't apply: %s (%d)", pcmk_strerror(rc), rc);
884  	
(16) Event path: Condition "rc == -205", taking false branch.
885  	    if (rc == -pcmk_err_old_data) {
886  	        // Mask this error, since it means we already have the supplied update
887  	        return pcmk_ok;
888  	    }
889  	
890  	    // Some other error
CID (unavailable; MK=46f1f22811675aa3ef01982fa82bf4d1) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(17) Event assign_union_field: The union field "in" of "_pp" is written.
(18) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
891  	    g_clear_pointer(output, pcmk__xml_free);
892  	    return rc;
893  	}
894  	
895  	#define log_signon_query_err(out, fmt, args...) do {    \
896  	        if (out != NULL) {                              \
897  	            out->err(out, fmt, ##args);                 \
898  	        } else {                                        \
899  	            pcmk__err(fmt, ##args);                     \
900  	        }                                               \
901  	    } while (0)
902  	
903  	int
904  	cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
905  	{
906  	    int rc = pcmk_rc_ok;
907  	    cib_t *cib_conn = NULL;
908  	
909  	    pcmk__assert(cib_object != NULL);
910  	
911  	    if (cib == NULL) {
912  	        cib_conn = cib_new();
913  	    } else {
914  	        if (*cib == NULL) {
915  	            *cib = cib_new();
916  	        }
917  	        cib_conn = *cib;
918  	    }
919  	
920  	    if (cib_conn == NULL) {
921  	        return ENOMEM;
922  	    }
923  	
924  	    if (cib_conn->state == cib_disconnected) {
925  	        rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
926  	        rc = pcmk_legacy2rc(rc);
927  	    }
928  	
929  	    if (rc != pcmk_rc_ok) {
930  	        log_signon_query_err(out, "Could not connect to the CIB: %s",
931  	                             pcmk_rc_str(rc));
932  	        goto done;
933  	    }
934  	
935  	    if (out != NULL) {
936  	        out->transient(out, "Querying CIB...");
937  	    }
938  	    rc = cib_conn->cmds->query(cib_conn, NULL, cib_object, cib_sync_call);
939  	    rc = pcmk_legacy2rc(rc);
940  	
941  	    if (rc != pcmk_rc_ok) {
942  	        log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
943  	    }
944  	
945  	done:
946  	    if (cib == NULL) {
947  	        cib__clean_up_connection(&cib_conn);
948  	    }
949  	
950  	    if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
951  	        return pcmk_rc_no_input;
952  	    }
953  	    return rc;
954  	}
955  	
956  	/*!
957  	 * \internal
958  	 * \brief Create a new CIB connection object and connect to the CIB API
959  	 *
960  	 * This function attempts to connect up to 5 times.
961  	 *
962  	 * \param[out] cib  Where to store CIB connection object
963  	 *
964  	 * \return Standard Pacemaker return code
965  	 *
966  	 * \note The caller is responsible for signing off and freeing the newly
967  	 *       allocated CIB connection object using the \c signoff() method and
968  	 *       \c cib_delete().
969  	 */
970  	int
971  	cib__create_signon(cib_t **cib)
972  	{
973  	    static const int attempts = 5;
974  	    int rc = pcmk_rc_ok;
975  	
976  	    pcmk__assert((cib != NULL) && (*cib == NULL));
977  	
978  	    *cib = cib_new();
979  	    if (*cib == NULL) {
980  	        return ENOMEM;
981  	    }
982  	
983  	    pcmk__trace("Attempting connection to CIB API (up to %d time%s)", attempts,
984  	                pcmk__plural_s(attempts));
985  	
986  	    for (int remaining = attempts - 1; remaining >= 0; --remaining) {
987  	        rc = (*cib)->cmds->signon(*cib, crm_system_name, cib_command);
988  	
989  	        if ((rc == pcmk_ok)
990  	            || (remaining == 0)
991  	            || ((errno != EAGAIN) && (errno != EALREADY))) {
992  	            break;
993  	        }
994  	
995  	        // Retry after soft error (interrupted by signal, etc.)
996  	        pcmk__sleep_ms((attempts - remaining) * 500);
997  	        pcmk__debug("Re-attempting connection to CIB manager (%d attempt%s "
998  	                    "remaining)",
999  	                    remaining, pcmk__plural_s(remaining));
1000 	    }
1001 	
1002 	    rc = pcmk_legacy2rc(rc);
1003 	    if (rc != pcmk_rc_ok) {
1004 	        cib__clean_up_connection(cib);
1005 	    }
1006 	
1007 	    return rc;
1008 	}
1009 	
1010 	int
1011 	cib__clean_up_connection(cib_t **cib)
1012 	{
1013 	    int rc;
1014 	
1015 	    if (*cib == NULL) {
1016 	        return pcmk_rc_ok;
1017 	    }
1018 	
1019 	    rc = (*cib)->cmds->signoff(*cib);
1020 	    cib_delete(*cib);
1021 	    *cib = NULL;
1022 	    return pcmk_legacy2rc(rc);
1023 	}
1024