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