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