pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
cib_utils.c
Go to the documentation of this file.
1/*
2 * Original copyright 2004 International Business Machines
3 * Later changes copyright 2008-2025 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 <stdlib.h>
13#include <stdio.h>
14#include <stdarg.h>
15#include <string.h>
16#include <sys/utsname.h>
17
18#include <glib.h>
19
20#include <crm/crm.h>
21#include <crm/cib/internal.h>
23#include <crm/common/xml.h>
25
26gboolean
27cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
28{
29 *epoch = -1;
30 *updates = -1;
31 *admin_epoch = -1;
32
33 if (cib == NULL) {
34 return FALSE;
35
36 } else {
40 }
41 return TRUE;
42}
43
44gboolean
45cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
46 int *_admin_epoch, int *_epoch, int *_updates)
47{
48 int add[] = { 0, 0, 0 };
49 int del[] = { 0, 0, 0 };
50
51 xml_patch_versions(diff, add, del);
52
53 *admin_epoch = add[0];
54 *epoch = add[1];
55 *updates = add[2];
56
57 *_admin_epoch = del[0];
58 *_epoch = del[1];
59 *_updates = del[2];
60
61 return TRUE;
62}
63
73int
74cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
75{
76 int rc = pcmk_err_generic;
77 xmlNode *wrapper = NULL;
78
79 pcmk__assert(patchset != NULL);
80 *patchset = NULL;
81
82 if (msg == NULL) {
83 crm_err("CIB diff notification received with no XML");
84 return ENOMSG;
85 }
86
87 if ((crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc) != 0)
88 || (rc != pcmk_ok)) {
89
90 crm_warn("Ignore failed CIB update: %s " QB_XS " rc=%d",
91 pcmk_strerror(rc), rc);
92 crm_log_xml_debug(msg, "failed");
93 return pcmk_legacy2rc(rc);
94 }
95
96 wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, NULL, NULL);
97 *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
98
99 if (*patchset == NULL) {
100 crm_err("CIB diff notification received with no patchset");
101 return ENOMSG;
102 }
103 return pcmk_rc_ok;
104}
105
116xmlNode *
117createEmptyCib(int cib_epoch)
118{
119 xmlNode *cib_root = NULL, *config = NULL;
120
121 cib_root = pcmk__xe_create(NULL, PCMK_XE_CIB);
124
125 crm_xml_add_int(cib_root, PCMK_XA_EPOCH, cib_epoch);
128
129 config = pcmk__xe_create(cib_root, PCMK_XE_CONFIGURATION);
131
136
137#if PCMK__RESOURCE_STICKINESS_DEFAULT != 0
138 {
139 xmlNode *rsc_defaults = pcmk__xe_create(config, PCMK_XE_RSC_DEFAULTS);
140 xmlNode *meta = pcmk__xe_create(rsc_defaults, PCMK_XE_META_ATTRIBUTES);
141 xmlNode *nvpair = pcmk__xe_create(meta, PCMK_XE_NVPAIR);
142
143 crm_xml_add(meta, PCMK_XA_ID, "build-resource-defaults");
148 }
149#endif
150 return cib_root;
151}
152
153static bool
154cib_acl_enabled(xmlNode *xml, const char *user)
155{
156 bool rc = FALSE;
157
158 if(pcmk_acl_required(user)) {
159 const char *value = NULL;
160 GHashTable *options = pcmk__strkey_table(free, free);
161
162 cib_read_config(options, xml);
164 rc = crm_is_true(value);
165 g_hash_table_destroy(options);
166 }
167
168 crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
169 return rc;
170}
171
182static bool
183should_copy_cib(const char *op, const char *section, int call_options)
184{
185 if (pcmk_is_set(call_options, cib_dryrun)) {
186 // cib_dryrun implies a scratch copy by definition; no side effects
187 return true;
188 }
189
190 if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
191 /* Commit-transaction must make a copy for atomicity. We must revert to
192 * the original CIB if the entire transaction cannot be applied
193 * successfully.
194 */
195 return true;
196 }
197
198 if (pcmk_is_set(call_options, cib_transaction)) {
199 /* If cib_transaction is set, then we're in the process of committing a
200 * transaction. The commit-transaction request already made a scratch
201 * copy, and we're accumulating changes in that copy.
202 */
203 return false;
204 }
205
206 if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_none)) {
207 /* Copying large CIBs accounts for a huge percentage of our CIB usage,
208 * and this avoids some of it.
209 *
210 * @TODO: Is this safe? See discussion at
211 * https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
212 */
213 return false;
214 }
215
216 // Default behavior is to operate on a scratch copy
217 return true;
218}
219
220int
221cib_perform_op(cib_t *cib, const char *op, uint32_t call_options,
222 cib__op_fn_t fn, bool is_query, const char *section,
223 xmlNode *req, xmlNode *input, bool manage_counters,
224 bool *config_changed, xmlNode **current_cib,
225 xmlNode **result_cib, xmlNode **diff, xmlNode **output)
226{
227 int rc = pcmk_ok;
228 bool check_schema = true;
229 bool make_copy = true;
230 xmlNode *top = NULL;
231 xmlNode *scratch = NULL;
232 xmlNode *patchset_cib = NULL;
233 xmlNode *local_diff = NULL;
234
235 const char *user = crm_element_value(req, PCMK__XA_CIB_USER);
236 const bool enable_acl = cib_acl_enabled(*current_cib, user);
237 bool with_digest = false;
238
239 crm_trace("Begin %s%s%s op",
240 (pcmk_is_set(call_options, cib_dryrun)? "dry run of " : ""),
241 (is_query? "read-only " : ""), op);
242
243 CRM_CHECK(output != NULL, return -ENOMSG);
244 CRM_CHECK(current_cib != NULL, return -ENOMSG);
245 CRM_CHECK(result_cib != NULL, return -ENOMSG);
246 CRM_CHECK(config_changed != NULL, return -ENOMSG);
247
248 if(output) {
249 *output = NULL;
250 }
251
252 *result_cib = NULL;
253 *config_changed = false;
254
255 if (fn == NULL) {
256 return -EINVAL;
257 }
258
259 if (is_query) {
260 xmlNode *cib_ro = *current_cib;
261 xmlNode *cib_filtered = NULL;
262
263 if (enable_acl
264 && xml_acl_filtered_copy(user, *current_cib, *current_cib,
265 &cib_filtered)) {
266
267 if (cib_filtered == NULL) {
268 crm_debug("Pre-filtered the entire cib");
269 return -EACCES;
270 }
271 cib_ro = cib_filtered;
272 crm_log_xml_trace(cib_ro, "filtered");
273 }
274
275 rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
276
277 if(output == NULL || *output == NULL) {
278 /* nothing */
279
280 } else if(cib_filtered == *output) {
281 cib_filtered = NULL; /* Let them have this copy */
282
283 } else if (*output == *current_cib) {
284 /* They already know not to free it */
285
286 } else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
287 /* We're about to free the document of which *output is a part */
288 *output = pcmk__xml_copy(NULL, *output);
289
290 } else if ((*output)->doc == (*current_cib)->doc) {
291 /* Give them a copy they can free */
292 *output = pcmk__xml_copy(NULL, *output);
293 }
294
295 pcmk__xml_free(cib_filtered);
296 return rc;
297 }
298
299 make_copy = should_copy_cib(op, section, call_options);
300
301 if (!make_copy) {
302 /* Conditional on v2 patch style */
303
304 scratch = *current_cib;
305
306 // Make a copy of the top-level element to store version details
307 top = pcmk__xe_create(NULL, (const char *) scratch->name);
308 pcmk__xe_copy_attrs(top, scratch, pcmk__xaf_none);
309 patchset_cib = top;
310
311 pcmk__xml_commit_changes(scratch->doc);
313 if (enable_acl) {
314 pcmk__enable_acl(*current_cib, scratch, user);
315 }
316
317 rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
318
319 /* If scratch points to a new object now (for example, after an erase
320 * operation), then *current_cib should point to the same object.
321 *
322 * @TODO Enable tracking and ACLs and calculate changes? Change tracking
323 * and unpacked ACLs didn't carry over to new object.
324 */
325 *current_cib = scratch;
326
327 } else {
328 scratch = pcmk__xml_copy(NULL, *current_cib);
329 patchset_cib = *current_cib;
330
332 if (enable_acl) {
333 pcmk__enable_acl(*current_cib, scratch, user);
334 }
335
336 rc = (*fn) (op, call_options, section, req, input, *current_cib,
337 &scratch, output);
338
339 /* @TODO This appears to be a hack to determine whether scratch points
340 * to a new object now, without saving the old pointer (which may be
341 * invalid now) for comparison. Confirm this, and check more clearly.
342 */
344 crm_trace("Inferring changes after %s op", op);
345 pcmk__xml_commit_changes(scratch->doc);
346 if (enable_acl) {
347 pcmk__enable_acl(*current_cib, scratch, user);
348 }
349 pcmk__xml_mark_changes(*current_cib, scratch);
350 }
351 CRM_CHECK(*current_cib != scratch, return -EINVAL);
352 }
353
354 xml_acl_disable(scratch); /* Allow the system to make any additional changes */
355
356 if (rc == pcmk_ok && scratch == NULL) {
357 rc = -EINVAL;
358 goto done;
359
360 } else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
361 crm_trace("ACL rejected part or all of the proposed changes");
362 rc = -EACCES;
363 goto done;
364
365 } else if (rc != pcmk_ok) {
366 goto done;
367 }
368
369 /* If the CIB is from a file, we don't need to check that the feature set is
370 * supported. All we care about in that case is the schema version, which
371 * is checked elsewhere.
372 */
373 if (scratch && (cib == NULL || cib->variant != cib_file)) {
374 const char *new_version = crm_element_value(scratch, PCMK_XA_CRM_FEATURE_SET);
375
376 rc = pcmk__check_feature_set(new_version);
377 if (rc != pcmk_rc_ok) {
378 crm_err("Discarding update with feature set '%s' greater than "
379 "our own '%s'", new_version, CRM_FEATURE_SET);
380 rc = pcmk_rc2legacy(rc);
381 goto done;
382 }
383 }
384
385 if (patchset_cib != NULL) {
386 int old = 0;
387 int new = 0;
388
390 crm_element_value_int(patchset_cib, PCMK_XA_ADMIN_EPOCH, &old);
391
392 if (old > new) {
393 crm_err("%s went backwards: %d -> %d (Opts: %#x)",
394 PCMK_XA_ADMIN_EPOCH, old, new, call_options);
395 crm_log_xml_warn(req, "Bad Op");
396 crm_log_xml_warn(input, "Bad Data");
397 rc = -pcmk_err_old_data;
398
399 } else if (old == new) {
400 crm_element_value_int(scratch, PCMK_XA_EPOCH, &new);
401 crm_element_value_int(patchset_cib, PCMK_XA_EPOCH, &old);
402 if (old > new) {
403 crm_err("%s went backwards: %d -> %d (Opts: %#x)",
404 PCMK_XA_EPOCH, old, new, call_options);
405 crm_log_xml_warn(req, "Bad Op");
406 crm_log_xml_warn(input, "Bad Data");
407 rc = -pcmk_err_old_data;
408 }
409 }
410 }
411
412 crm_trace("Massaging CIB contents");
413 pcmk__strip_xml_text(scratch);
414
415 if (make_copy) {
416 static time_t expires = 0;
417 time_t tm_now = time(NULL);
418
419 if (expires < tm_now) {
420 expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */
421 with_digest = true;
422 }
423 }
424
425 local_diff = xml_create_patchset(0, patchset_cib, scratch,
426 config_changed, manage_counters);
427
429 pcmk__xml_commit_changes(scratch->doc);
430
431 if(local_diff) {
432 patchset_process_digest(local_diff, patchset_cib, scratch, with_digest);
433 pcmk__log_xml_patchset(LOG_INFO, local_diff);
434 crm_log_xml_trace(local_diff, "raw patch");
435 }
436
437 if (make_copy && (local_diff != NULL)) {
438 // Original to compare against doesn't exist
440 {
441 // Validate the calculated patch set
442 int test_rc = pcmk_ok;
443 int format = 1;
444 xmlNode *cib_copy = pcmk__xml_copy(NULL, patchset_cib);
445
446 crm_element_value_int(local_diff, PCMK_XA_FORMAT, &format);
447 test_rc = xml_apply_patchset(cib_copy, local_diff,
448 manage_counters);
449
450 if (test_rc != pcmk_ok) {
451 save_xml_to_file(cib_copy, "PatchApply:calculated", NULL);
452 save_xml_to_file(patchset_cib, "PatchApply:input", NULL);
453 save_xml_to_file(scratch, "PatchApply:actual", NULL);
454 save_xml_to_file(local_diff, "PatchApply:diff", NULL);
455 crm_err("v%d patchset error, patch failed to apply: %s "
456 "(%d)",
457 format, pcmk_rc_str(pcmk_legacy2rc(test_rc)),
458 test_rc);
459 }
460 pcmk__xml_free(cib_copy);
461 },
462 {}
463 );
464 }
465
466 if (pcmk__str_eq(section, PCMK_XE_STATUS, pcmk__str_casei)) {
467 /* Throttle the amount of costly validation we perform due to status updates
468 * a) we don't really care whats in the status section
469 * b) we don't validate any of its contents at the moment anyway
470 */
471 check_schema = false;
472 }
473
474 /* === scratch must not be modified after this point ===
475 * Exceptions, anything in:
476
477 static filter_t filter[] = {
478 { 0, PCMK_XA_CRM_DEBUG_ORIGIN },
479 { 0, PCMK_XA_CIB_LAST_WRITTEN },
480 { 0, PCMK_XA_UPDATE_ORIGIN },
481 { 0, PCMK_XA_UPDATE_CLIENT },
482 { 0, PCMK_XA_UPDATE_USER },
483 };
484 */
485
486 if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) {
487 const char *schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH);
488
489 if (schema == NULL) {
491 }
492
495
496 /* Make values of origin, client, and user in scratch match
497 * the ones in req (if the schema allows the attributes)
498 */
499 if (pcmk__cmp_schemas_by_name(schema, "pacemaker-1.2") >= 0) {
500 const char *origin = crm_element_value(req, PCMK__XA_SRC);
501 const char *client = crm_element_value(req,
503
504 if (origin != NULL) {
505 crm_xml_add(scratch, PCMK_XA_UPDATE_ORIGIN, origin);
506 } else {
508 }
509
510 if (client != NULL) {
511 crm_xml_add(scratch, PCMK_XA_UPDATE_CLIENT, user);
512 } else {
514 }
515
516 if (user != NULL) {
517 crm_xml_add(scratch, PCMK_XA_UPDATE_USER, user);
518 } else {
520 }
521 }
522 }
523
524 crm_trace("Perform validation: %s", pcmk__btoa(check_schema));
525 if ((rc == pcmk_ok) && check_schema
528 }
529
530 done:
531
532 *result_cib = scratch;
533
534 /* @TODO: This may not work correctly with !make_copy, since we don't
535 * keep the original CIB.
536 */
537 if ((rc != pcmk_ok) && cib_acl_enabled(patchset_cib, user)
538 && xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) {
539
540 if (*result_cib == NULL) {
541 crm_debug("Pre-filtered the entire cib result");
542 }
543 pcmk__xml_free(scratch);
544 }
545
546 if(diff) {
547 *diff = local_diff;
548 } else {
549 pcmk__xml_free(local_diff);
550 }
551
552 pcmk__xml_free(top);
553 crm_trace("Done");
554 return rc;
555}
556
557int
558cib__create_op(cib_t *cib, const char *op, const char *host,
559 const char *section, xmlNode *data, int call_options,
560 const char *user_name, const char *client_name,
561 xmlNode **op_msg)
562{
563 CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO);
564
565 *op_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_COMMAND);
566
567 cib->call_id++;
568 if (cib->call_id < 1) {
569 cib->call_id = 1;
570 }
571
573 crm_xml_add(*op_msg, PCMK__XA_CIB_OP, op);
575 crm_xml_add(*op_msg, PCMK__XA_CIB_SECTION, section);
576 crm_xml_add(*op_msg, PCMK__XA_CIB_USER, user_name);
577 crm_xml_add(*op_msg, PCMK__XA_CIB_CLIENTNAME, client_name);
579
580 crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
581 crm_xml_add_int(*op_msg, PCMK__XA_CIB_CALLOPT, call_options);
582
583 if (data != NULL) {
584 xmlNode *wrapper = pcmk__xe_create(*op_msg, PCMK__XE_CIB_CALLDATA);
585
586 pcmk__xml_copy(wrapper, data);
587 }
588
589 return pcmk_ok;
590}
591
600static int
601validate_transaction_request(const xmlNode *request)
602{
603 const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
604 const char *host = crm_element_value(request, PCMK__XA_CIB_HOST);
605 const cib__operation_t *operation = NULL;
606 int rc = cib__get_operation(op, &operation);
607
608 if (rc != pcmk_rc_ok) {
609 // cib__get_operation() logs error
610 return rc;
611 }
612
613 if (!pcmk_is_set(operation->flags, cib__op_attr_transaction)) {
614 crm_err("Operation %s is not supported in CIB transactions", op);
615 return EOPNOTSUPP;
616 }
617
618 if (host != NULL) {
619 crm_err("Operation targeting a specific node (%s) is not supported in "
620 "a CIB transaction",
621 host);
622 return EOPNOTSUPP;
623 }
624 return pcmk_rc_ok;
625}
626
636int
637cib__extend_transaction(cib_t *cib, xmlNode *request)
638{
639 int rc = pcmk_rc_ok;
640
641 pcmk__assert((cib != NULL) && (request != NULL));
642
643 rc = validate_transaction_request(request);
644
645 if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
647 }
648
649 if (rc == pcmk_rc_ok) {
650 pcmk__xml_copy(cib->transaction, request);
651
652 } else {
653 const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
654 const char *client_id = NULL;
655
656 cib->cmds->client_id(cib, NULL, &client_id);
657 crm_err("Failed to add '%s' operation to transaction for client %s: %s",
658 op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
659 crm_log_xml_info(request, "failed");
660 }
661 return pcmk_rc2legacy(rc);
662}
663
664void
665cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
666{
667 xmlNode *output = NULL;
668 cib_callback_client_t *blob = NULL;
669
670 if (msg != NULL) {
671 xmlNode *wrapper = NULL;
672
675 wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_CALLDATA, NULL, NULL);
676 output = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
677 }
678
679 blob = cib__lookup_id(call_id);
680
681 if (blob == NULL) {
682 crm_trace("No callback found for call %d", call_id);
683 }
684
685 if (cib == NULL) {
686 crm_debug("No cib object supplied");
687 }
688
689 if (rc == -pcmk_err_diff_resync) {
690 /* This is an internal value that clients do not and should not care about */
691 rc = pcmk_ok;
692 }
693
694 if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
695 crm_trace("Invoking callback %s for call %d",
696 pcmk__s(blob->id, "without ID"), call_id);
697 blob->callback(msg, call_id, rc, output, blob->user_data);
698
699 } else if ((cib != NULL) && (rc != pcmk_ok)) {
700 crm_warn("CIB command failed: %s", pcmk_strerror(rc));
701 crm_log_xml_debug(msg, "Failed CIB Update");
702 }
703
704 /* This may free user_data, so do it after the callback */
705 if (blob) {
706 remove_cib_op_callback(call_id, FALSE);
707 }
708
709 crm_trace("OP callback activated for %d", call_id);
710}
711
712void
713cib_native_notify(gpointer data, gpointer user_data)
714{
715 xmlNode *msg = user_data;
716 cib_notify_client_t *entry = data;
717 const char *event = NULL;
718
719 if (msg == NULL) {
720 crm_warn("Skipping callback - NULL message");
721 return;
722 }
723
724 event = crm_element_value(msg, PCMK__XA_SUBT);
725
726 if (entry == NULL) {
727 crm_warn("Skipping callback - NULL callback client");
728 return;
729
730 } else if (entry->callback == NULL) {
731 crm_warn("Skipping callback - NULL callback");
732 return;
733
734 } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
735 crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
736 return;
737 }
738
739 crm_trace("Invoking callback for %p/%s event...", entry, event);
740 entry->callback(event, msg);
741 crm_trace("Callback invoked...");
742}
743
744gboolean
745cib_read_config(GHashTable * options, xmlNode * current_cib)
746{
747 xmlNode *config = NULL;
748 crm_time_t *now = NULL;
749
750 if (options == NULL || current_cib == NULL) {
751 return FALSE;
752 }
753
754 now = crm_time_new(NULL);
755
756 g_hash_table_remove_all(options);
757
758 config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG);
759 if (config) {
760 pcmk_rule_input_t rule_input = {
761 .now = now,
762 };
763
766 options, NULL);
767 }
768
770
771 crm_time_free(now);
772
773 return TRUE;
774}
775
776int
777cib_internal_op(cib_t * cib, const char *op, const char *host,
778 const char *section, xmlNode * data,
779 xmlNode ** output_data, int call_options, const char *user_name)
780{
781 int (*delegate)(cib_t *cib, const char *op, const char *host,
782 const char *section, xmlNode *data, xmlNode **output_data,
783 int call_options, const char *user_name) = NULL;
784
785 if (cib == NULL) {
786 return -EINVAL;
787 }
788
789 delegate = cib->delegate_fn;
790 if (delegate == NULL) {
791 return -EPROTONOSUPPORT;
792 }
793 if (user_name == NULL) {
794 user_name = getenv("CIB_user");
795 }
796 return delegate(cib, op, host, section, data, output_data, call_options, user_name);
797}
798
810int
811cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
812 int level)
813{
814 int rc = pcmk_err_generic;
815
816 xmlNode *wrapper = NULL;
817 xmlNode *diff = NULL;
818
819 pcmk__assert((event != NULL) && (input != NULL) && (output != NULL));
820
823 NULL);
824 diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
825
826 if (rc < pcmk_ok || diff == NULL) {
827 return rc;
828 }
829
830 if (level > LOG_CRIT) {
831 pcmk__log_xml_patchset(level, diff);
832 }
833
834 if (input != NULL) {
835 rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
836 NULL);
837
838 if (rc != pcmk_ok) {
839 crm_debug("Update didn't apply: %s (%d) %p",
840 pcmk_strerror(rc), rc, *output);
841
842 if (rc == -pcmk_err_old_data) {
843 crm_trace("Masking error, we already have the supplied update");
844 return pcmk_ok;
845 }
846 pcmk__xml_free(*output);
847 *output = NULL;
848 return rc;
849 }
850 }
851 return rc;
852}
853
854#define log_signon_query_err(out, fmt, args...) do { \
855 if (out != NULL) { \
856 out->err(out, fmt, ##args); \
857 } else { \
858 crm_err(fmt, ##args); \
859 } \
860 } while (0)
861
862int
863cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
864{
865 int rc = pcmk_rc_ok;
866 cib_t *cib_conn = NULL;
867
868 pcmk__assert(cib_object != NULL);
869
870 if (cib == NULL) {
871 cib_conn = cib_new();
872 } else {
873 if (*cib == NULL) {
874 *cib = cib_new();
875 }
876 cib_conn = *cib;
877 }
878
879 if (cib_conn == NULL) {
880 return ENOMEM;
881 }
882
883 if (cib_conn->state == cib_disconnected) {
884 rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
885 rc = pcmk_legacy2rc(rc);
886 }
887
888 if (rc != pcmk_rc_ok) {
889 log_signon_query_err(out, "Could not connect to the CIB: %s",
890 pcmk_rc_str(rc));
891 goto done;
892 }
893
894 if (out != NULL) {
895 out->transient(out, "Querying CIB...");
896 }
897 rc = cib_conn->cmds->query(cib_conn, NULL, cib_object, cib_sync_call);
898 rc = pcmk_legacy2rc(rc);
899
900 if (rc != pcmk_rc_ok) {
901 log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
902 }
903
904done:
905 if (cib == NULL) {
906 cib__clean_up_connection(&cib_conn);
907 }
908
909 if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
910 return pcmk_rc_no_input;
911 }
912 return rc;
913}
914
915int
917{
918 int rc = pcmk_rc_ok;
919
920 crm_trace("Attempting connection to CIB manager (up to %d time%s)",
921 attempts, pcmk__plural_s(attempts));
922
923 for (int remaining = attempts - 1; remaining >= 0; --remaining) {
924 rc = cib->cmds->signon(cib, crm_system_name, type);
925
926 if ((rc == pcmk_rc_ok)
927 || (remaining == 0)
928 || ((errno != EAGAIN) && (errno != EALREADY))) {
929 break;
930 }
931
932 // Retry after soft error (interrupted by signal, etc.)
933 pcmk__sleep_ms((attempts - remaining) * 500);
934 crm_debug("Re-attempting connection to CIB manager (%d attempt%s remaining)",
935 remaining, pcmk__plural_s(remaining));
936 }
937
938 return rc;
939}
940
941int
943{
944 int rc;
945
946 if (*cib == NULL) {
947 return pcmk_rc_ok;
948 }
949
950 rc = (*cib)->cmds->signoff(*cib);
951 cib_delete(*cib);
952 *cib = NULL;
953 return pcmk_legacy2rc(rc);
954}
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition acl.c:671
bool pcmk_acl_required(const char *user)
Check whether ACLs are required for a given user.
Definition acl.c:808
void xml_acl_disable(xmlNode *xml)
Definition acl.c:682
bool xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml, xmlNode **result)
Copy ACL-allowed portions of specified XML.
Definition acl.c:473
void pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
Definition acl.c:378
int cib__get_operation(const char *op, const cib__operation_t **operation)
Definition cib_ops.c:145
#define PCMK__CIB_REQUEST_COMMIT_TRANSACT
Definition internal.h:41
@ cib__op_attr_transaction
Supported in a transaction.
Definition internal.h:55
int cib_process_diff(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition cib_ops.c:645
cib_callback_client_t * cib__lookup_id(int call_id)
Definition cib_client.c:768
int(* cib__op_fn_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **)
Definition internal.h:90
void cib_delete(cib_t *cib)
Free all memory used by CIB connection.
Definition cib_client.c:721
cib_t * cib_new(void)
Create a new CIB connection object.
Definition cib_client.c:562
void remove_cib_op_callback(int call_id, gboolean all_callbacks)
Definition cib_client.c:730
int pcmk__check_feature_set(const char *cib_version)
Definition cib.c:185
cib_conn_type
Definition cib_types.h:45
@ cib_command
Definition cib_types.h:46
@ cib_none
Definition cib_types.h:56
@ cib_transaction
Process request when the client commits the active transaction.
Definition cib_types.h:87
@ cib_sync_call
Definition cib_types.h:112
@ cib_dryrun
Definition cib_types.h:71
@ cib_no_mtime
Definition cib_types.h:114
@ cib_file
Definition cib_types.h:31
@ cib_disconnected
Definition cib_types.h:42
int cib__extend_transaction(cib_t *cib, xmlNode *request)
Definition cib_utils.c:637
int cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, int level)
Apply a CIB update patch to a given CIB.
Definition cib_utils.c:811
int cib__clean_up_connection(cib_t **cib)
Definition cib_utils.c:942
void cib_native_callback(cib_t *cib, xmlNode *msg, int call_id, int rc)
Definition cib_utils.c:665
int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
Definition cib_utils.c:74
int cib_perform_op(cib_t *cib, const char *op, uint32_t call_options, cib__op_fn_t fn, bool is_query, const char *section, xmlNode *req, xmlNode *input, bool manage_counters, bool *config_changed, xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff, xmlNode **output)
Definition cib_utils.c:221
#define log_signon_query_err(out, fmt, args...)
Definition cib_utils.c:854
int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
Definition cib_utils.c:863
int cib__create_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, int call_options, const char *user_name, const char *client_name, xmlNode **op_msg)
Definition cib_utils.c:558
int cib_internal_op(cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options, const char *user_name)
Definition cib_utils.c:777
gboolean cib_diff_version_details(xmlNode *diff, int *admin_epoch, int *epoch, int *updates, int *_admin_epoch, int *_epoch, int *_updates)
Definition cib_utils.c:45
gboolean cib_version_details(xmlNode *cib, int *admin_epoch, int *epoch, int *updates)
Definition cib_utils.c:27
xmlNode * createEmptyCib(int cib_epoch)
Create XML for a new (empty) CIB.
Definition cib_utils.c:117
gboolean cib_read_config(GHashTable *options, xmlNode *current_cib)
Definition cib_utils.c:745
int cib__signon_attempts(cib_t *cib, enum cib_conn_type type, int attempts)
Definition cib_utils.c:916
void cib_native_notify(gpointer data, gpointer user_data)
Definition cib_utils.c:713
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition cib.c:172
void pcmk__sleep_ms(unsigned int ms)
Definition utils.c:359
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
#define PCMK__RESOURCE_STICKINESS_DEFAULT
Definition config.h:511
pcmk__cpg_host_t host
Definition cpg.c:4
char data[0]
Definition cpg.c:10
enum pcmk_ipc_server type
Definition cpg.c:3
A dumping ground.
#define CRM_FEATURE_SET
Definition crm.h:66
char * crm_system_name
Definition utils.c:45
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:112
struct crm_time_s crm_time_t
Definition iso8601.h:32
#define crm_log_xml_info(xml, text)
Definition logging.h:376
#define crm_warn(fmt, args...)
Definition logging.h:360
#define crm_log_xml_debug(xml, text)
Definition logging.h:377
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_log_xml_trace(xml, text)
Definition logging.h:378
#define crm_log_xml_warn(xml, text)
Definition logging.h:374
#define crm_trace(fmt, args...)
Definition logging.h:370
#define LOG_TRACE
Definition logging.h:38
#define pcmk__log_xml_patchset(level, patchset)
#define pcmk__if_tracing(if_action, else_action)
#define pcmk__log_xml_changes(level, xml)
xmlNode * input
void pcmk_unpack_nvpair_blocks(const xmlNode *xml, const char *element_name, const char *first_id, const pcmk_rule_input_t *rule_input, GHashTable *values, crm_time_t *next_change)
Unpack nvpair blocks contained by an XML element into a hash table, evaluated for any rules.
Definition nvpair.c:483
#define PCMK_META_RESOURCE_STICKINESS
Definition options.h:112
#define PCMK_OPT_ENABLE_ACL
Definition options.h:37
#define PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS
Definition options.h:138
const char * pcmk__cluster_option(GHashTable *options, const char *name)
Definition options.c:1410
#define PCMK__VALUE_CIB
void pcmk__validate_cluster_options(GHashTable *options)
Definition options.c:1556
#define pcmk_err_cib_corrupt
Definition results.h:85
const char * pcmk_strerror(int rc)
Definition results.c:257
#define pcmk_err_old_data
Definition results.h:73
#define pcmk_err_generic
Definition results.h:69
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
#define pcmk_err_schema_validation
Definition results.h:71
@ pcmk_rc_no_input
Definition results.h:127
@ pcmk_rc_no_transaction
Definition results.h:117
@ pcmk_rc_ok
Definition results.h:159
#define pcmk_ok
Definition results.h:65
int pcmk_rc2legacy(int rc)
Definition results.c:662
#define pcmk_err_diff_resync
Definition results.h:79
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:675
#define pcmk__assert(expr)
const char * pcmk__highest_schema_name(void)
Definition schemas.c:101
bool pcmk__configured_schema_validates(xmlNode *xml)
Definition schemas.c:861
void pcmk__warn_if_schema_deprecated(const char *schema)
Definition schemas.c:1565
int pcmk__cmp_schemas_by_name(const char *schema1_name, const char *schema2_name)
Definition schemas.c:759
gboolean crm_is_true(const char *s)
Definition strings.c:490
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
#define pcmk__plural_s(i)
@ pcmk__str_none
@ pcmk__str_casei
uint32_t flags
Group of enum cib__op_attr flags.
Definition internal.h:96
int(* signon)(cib_t *cib, const char *name, enum cib_conn_type type)
Definition cib_types.h:124
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition cib_types.h:151
int(* client_id)(const cib_t *cib, const char **async_id, const char **sync_id)
Get the given CIB connection's unique client identifier(s)
Definition cib_types.h:229
const char * id
Definition internal.h:109
void(* callback)(xmlNode *, int, int, xmlNode *, void *)
Definition internal.h:108
const char * event
Definition internal.h:100
void(* callback)(const char *event, xmlNode *msg)
Definition internal.h:103
enum cib_state state
Definition cib_types.h:312
xmlNode * transaction
Definition cib_types.h:327
void * delegate_fn
Definition cib_types.h:320
cib_api_operations_t * cmds
Definition cib_types.h:325
enum cib_variant variant
Definition cib_types.h:315
int call_id
Definition cib_types.h:317
This structure contains everything that makes up a single output formatter.
int int(* transient)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Data used to evaluate a rule (any NULL items are ignored)
Definition rules.h:57
Wrappers for and extensions to libxml2.
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
Definition patchset.c:315
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
Definition patchset.c:289
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
Definition patchset.c:765
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version)
Definition patchset.c:244
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
const char * pcmk__xe_add_last_written(xmlNode *xe)
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
@ pcmk__xaf_none
Flag has no effect.
bool pcmk__xml_doc_all_flags_set(const xmlDoc *xml, uint32_t flags)
Definition xml.c:147
void pcmk__xml_commit_changes(xmlDoc *doc)
Definition xml.c:468
void pcmk__strip_xml_text(xmlNode *xml)
Definition xml.c:870
void pcmk__xml_doc_set_flags(xmlDoc *doc, uint32_t flags)
Definition xml.c:128
@ pcmk__xf_tracking
Tracking is enabled (set for document only)
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
void pcmk__xml_mark_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition xml.c:1537
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
Definition xml_io.c:604
#define PCMK_XE_CONSTRAINTS
Definition xml_names.h:89
#define PCMK_XE_CIB
Definition xml_names.h:79
#define PCMK_XE_STATUS
Definition xml_names.h:204
#define PCMK_XA_FORMAT
Definition xml_names.h:291
#define PCMK_XA_VALIDATE_WITH
Definition xml_names.h:441
#define PCMK_XA_UPDATE_ORIGIN
Definition xml_names.h:437
#define PCMK_XA_EPOCH
Definition xml_names.h:268
#define PCMK_XE_CRM_CONFIG
Definition xml_names.h:91
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XE_RESOURCES
Definition xml_names.h:179
#define PCMK_XE_META_ATTRIBUTES
Definition xml_names.h:130
#define PCMK_XA_ADMIN_EPOCH
Definition xml_names.h:232
#define PCMK_XA_VALUE
Definition xml_names.h:442
#define PCMK_XA_CRM_FEATURE_SET
Definition xml_names.h:254
#define PCMK_XE_CONFIGURATION
Definition xml_names.h:87
#define PCMK_XE_CLUSTER_PROPERTY_SET
Definition xml_names.h:84
#define PCMK_XA_UPDATE_USER
Definition xml_names.h:438
#define PCMK_XE_NVPAIR
Definition xml_names.h:144
#define PCMK_XE_NODES
Definition xml_names.h:142
#define PCMK_XA_NUM_UPDATES
Definition xml_names.h:341
#define PCMK_XA_NAME
Definition xml_names.h:330
#define PCMK_XA_UPDATE_CLIENT
Definition xml_names.h:436
#define PCMK_XE_RSC_DEFAULTS
Definition xml_names.h:186
#define PCMK__XA_CIB_CALLOPT
#define PCMK__XA_CIB_OP
#define PCMK__XA_CIB_CALLID
#define PCMK__XA_CIB_USER
#define PCMK__XE_CIB_COMMAND
#define PCMK__XA_CIB_SECTION
#define PCMK__XA_CIB_RC
#define PCMK__XA_T
#define PCMK__XE_CIB_CALLDATA
#define PCMK__XA_SUBT
#define PCMK__XA_CIB_HOST
#define PCMK__XA_CIB_CLIENTNAME
#define PCMK__XE_CIB_UPDATE_RESULT
#define PCMK__XA_SRC