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  	/*!
173  	 * \internal
174  	 * \brief Get input data from a CIB request
175  	 *
176  	 * \param[in] request  CIB request XML
177  	 */
178  	static xmlNode *
179  	get_op_input(const xmlNode *request)
180  	{
181  	    xmlNode *wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA,
182  	                                            NULL, NULL);
183  	
184  	    return pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
185  	}
186  	
187  	int
188  	cib__perform_query(cib__op_fn_t fn, xmlNode *req, xmlNode **current_cib,
189  	                   xmlNode **output)
190  	{
191  	    int rc = pcmk_rc_ok;
192  	    const char *op = NULL;
193  	    const char *section = NULL;
194  	    const char *user = NULL;
195  	    uint32_t call_options = cib_none;
196  	    xmlNode *input = NULL;
197  	
198  	    xmlNode *cib = NULL;
199  	    xmlNode *cib_filtered = NULL;
200  	    const xmlNode *saved_cib = NULL;
201  	    const xmlDoc *saved_doc = NULL;
202  	
203  	    pcmk__assert((fn != NULL) && (req != NULL)
204  	                 && (current_cib != NULL) && (*current_cib != NULL)
205  	                 && (output != NULL) && (*output == NULL));
206  	
207  	    op = pcmk__xe_get(req, PCMK__XA_CIB_OP);
208  	    section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION);
209  	    user = pcmk__xe_get(req, PCMK__XA_CIB_USER);
210  	    pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none);
211  	
212  	    input = get_op_input(req);
213  	    cib = *current_cib;
214  	
215  	    if (cib_acl_enabled(*current_cib, user)
216  	        && xml_acl_filtered_copy(user, *current_cib, *current_cib,
217  	                                 &cib_filtered)) {
218  	
219  	        if (cib_filtered == NULL) {
220  	            pcmk__debug("Pre-filtered the entire cib");
221  	            return EACCES;
222  	        }
223  	        cib = cib_filtered;
224  	        pcmk__log_xml_trace(cib, "filtered");
225  	    }
226  	
227  	    pcmk__trace("Processing %s for section '%s', user '%s'", op,
228  	                pcmk__s(section, "(null)"), pcmk__s(user, "(null)"));
229  	    pcmk__log_xml_trace(req, "request");
230  	
231  	    saved_cib = cib;
232  	    saved_doc = cib->doc;
233  	
234  	    rc = fn(req, input, &cib, output);
235  	
236  	    /* Sanity check: op should be read-only (but this does not check children,
237  	     * attributes, or private data)
238  	     */
239  	    pcmk__assert((cib == saved_cib) && (cib->doc == saved_doc)
240  	                 && (cib == xmlDocGetRootElement(cib->doc)));
241  	
242  	    if (*output == NULL) {
243  	        // Do nothing
244  	
245  	    } else if (cib_filtered == *output) {
246  	        // Let them have this copy
247  	        cib_filtered = NULL;
248  	
249  	    } else if (*output == *current_cib) {
250  	        // They already know not to free it
251  	
252  	    } else if ((cib_filtered != NULL)
253  	               && ((*output)->doc == cib_filtered->doc)) {
254  	        // We're about to free the document of which *output is a part
255  	        *output = pcmk__xml_copy(NULL, *output);
256  	
257  	    } else if ((*output)->doc == (*current_cib)->doc) {
258  	        // Give them a copy they can free
259  	        *output = pcmk__xml_copy(NULL, *output);
260  	    }
261  	
262  	    pcmk__xml_free(cib_filtered);
263  	    return rc;
264  	}
265  	
266  	/*!
267  	 * \internal
268  	 * \brief Determine whether to perform operations on a scratch copy of the CIB
269  	 *
270  	 * \param[in] op            CIB operation
271  	 * \param[in] section       CIB section
272  	 * \param[in] call_options  CIB call options
273  	 *
274  	 * \return \p true if we should make a copy of the CIB, or \p false otherwise
275  	 */
276  	static bool
277  	should_copy_cib(const char *op, const char *section, int call_options)
278  	{
279  	    if (pcmk__is_set(call_options, cib_dryrun)) {
280  	        // cib_dryrun implies a scratch copy by definition; no side effects
281  	        return true;
282  	    }
283  	
284  	    if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
285  	        /* Commit-transaction must make a copy for atomicity. We must revert to
286  	         * the original CIB if the entire transaction cannot be applied
287  	         * successfully.
288  	         */
289  	        return true;
290  	    }
291  	
292  	    if (pcmk__is_set(call_options, cib_transaction)) {
293  	        /* If cib_transaction is set, then we're in the process of committing a
294  	         * transaction. The commit-transaction request already made a scratch
295  	         * copy, and we're accumulating changes in that copy.
296  	         */
297  	        return false;
298  	    }
299  	
300  	    if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_none)) {
301  	        /* Copying large CIBs accounts for a huge percentage of our CIB usage,
302  	         * and this avoids some of it.
303  	         *
304  	         * @TODO: Is this safe? See discussion at
305  	         * https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
306  	         */
307  	        return false;
308  	    }
309  	
310  	    // Default behavior is to operate on a scratch copy
311  	    return true;
312  	}
313  	
314  	/*!
315  	 * \internal
316  	 * \brief Validate that a new CIB's feature set is not newer than ours
317  	 *
318  	 * Return an error if the new CIB's feature set is newer than ours.
319  	 *
320  	 * \param[in] new_cib  Result CIB after performing operation
321  	 *
322  	 * \return Standard Pacemaker return code
323  	 */
324  	static int
325  	check_new_feature_set(const xmlNode *new_cib)
326  	{
327  	    const char *new_version = pcmk__xe_get(new_cib, PCMK_XA_CRM_FEATURE_SET);
328  	    int rc = pcmk__check_feature_set(new_version);
329  	
330  	    if (rc == pcmk_rc_ok) {
331  	        return pcmk_rc_ok;
332  	    }
333  	
334  	    pcmk__err("Discarding update with feature set %s greater than our own (%s)",
335  	              new_version, CRM_FEATURE_SET);
336  	    return rc;
337  	}
338  	
339  	/*!
340  	 * \internal
341  	 * \brief Validate that a new CIB has a newer version attribute than an old CIB
342  	 *
343  	 * Return an error if the value of the given attribute is higher in the old CIB
344  	 * than in the new CIB.
345  	 *
346  	 * \param[in] attr     Name of version attribute to check
347  	 * \param[in] old_cib  \c PCMK_XE_CIB element before performing operation
348  	 * \param[in] new_cib  \c PCMK_XE_CIB element from result of operation
349  	 * \param[in] request  CIB request
350  	 * \param[in] input    Input data for CIB request
351  	 *
352  	 * \return Standard Pacemaker return code
353  	 *
354  	 * \note \p old_cib only has to contain the top-level \c PCMK_XE_CIB element. It
355  	 *       might not be a full CIB.
356  	 */
357  	static int
358  	check_cib_version_attr(const char *attr, const xmlNode *old_cib,
359  	                       const xmlNode *new_cib, const xmlNode *request,
360  	                       const xmlNode *input)
361  	{
362  	    const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP);
363  	    int old_version = 0;
364  	    int new_version = 0;
365  	
366  	    pcmk__xe_get_int(old_cib, attr, &old_version);
367  	    pcmk__xe_get_int(new_cib, attr, &new_version);
368  	
369  	    if (old_version < new_version) {
370  	        return pcmk_rc_ok;
371  	    }
372  	
373  	    if (old_version == new_version) {
374  	        return pcmk_rc_undetermined;
375  	    }
376  	
377  	    pcmk__err("%s went backwards in %s request: %d -> %d", attr, op,
378  	              old_version, new_version);
379  	    pcmk__log_xml_warn(request, "bad-request");
380  	    pcmk__log_xml_warn(input, "bad-input");
381  	
382  	    return pcmk_rc_old_data;
383  	}
384  	
385  	/*!
386  	 * \internal
387  	 * \brief Validate that a new CIB has newer versions than an old CIB
388  	 *
389  	 * Return an error if:
390  	 * - \c PCMK_XA_ADMIN_EPOCH is newer in the old CIB than in the new CIB; or
391  	 * - The \c PCMK_XA_ADMIN_EPOCH attributes are equal and \c PCMK_XA_EPOCH is
392  	 *   newer in the old CIB than in the new CIB.
393  	 *
394  	 * \param[in] old_cib  \c PCMK_XE_CIB element before performing operation
395  	 * \param[in] new_cib  \c PCMK_XE_CIB element from result of operation
396  	 * \param[in] request  CIB request
397  	 * \param[in] input    Input data for CIB request
398  	 *
399  	 * \return Standard Pacemaker return code
400  	 *
401  	 * \note \p old_cib only has to contain the top-level \c PCMK_XE_CIB element. It
402  	 *       might not be a full CIB.
403  	 */
404  	static int
405  	check_cib_versions(const xmlNode *old_cib, const xmlNode *new_cib,
406  	                   const xmlNode *request, const xmlNode *input)
407  	{
408  	    int rc = check_cib_version_attr(PCMK_XA_ADMIN_EPOCH, old_cib, new_cib,
409  	                                    request, input);
410  	
411  	    if (rc != pcmk_rc_undetermined) {
412  	        return rc;
413  	    }
414  	
415  	    // @TODO Why aren't we checking PCMK_XA_NUM_UPDATES if epochs are equal?
416  	    rc = check_cib_version_attr(PCMK_XA_EPOCH, old_cib, new_cib, request,
417  	                                input);
418  	    if (rc == pcmk_rc_undetermined) {
419  	        rc = pcmk_rc_ok;
420  	    }
421  	
422  	    return rc;
423  	}
424  	
425  	/*!
426  	 * \internal
427  	 * \brief Set values for update origin host, client, and user in new CIB
428  	 *
429  	 * \param[in,out] new_cib  Result CIB after performing operation
430  	 * \param[in]     request  CIB request (source of origin info)
431  	 *
432  	 * \return Standard Pacemaker return code
433  	 */
434  	static int
435  	set_update_origin(xmlNode *new_cib, const xmlNode *request)
436  	{
437  	    const char *origin = pcmk__xe_get(request, PCMK__XA_SRC);
438  	    const char *client = pcmk__xe_get(request, PCMK__XA_CIB_CLIENTNAME);
439  	    const char *user = pcmk__xe_get(request, PCMK__XA_CIB_USER);
440  	    const char *schema = pcmk__xe_get(new_cib, PCMK_XA_VALIDATE_WITH);
441  	
442  	    if (schema == NULL) {
443  	        return pcmk_rc_cib_corrupt;
444  	    }
445  	
446  	    pcmk__xe_add_last_written(new_cib);
447  	    pcmk__warn_if_schema_deprecated(schema);
448  	
449  	    // pacemaker-1.2 is the earliest schema version that allow these attributes
450  	    if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") < 0) {
451  	        return pcmk_rc_ok;
452  	    }
453  	
454  	    if (origin != NULL) {
455  	        pcmk__xe_set(new_cib, PCMK_XA_UPDATE_ORIGIN, origin);
456  	    } else {
457  	        pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_ORIGIN);
458  	    }
459  	
460  	    if (client != NULL) {
461  	        pcmk__xe_set(new_cib, PCMK_XA_UPDATE_CLIENT, client);
462  	    } else {
463  	        pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_CLIENT);
464  	    }
465  	
466  	    if (user != NULL) {
467  	        pcmk__xe_set(new_cib, PCMK_XA_UPDATE_USER, user);
468  	    } else {
469  	        pcmk__xe_remove_attr(new_cib, PCMK_XA_UPDATE_USER);
470  	    }
471  	
472  	    return pcmk_rc_ok;
473  	}
474  	
475  	int
476  	cib_perform_op(enum cib_variant variant, cib__op_fn_t fn, xmlNode *req,
477  	               bool *config_changed, xmlNode **cib, xmlNode **diff,
478  	               xmlNode **output)
479  	{
480  	    int rc = pcmk_rc_ok;
481  	
482  	    const char *op = NULL;
483  	    const char *section = NULL;
484  	    const char *user = NULL;
485  	    uint32_t call_options = cib_none;
486  	    xmlNode *input = NULL;
487  	    bool enable_acl = false;
488  	    bool manage_version = true;
489  	
490  	    /* PCMK_XE_CIB element containing version numbers from before the operation.
491  	     * This may or may not point to a full CIB XML tree. Do not free, as this
492  	     * will be used as an alias for another pointer.
493  	     */
494  	    xmlNode *old_versions = NULL;
495  	
496  	    xmlNode *top = NULL;
497  	    const xmlDoc *saved_doc = NULL;
498  	
499  	    pcmk__assert((fn != NULL) && (req != NULL)
500  	                 && (config_changed != NULL) && (!*config_changed)
501  	                 && (cib != NULL) && (*cib != NULL)
502  	                 && (diff != NULL) && (*diff == NULL)
503  	                 && (output != NULL) && (*output == NULL));
504  	
505  	    op = pcmk__xe_get(req, PCMK__XA_CIB_OP);
506  	    section = pcmk__xe_get(req, PCMK__XA_CIB_SECTION);
507  	    user = pcmk__xe_get(req, PCMK__XA_CIB_USER);
508  	    pcmk__xe_get_flags(req, PCMK__XA_CIB_CALLOPT, &call_options, cib_none);
509  	
510  	    input = get_op_input(req);
511  	    enable_acl = cib_acl_enabled(*cib, user);
512  	
513  	    pcmk__trace("Processing %s for section '%s', user '%s'", op,
514  	                pcmk__s(section, "(null)"), pcmk__s(user, "(null)"));
515  	    pcmk__log_xml_trace(req, "request");
516  	
517  	    if (!should_copy_cib(op, section, call_options)) {
518  	        // Make a copy of the top-level element to store version details
519  	        top = pcmk__xe_create(NULL, (const char *) (*cib)->name);
520  	        pcmk__xe_copy_attrs(top, *cib, pcmk__xaf_none);
521  	        old_versions = top;
522  	
523  	    } else {
524  	        old_versions = *cib;
525  	        *cib = pcmk__xml_copy(NULL, *cib);
526  	    }
527  	
528  	    if (pcmk__is_set(call_options, cib_transaction)) {
529  	        /* This is a request within a transaction, so don't commit changes yet.
530  	         * On success, we'll commit when we finish performing the whole commit-
531  	         * transaction operation. The tracking flag is already set, and ACLs
532  	         * have already been unpacked and applied.
533  	         */
534  	        pcmk__assert(pcmk__xml_doc_all_flags_set((*cib)->doc,
535  	                                                 pcmk__xf_tracking));
536  	
537  	    } else {
538  	        pcmk__xml_commit_changes((*cib)->doc);
539  	        pcmk__xml_doc_set_flags((*cib)->doc, pcmk__xf_tracking);
540  	        if (enable_acl) {
541  	            pcmk__enable_acls((*cib)->doc, (*cib)->doc, user);
542  	        }
543  	    }
544  	
545  	    saved_doc = (*cib)->doc;
546  	
547  	    rc = fn(req, input, cib, output);
548  	    if (rc != pcmk_rc_ok) {
549  	        goto done;
550  	    }
551  	
552  	    if (*cib == NULL) {
553  	        rc = EINVAL;
554  	        goto done;
555  	    }
556  	
557  	    // Sanity check: doc should not change and *cib must be the root
558  	    pcmk__assert(((*cib)->doc == saved_doc)
559  	                 && (*cib == xmlDocGetRootElement((*cib)->doc)));
560  	
561  	    // Tracking flag should still be set after operation
562  	    pcmk__assert(pcmk__xml_doc_all_flags_set((*cib)->doc, pcmk__xf_tracking));
563  	
564  	    // Allow ourselves to make any additional necessary changes
565  	    xml_acl_disable(*cib);
566  	
567  	    if (xml_acl_denied(*cib)) {
568  	        pcmk__trace("ACL rejected part or all of the proposed changes");
569  	        rc = EACCES;
570  	        goto done;
571  	    }
572  	
573  	    /* If the CIB is from a file, we don't need to check that the feature set is
574  	     * supported.  All we care about in that case is the schema version, which
575  	     * is checked elsewhere.
576  	     */
577  	    if (variant != cib_file) {
578  	        rc = check_new_feature_set(*cib);
579  	        if (rc != pcmk_rc_ok) {
580  	            goto done;
581  	        }
582  	    }
583  	
584  	    rc = check_cib_versions(old_versions, *cib, req, input);
585  	
586  	    pcmk__strip_xml_text(*cib);
587  	
588  	    if (pcmk__xe_attr_is_true(req, PCMK__XA_CIB_UPDATE)) {
589  	        /* This is a replace operation as a reply to a sync request. Keep
590  	         * whatever versions are in the received CIB.
591  	         */
592  	        manage_version = false;
593  	    }
594  	
595  	    /* If we didn't make a copy, the diff will only be accurate for the
596  	     * top-level PCMK_XE_CIB element
597  	     */
598  	    *diff = xml_create_patchset(0, old_versions, *cib, config_changed,
599  	                                manage_version);
600  	
601  	    /* pcmk__xml_commit_changes() resets document private data, so call it even
602  	     * if there were no changes. If this request is part of a transaction, we'll
603  	     * commit changes later, as part of the commit-transaction operation.
604  	     */
605  	    if (!pcmk__is_set(call_options, cib_transaction)) {
606  	        pcmk__xml_commit_changes((*cib)->doc);
607  	    }
608  	
609  	    if (*diff == NULL) {
610  	        goto done;
611  	    }
612  	
613  	    /* If this request is within a transaction, *diff is the cumulative set of
614  	     * changes so far, since we don't commit changes between requests.
615  	     */
616  	    pcmk__log_xml_patchset(LOG_INFO, *diff);
617  	
618  	    /* *cib must not be modified after this point, except for the attributes for
619  	     * which pcmk__xa_filterable() returns true
620  	     */
621  	
622  	    if (*config_changed && !pcmk__is_set(call_options, cib_no_mtime)) {
623  	        rc = set_update_origin(*cib, req);
624  	        if (rc != pcmk_rc_ok) {
625  	            goto done;
626  	        }
627  	    }
628  	
629  	    // Skip validation for status-only updates, since we allow anything there
630  	    if ((rc == pcmk_rc_ok)
631  	        && !pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)
632  	        && !pcmk__configured_schema_validates(*cib)) {
633  	
634  	        rc = pcmk_rc_schema_validation;
635  	    }
636  	
637  	done:
638  	    /* @TODO This may not work correctly when !should_copy_cib(), since we don't
639  	     * keep the original CIB.
640  	     */
641  	    if ((rc != pcmk_rc_ok) && cib_acl_enabled(old_versions, user)) {
642  	        xmlNode *saved_cib = *cib;
643  	
644  	        if (xml_acl_filtered_copy(user, old_versions, *cib, cib)) {
645  	            if (*cib == NULL) {
646  	                pcmk__debug("Pre-filtered the entire cib result");
647  	            }
648  	            pcmk__xml_free(saved_cib);
649  	        }
650  	    }
651  	
652  	    pcmk__xml_free(top);
653  	    pcmk__trace("Done");
654  	    return rc;
655  	}
656  	
657  	int
658  	cib__create_op(cib_t *cib, const char *op, const char *host,
659  	               const char *section, xmlNode *data, int call_options,
660  	               const char *user_name, const char *client_name,
661  	               xmlNode **op_msg)
662  	{
663  	    CRM_CHECK((cib != NULL) && (op_msg != NULL), return EPROTO);
664  	
665  	    *op_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND);
666  	
667  	    cib->call_id++;
668  	    if (cib->call_id < 1) {
669  	        cib->call_id = 1;
670  	    }
671  	
672  	    pcmk__xe_set(*op_msg, PCMK__XA_T, PCMK__VALUE_CIB);
673  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_OP, op);
674  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_HOST, host);
675  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_SECTION, section);
676  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_USER, user_name);
677  	    pcmk__xe_set(*op_msg, PCMK__XA_CIB_CLIENTNAME, client_name);
678  	    pcmk__xe_set_int(*op_msg, PCMK__XA_CIB_CALLID, cib->call_id);
679  	
680  	    pcmk__trace("Sending call options: %.8lx, %d", (long) call_options,
681  	                call_options);
682  	    pcmk__xe_set_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options);
683  	
684  	    if (data != NULL) {
685  	        xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA);
686  	
687  	        pcmk__xml_copy(wrapper, data);
688  	    }
689  	
690  	    return pcmk_rc_ok;
691  	}
692  	
693  	/*!
694  	 * \internal
695  	 * \brief Check whether a CIB request is supported in a transaction
696  	 *
697  	 * \param[in] request  CIB request
698  	 *
699  	 * \return Standard Pacemaker return code
700  	 */
701  	static int
702  	validate_transaction_request(const xmlNode *request)
703  	{
704  	    const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP);
705  	    const char *host = pcmk__xe_get(request, PCMK__XA_CIB_HOST);
706  	    const cib__operation_t *operation = NULL;
707  	    int rc = cib__get_operation(op, &operation);
708  	
709  	    if (rc != pcmk_rc_ok) {
710  	        // cib__get_operation() logs error
711  	        return rc;
712  	    }
713  	
714  	    if (!pcmk__is_set(operation->flags, cib__op_attr_transaction)) {
715  	        pcmk__err("Operation %s is not supported in CIB transactions", op);
716  	        return EOPNOTSUPP;
717  	    }
718  	
719  	    if (host != NULL) {
720  	        pcmk__err("Operation targeting a specific node (%s) is not supported "
721  	                  "in a CIB transaction",
722  	                  host);
723  	        return EOPNOTSUPP;
724  	    }
725  	    return pcmk_rc_ok;
726  	}
727  	
728  	/*!
729  	 * \internal
730  	 * \brief Append a CIB request to a CIB transaction
731  	 *
732  	 * \param[in,out] cib      CIB client whose transaction to extend
733  	 * \param[in,out] request  Request to add to transaction
734  	 *
735  	 * \return Standard Pacemaker return code
736  	 */
737  	int
738  	cib__extend_transaction(cib_t *cib, xmlNode *request)
739  	{
740  	    const char *op = pcmk__xe_get(request, PCMK__XA_CIB_OP);
741  	    const char *client_id = NULL;
742  	    int rc = pcmk_rc_ok;
743  	
744  	    pcmk__assert((cib != NULL) && (request != NULL));
745  	
746  	    rc = validate_transaction_request(request);
747  	
748  	    if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
749  	        rc = pcmk_rc_no_transaction;
750  	    }
751  	
752  	    if (rc == pcmk_rc_ok) {
753  	        pcmk__xml_copy(cib->transaction, request);
754  	        return pcmk_rc_ok;
755  	    }
756  	
757  	    cib->cmds->client_id(cib, NULL, &client_id);
758  	
759  	    pcmk__err("Failed to add '%s' operation to transaction for client %s: %s",
760  	              op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
761  	    pcmk__log_xml_info(request, "failed");
762  	
763  	    return rc;
764  	}
765  	
766  	void
767  	cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
768  	{
769  	    xmlNode *output = NULL;
770  	    cib_callback_client_t *blob = NULL;
771  	
772  	    if (msg != NULL) {
773  	        xmlNode *wrapper = NULL;
774  	
775  	        pcmk__xe_get_int(msg, PCMK__XA_CIB_RC, &rc);
776  	        pcmk__xe_get_int(msg, PCMK__XA_CIB_CALLID, &call_id);
777  	        wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL);
778  	        output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
779  	    }
780  	
781  	    blob = cib__lookup_id(call_id);
782  	
783  	    if (blob == NULL) {
784  	        pcmk__trace("No callback found for call %d", call_id);
785  	    }
786  	
787  	    if (cib == NULL) {
788  	        pcmk__debug("No cib object supplied");
789  	    }
790  	
791  	    if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
792  	        pcmk__trace("Invoking callback %s for call %d",
793  	                    pcmk__s(blob->id, "without ID"), call_id);
794  	        blob->callback(msg, call_id, rc, output, blob->user_data);
795  	
796  	    } else if ((cib != NULL) && (rc != pcmk_ok)) {
797  	        pcmk__warn("CIB command failed: %s", pcmk_strerror(rc));
798  	        pcmk__log_xml_debug(msg, "Failed CIB Update");
799  	    }
800  	
801  	    /* This may free user_data, so do it after the callback */
802  	    if (blob) {
803  	        remove_cib_op_callback(call_id, FALSE);
804  	    }
805  	
806  	    pcmk__trace("OP callback activated for %d", call_id);
807  	}
808  	
809  	void
810  	cib_native_notify(gpointer data, gpointer user_data)
811  	{
812  	    xmlNode *msg = user_data;
813  	    cib_notify_client_t *entry = data;
814  	    const char *event = NULL;
815  	
816  	    if (msg == NULL) {
817  	        pcmk__warn("Skipping callback - NULL message");
818  	        return;
819  	    }
820  	
821  	    event = pcmk__xe_get(msg, PCMK__XA_SUBT);
822  	
823  	    if (entry == NULL) {
824  	        pcmk__warn("Skipping callback - NULL callback client");
825  	        return;
826  	
827  	    } else if (entry->callback == NULL) {
828  	        pcmk__warn("Skipping callback - NULL callback");
829  	        return;
830  	
831  	    } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
832  	        pcmk__trace("Skipping callback - event mismatch %p/%s vs. %s", entry,
833  	                    entry->event, event);
834  	        return;
835  	    }
836  	
837  	    pcmk__trace("Invoking callback for %p/%s event...", entry, event);
838  	    entry->callback(event, msg);
839  	    pcmk__trace("Callback invoked...");
840  	}
841  	
842  	int
843  	cib_internal_op(cib_t * cib, const char *op, const char *host,
844  	                const char *section, xmlNode * data,
845  	                xmlNode ** output_data, int call_options, const char *user_name)
846  	{
847  	    /* Note: *output_data gets set only for create and query requests. There are
848  	     * a lot of opportunities to clean up, clarify, check/enforce things, etc.
849  	     */
850  	    int (*delegate)(cib_t *cib, const char *op, const char *host,
851  	                    const char *section, xmlNode *data, xmlNode **output_data,
852  	                    int call_options, const char *user_name) = NULL;
853  	
854  	    if (cib == NULL) {
855  	        return -EINVAL;
856  	    }
857  	
858  	    delegate = cib->delegate_fn;
859  	    if (delegate == NULL) {
860  	        return -EPROTONOSUPPORT;
861  	    }
862  	    if (user_name == NULL) {
863  	        user_name = getenv("CIB_user");
864  	    }
865  	    return delegate(cib, op, host, section, data, output_data, call_options, user_name);
866  	}
867  	
868  	/*!
869  	 * \brief Apply a CIB update patch to a given CIB
870  	 *
871  	 * \param[in]  event   CIB update patch
872  	 * \param[in]  input   CIB to patch
873  	 * \param[out] output  Resulting CIB after patch
874  	 * \param[in]  level   Log the patch at this log level (unless LOG_CRIT)
875  	 *
876  	 * \return Legacy Pacemaker return code
877  	 * \note sbd calls this function
878  	 */
879  	int
880  	cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
881  	                      int level)
882  	{
883  	    int rc = pcmk_err_generic;
884  	
885  	    xmlNode *wrapper = NULL;
886  	    xmlNode *diff = NULL;
887  	
(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.
888  	    pcmk__assert((event != NULL) && (input != NULL) && (output != NULL));
889  	
890  	    pcmk__xe_get_int(event, PCMK__XA_CIB_RC, &rc);
891  	    wrapper = pcmk__xe_first_child(event, PCMK__XE_CIB_UPDATE_RESULT, NULL,
892  	                                   NULL);
893  	    diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
894  	
(4) Event path: Condition "rc < 0", taking false branch.
(5) Event path: Condition "diff == NULL", taking false branch.
895  	    if ((rc < pcmk_ok) || (diff == NULL)) {
896  	        return rc;
897  	    }
898  	
(6) Event path: Condition "level > 2", taking true branch.
899  	    if (level > LOG_CRIT) {
(7) Event path: Switch case value "255".
(8) Event path: Breaking from switch.
900  	        pcmk__log_xml_patchset(level, diff);
901  	    }
902  	
(9) Event path: Condition "input == NULL", taking false branch.
903  	    if (input == NULL) {
904  	        return rc;
905  	    }
906  	
(10) Event path: Condition "*output != input", taking true branch.
907  	    if (*output != input) {
908  	        pcmk__xml_free(*output);
909  	        *output = pcmk__xml_copy(NULL, input);
910  	    }
911  	
912  	    rc = cib__process_apply_patch(event, diff, output, NULL);
913  	    rc = pcmk_rc2legacy(rc);
(11) Event path: Condition "rc == 0", taking false branch.
914  	    if (rc == pcmk_ok) {
915  	        return pcmk_ok;
916  	    }
917  	
(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.
918  	    pcmk__debug("Update didn't apply: %s (%d)", pcmk_strerror(rc), rc);
919  	
(16) Event path: Condition "rc == -205", taking false branch.
920  	    if (rc == -pcmk_err_old_data) {
921  	        // Mask this error, since it means we already have the supplied update
922  	        return pcmk_ok;
923  	    }
924  	
925  	    // 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".
926  	    g_clear_pointer(output, pcmk__xml_free);
927  	    return rc;
928  	}
929  	
930  	#define log_signon_query_err(out, fmt, args...) do {    \
931  	        if (out != NULL) {                              \
932  	            out->err(out, fmt, ##args);                 \
933  	        } else {                                        \
934  	            pcmk__err(fmt, ##args);                     \
935  	        }                                               \
936  	    } while (0)
937  	
938  	int
939  	cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
940  	{
941  	    int rc = pcmk_rc_ok;
942  	    cib_t *cib_conn = NULL;
943  	
944  	    pcmk__assert(cib_object != NULL);
945  	
946  	    if (cib == NULL) {
947  	        cib_conn = cib_new();
948  	    } else {
949  	        if (*cib == NULL) {
950  	            *cib = cib_new();
951  	        }
952  	        cib_conn = *cib;
953  	    }
954  	
955  	    if (cib_conn == NULL) {
956  	        return ENOMEM;
957  	    }
958  	
959  	    if (cib_conn->state == cib_disconnected) {
960  	        rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
961  	        rc = pcmk_legacy2rc(rc);
962  	    }
963  	
964  	    if (rc != pcmk_rc_ok) {
965  	        log_signon_query_err(out, "Could not connect to the CIB: %s",
966  	                             pcmk_rc_str(rc));
967  	        goto done;
968  	    }
969  	
970  	    if (out != NULL) {
971  	        out->transient(out, "Querying CIB...");
972  	    }
973  	    rc = cib_conn->cmds->query(cib_conn, NULL, cib_object, cib_sync_call);
974  	    rc = pcmk_legacy2rc(rc);
975  	
976  	    if (rc != pcmk_rc_ok) {
977  	        log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
978  	    }
979  	
980  	done:
981  	    if (cib == NULL) {
982  	        cib__clean_up_connection(&cib_conn);
983  	    }
984  	
985  	    if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
986  	        return pcmk_rc_no_input;
987  	    }
988  	    return rc;
989  	}
990  	
991  	/*!
992  	 * \internal
993  	 * \brief Create a new CIB connection object and connect to the CIB API
994  	 *
995  	 * This function attempts to connect up to 5 times.
996  	 *
997  	 * \param[out] cib  Where to store CIB connection object
998  	 *
999  	 * \return Standard Pacemaker return code
1000 	 *
1001 	 * \note The caller is responsible for signing off and freeing the newly
1002 	 *       allocated CIB connection object using the \c signoff() method and
1003 	 *       \c cib_delete().
1004 	 */
1005 	int
1006 	cib__create_signon(cib_t **cib)
1007 	{
1008 	    static const int attempts = 5;
1009 	    int rc = pcmk_rc_ok;
1010 	
1011 	    pcmk__assert((cib != NULL) && (*cib == NULL));
1012 	
1013 	    *cib = cib_new();
1014 	    if (*cib == NULL) {
1015 	        return ENOMEM;
1016 	    }
1017 	
1018 	    pcmk__trace("Attempting connection to CIB API (up to %d time%s)", attempts,
1019 	                pcmk__plural_s(attempts));
1020 	
1021 	    for (int remaining = attempts - 1; remaining >= 0; --remaining) {
1022 	        rc = (*cib)->cmds->signon(*cib, crm_system_name, cib_command);
1023 	
1024 	        if ((rc == pcmk_ok)
1025 	            || (remaining == 0)
1026 	            || ((errno != EAGAIN) && (errno != EALREADY))) {
1027 	            break;
1028 	        }
1029 	
1030 	        // Retry after soft error (interrupted by signal, etc.)
1031 	        pcmk__sleep_ms((attempts - remaining) * 500);
1032 	        pcmk__debug("Re-attempting connection to CIB manager (%d attempt%s "
1033 	                    "remaining)",
1034 	                    remaining, pcmk__plural_s(remaining));
1035 	    }
1036 	
1037 	    rc = pcmk_legacy2rc(rc);
1038 	    if (rc != pcmk_rc_ok) {
1039 	        cib__clean_up_connection(cib);
1040 	    }
1041 	
1042 	    return rc;
1043 	}
1044 	
1045 	int
1046 	cib__clean_up_connection(cib_t **cib)
1047 	{
1048 	    int rc;
1049 	
1050 	    if (*cib == NULL) {
1051 	        return pcmk_rc_ok;
1052 	    }
1053 	
1054 	    rc = (*cib)->cmds->signoff(*cib);
1055 	    cib_delete(*cib);
1056 	    *cib = NULL;
1057 	    return pcmk_legacy2rc(rc);
1058 	}
1059