pacemaker 2.1.7-2.1.7
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-2023 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>
22#include <crm/msg_xml.h>
24#include <crm/common/xml.h>
26#include <crm/pengine/rules.h>
27
28xmlNode *
30{
31 xmlNode *the_cib = NULL;
32 xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
33
34 cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call);
35 if (the_cib != NULL) {
36 copy_in_properties(generation, the_cib);
37 free_xml(the_cib);
38 }
39
40 return generation;
41}
42
43gboolean
44cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
45{
46 *epoch = -1;
47 *updates = -1;
48 *admin_epoch = -1;
49
50 if (cib == NULL) {
51 return FALSE;
52
53 } else {
57 }
58 return TRUE;
59}
60
61gboolean
62cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
63 int *_admin_epoch, int *_epoch, int *_updates)
64{
65 int add[] = { 0, 0, 0 };
66 int del[] = { 0, 0, 0 };
67
68 xml_patch_versions(diff, add, del);
69
70 *admin_epoch = add[0];
71 *epoch = add[1];
72 *updates = add[2];
73
74 *_admin_epoch = del[0];
75 *_epoch = del[1];
76 *_updates = del[2];
77
78 return TRUE;
79}
80
90int
91cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
92{
93 int rc = pcmk_err_generic;
94
95 CRM_ASSERT(patchset != NULL);
96 *patchset = NULL;
97
98 if (msg == NULL) {
99 crm_err("CIB diff notification received with no XML");
100 return ENOMSG;
101 }
102
103 if ((crm_element_value_int(msg, F_CIB_RC, &rc) != 0) || (rc != pcmk_ok)) {
104 crm_warn("Ignore failed CIB update: %s " CRM_XS " rc=%d",
105 pcmk_strerror(rc), rc);
106 crm_log_xml_debug(msg, "failed");
107 return pcmk_legacy2rc(rc);
108 }
109
110 *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
111
112 if (*patchset == NULL) {
113 crm_err("CIB diff notification received with no patchset");
114 return ENOMSG;
115 }
116 return pcmk_rc_ok;
117}
118
119#define XPATH_DIFF_V1 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
120
131static bool
132element_in_patchset_v1(const xmlNode *patchset, const char *element)
133{
134 char *xpath = crm_strdup_printf(XPATH_DIFF_V1 "//%s",
135 pcmk__s(element, XML_TAG_CIB));
136 xmlXPathObject *xpath_obj = xpath_search(patchset, xpath);
137
138 free(xpath);
139
140 if (xpath_obj == NULL) {
141 return false;
142 }
143 freeXpathObject(xpath_obj);
144 return true;
145}
146
158static bool
159element_in_patchset_v2(const xmlNode *patchset, const char *element)
160{
161 const char *element_xpath = pcmk__cib_abs_xpath_for(element);
162 const char *parent_xpath = pcmk_cib_parent_name_for(element);
163 char *element_regex = NULL;
164 bool rc = false;
165
166 CRM_CHECK(element_xpath != NULL, return false); // Unsupported element
167
168 // Matches if and only if element_xpath is part of a changed path
169 element_regex = crm_strdup_printf("^%s(/|$)", element_xpath);
170
171 for (const xmlNode *change = first_named_child(patchset, XML_DIFF_CHANGE);
172 change != NULL; change = crm_next_same_xml(change)) {
173
174 const char *op = crm_element_value(change, F_CIB_OPERATION);
175 const char *diff_xpath = crm_element_value(change, XML_DIFF_PATH);
176
177 if (pcmk__str_eq(diff_xpath, element_regex, pcmk__str_regex)) {
178 // Change to an existing element
179 rc = true;
180 break;
181 }
182
183 if (pcmk__str_eq(op, "create", pcmk__str_none)
184 && pcmk__str_eq(diff_xpath, parent_xpath, pcmk__str_none)
185 && pcmk__xe_is(pcmk__xml_first_child(change), element)) {
186
187 // Newly added element
188 rc = true;
189 break;
190 }
191 }
192
193 free(element_regex);
194 return rc;
195}
196
208bool
209cib__element_in_patchset(const xmlNode *patchset, const char *element)
210{
211 int format = 1;
212
213 CRM_ASSERT(patchset != NULL);
214
215 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
216 switch (format) {
217 case 1:
218 return element_in_patchset_v1(patchset, element);
219
220 case 2:
221 return element_in_patchset_v2(patchset, element);
222
223 default:
224 crm_warn("Unknown patch format: %d", format);
225 return false;
226 }
227}
228
237xmlNode *
238createEmptyCib(int cib_epoch)
239{
240 xmlNode *cib_root = NULL, *config = NULL;
241
242 cib_root = create_xml_node(NULL, XML_TAG_CIB);
245
246 crm_xml_add_int(cib_root, XML_ATTR_GENERATION, cib_epoch);
249
250 config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
252
257
258#if PCMK__RESOURCE_STICKINESS_DEFAULT != 0
259 {
260 xmlNode *rsc_defaults = create_xml_node(config, XML_CIB_TAG_RSCCONFIG);
261 xmlNode *meta = create_xml_node(rsc_defaults, XML_TAG_META_SETS);
262 xmlNode *nvpair = create_xml_node(meta, XML_CIB_TAG_NVPAIR);
263
264 crm_xml_add(meta, XML_ATTR_ID, "build-resource-defaults");
269 }
270#endif
271 return cib_root;
272}
273
274static bool
275cib_acl_enabled(xmlNode *xml, const char *user)
276{
277 bool rc = FALSE;
278
279 if(pcmk_acl_required(user)) {
280 const char *value = NULL;
281 GHashTable *options = pcmk__strkey_table(free, free);
282
283 cib_read_config(options, xml);
284 value = cib_pref(options, "enable-acl");
285 rc = crm_is_true(value);
286 g_hash_table_destroy(options);
287 }
288
289 crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
290 return rc;
291}
292
303static bool
304should_copy_cib(const char *op, const char *section, int call_options)
305{
306 if (pcmk_is_set(call_options, cib_dryrun)) {
307 // cib_dryrun implies a scratch copy by definition; no side effects
308 return true;
309 }
310
311 if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
312 /* Commit-transaction must make a copy for atomicity. We must revert to
313 * the original CIB if the entire transaction cannot be applied
314 * successfully.
315 */
316 return true;
317 }
318
319 if (pcmk_is_set(call_options, cib_transaction)) {
320 /* If cib_transaction is set, then we're in the process of committing a
321 * transaction. The commit-transaction request already made a scratch
322 * copy, and we're accumulating changes in that copy.
323 */
324 return false;
325 }
326
327 if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_none)) {
328 /* Copying large CIBs accounts for a huge percentage of our CIB usage,
329 * and this avoids some of it.
330 *
331 * @TODO: Is this safe? See discussion at
332 * https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
333 */
334 return false;
335 }
336
337 // Default behavior is to operate on a scratch copy
338 return true;
339}
340
341int
342cib_perform_op(const char *op, int call_options, cib__op_fn_t fn, bool is_query,
343 const char *section, xmlNode *req, xmlNode *input,
344 bool manage_counters, bool *config_changed,
345 xmlNode **current_cib, xmlNode **result_cib, xmlNode **diff,
346 xmlNode **output)
347{
348 int rc = pcmk_ok;
349 bool check_schema = true;
350 bool make_copy = true;
351 xmlNode *top = NULL;
352 xmlNode *scratch = NULL;
353 xmlNode *patchset_cib = NULL;
354 xmlNode *local_diff = NULL;
355
356 const char *new_version = NULL;
357 const char *user = crm_element_value(req, F_CIB_USER);
358 bool with_digest = false;
359
360 crm_trace("Begin %s%s%s op",
361 (pcmk_is_set(call_options, cib_dryrun)? "dry run of " : ""),
362 (is_query? "read-only " : ""), op);
363
364 CRM_CHECK(output != NULL, return -ENOMSG);
365 CRM_CHECK(current_cib != NULL, return -ENOMSG);
366 CRM_CHECK(result_cib != NULL, return -ENOMSG);
367 CRM_CHECK(config_changed != NULL, return -ENOMSG);
368
369 if(output) {
370 *output = NULL;
371 }
372
373 *result_cib = NULL;
374 *config_changed = false;
375
376 if (fn == NULL) {
377 return -EINVAL;
378 }
379
380 if (is_query) {
381 xmlNode *cib_ro = *current_cib;
382 xmlNode *cib_filtered = NULL;
383
384 if (cib_acl_enabled(cib_ro, user)
385 && xml_acl_filtered_copy(user, *current_cib, *current_cib,
386 &cib_filtered)) {
387
388 if (cib_filtered == NULL) {
389 crm_debug("Pre-filtered the entire cib");
390 return -EACCES;
391 }
392 cib_ro = cib_filtered;
393 crm_log_xml_trace(cib_ro, "filtered");
394 }
395
396 rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
397
398 if(output == NULL || *output == NULL) {
399 /* nothing */
400
401 } else if(cib_filtered == *output) {
402 cib_filtered = NULL; /* Let them have this copy */
403
404 } else if (*output == *current_cib) {
405 /* They already know not to free it */
406
407 } else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
408 /* We're about to free the document of which *output is a part */
409 *output = copy_xml(*output);
410
411 } else if ((*output)->doc == (*current_cib)->doc) {
412 /* Give them a copy they can free */
413 *output = copy_xml(*output);
414 }
415
416 free_xml(cib_filtered);
417 return rc;
418 }
419
420 make_copy = should_copy_cib(op, section, call_options);
421
422 if (!make_copy) {
423 /* Conditional on v2 patch style */
424
425 scratch = *current_cib;
426
427 // Make a copy of the top-level element to store version details
428 top = create_xml_node(NULL, (const char *) scratch->name);
429 copy_in_properties(top, scratch);
430 patchset_cib = top;
431
432 xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
433 rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
434
435 /* If scratch points to a new object now (for example, after an erase
436 * operation), then *current_cib should point to the same object.
437 */
438 *current_cib = scratch;
439
440 } else {
441 scratch = copy_xml(*current_cib);
442 patchset_cib = *current_cib;
443
444 xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
445 rc = (*fn) (op, call_options, section, req, input, *current_cib,
446 &scratch, output);
447
448 if ((scratch != NULL) && !xml_tracking_changes(scratch)) {
449 crm_trace("Inferring changes after %s op", op);
450 xml_track_changes(scratch, user, *current_cib,
451 cib_acl_enabled(*current_cib, user));
452 xml_calculate_changes(*current_cib, scratch);
453 }
454 CRM_CHECK(*current_cib != scratch, return -EINVAL);
455 }
456
457 xml_acl_disable(scratch); /* Allow the system to make any additional changes */
458
459 if (rc == pcmk_ok && scratch == NULL) {
460 rc = -EINVAL;
461 goto done;
462
463 } else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
464 crm_trace("ACL rejected part or all of the proposed changes");
465 rc = -EACCES;
466 goto done;
467
468 } else if (rc != pcmk_ok) {
469 goto done;
470 }
471
472 if (scratch) {
473 new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION);
474
475 if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) {
476 crm_err("Discarding update with feature set '%s' greater than our own '%s'",
477 new_version, CRM_FEATURE_SET);
478 rc = -EPROTONOSUPPORT;
479 goto done;
480 }
481 }
482
483 if (patchset_cib != NULL) {
484 int old = 0;
485 int new = 0;
486
489
490 if (old > new) {
491 crm_err("%s went backwards: %d -> %d (Opts: %#x)",
492 XML_ATTR_GENERATION_ADMIN, old, new, call_options);
493 crm_log_xml_warn(req, "Bad Op");
494 crm_log_xml_warn(input, "Bad Data");
495 rc = -pcmk_err_old_data;
496
497 } else if (old == new) {
499 crm_element_value_int(patchset_cib, XML_ATTR_GENERATION, &old);
500 if (old > new) {
501 crm_err("%s went backwards: %d -> %d (Opts: %#x)",
502 XML_ATTR_GENERATION, old, new, call_options);
503 crm_log_xml_warn(req, "Bad Op");
504 crm_log_xml_warn(input, "Bad Data");
505 rc = -pcmk_err_old_data;
506 }
507 }
508 }
509
510 crm_trace("Massaging CIB contents");
511 pcmk__strip_xml_text(scratch);
513
514 if (!make_copy) {
515 /* At this point, patchset_cib is just the "cib" tag and its properties.
516 *
517 * The v1 format would barf on this, but we know the v2 patch
518 * format only needs it for the top-level version fields
519 */
520 local_diff = xml_create_patchset(2, patchset_cib, scratch,
521 config_changed, manage_counters);
522
523 } else {
524 static time_t expires = 0;
525 time_t tm_now = time(NULL);
526
527 if (expires < tm_now) {
528 expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */
529 with_digest = true;
530 }
531
532 local_diff = xml_create_patchset(0, patchset_cib, scratch,
533 config_changed, manage_counters);
534 }
535
537 xml_accept_changes(scratch);
538
539 if(local_diff) {
540 patchset_process_digest(local_diff, patchset_cib, scratch, with_digest);
541 pcmk__log_xml_patchset(LOG_INFO, local_diff);
542 crm_log_xml_trace(local_diff, "raw patch");
543 }
544
545 if (make_copy && (local_diff != NULL)) {
546 // Original to compare against doesn't exist
548 {
549 // Validate the calculated patch set
550 int test_rc = pcmk_ok;
551 int format = 1;
552 xmlNode *cib_copy = copy_xml(patchset_cib);
553
554 crm_element_value_int(local_diff, PCMK_XA_FORMAT, &format);
555 test_rc = xml_apply_patchset(cib_copy, local_diff,
556 manage_counters);
557
558 if (test_rc != pcmk_ok) {
559 save_xml_to_file(cib_copy, "PatchApply:calculated", NULL);
560 save_xml_to_file(patchset_cib, "PatchApply:input", NULL);
561 save_xml_to_file(scratch, "PatchApply:actual", NULL);
562 save_xml_to_file(local_diff, "PatchApply:diff", NULL);
563 crm_err("v%d patchset error, patch failed to apply: %s "
564 "(%d)",
565 format, pcmk_rc_str(pcmk_legacy2rc(test_rc)),
566 test_rc);
567 }
568 free_xml(cib_copy);
569 },
570 {}
571 );
572 }
573
574 if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
575 /* Throttle the amount of costly validation we perform due to status updates
576 * a) we don't really care whats in the status section
577 * b) we don't validate any of its contents at the moment anyway
578 */
579 check_schema = false;
580 }
581
582 /* === scratch must not be modified after this point ===
583 * Exceptions, anything in:
584
585 static filter_t filter[] = {
586 { 0, XML_ATTR_ORIGIN },
587 { 0, XML_CIB_ATTR_WRITTEN },
588 { 0, XML_ATTR_UPDATE_ORIG },
589 { 0, XML_ATTR_UPDATE_CLIENT },
590 { 0, XML_ATTR_UPDATE_USER },
591 };
592 */
593
594 if (*config_changed && !pcmk_is_set(call_options, cib_no_mtime)) {
595 const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION);
596
598 if (schema) {
599 static int minimum_schema = 0;
600 int current_schema = get_schema_version(schema);
601
602 if (minimum_schema == 0) {
603 minimum_schema = get_schema_version("pacemaker-1.2");
604 }
605
606 /* Does the CIB support the "update-*" attributes... */
607 if (current_schema >= minimum_schema) {
608 /* Ensure values of origin, client, and user in scratch match
609 * the values in req
610 */
611 const char *origin = crm_element_value(req, F_ORIG);
612 const char *client = crm_element_value(req, F_CIB_CLIENTNAME);
613
614 if (origin != NULL) {
615 crm_xml_add(scratch, XML_ATTR_UPDATE_ORIG, origin);
616 } else {
618 }
619
620 if (client != NULL) {
621 crm_xml_add(scratch, XML_ATTR_UPDATE_CLIENT, user);
622 } else {
624 }
625
626 if (user != NULL) {
627 crm_xml_add(scratch, XML_ATTR_UPDATE_USER, user);
628 } else {
630 }
631 }
632 }
633 }
634
635 crm_trace("Perform validation: %s", pcmk__btoa(check_schema));
636 if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, true)) {
637 const char *current_schema = crm_element_value(scratch,
639
640 crm_warn("Updated CIB does not validate against %s schema",
641 pcmk__s(current_schema, "unspecified"));
643 }
644
645 done:
646
647 *result_cib = scratch;
648
649 /* @TODO: This may not work correctly with !make_copy, since we don't
650 * keep the original CIB.
651 */
652 if ((rc != pcmk_ok) && cib_acl_enabled(patchset_cib, user)
653 && xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) {
654
655 if (*result_cib == NULL) {
656 crm_debug("Pre-filtered the entire cib result");
657 }
658 free_xml(scratch);
659 }
660
661 if(diff) {
662 *diff = local_diff;
663 } else {
664 free_xml(local_diff);
665 }
666
667 free_xml(top);
668 crm_trace("Done");
669 return rc;
670}
671
672int
673cib__create_op(cib_t *cib, const char *op, const char *host,
674 const char *section, xmlNode *data, int call_options,
675 const char *user_name, const char *client_name,
676 xmlNode **op_msg)
677{
678 CRM_CHECK((cib != NULL) && (op_msg != NULL), return -EPROTO);
679
680 *op_msg = create_xml_node(NULL, T_CIB_COMMAND);
681 if (*op_msg == NULL) {
682 return -EPROTO;
683 }
684
685 cib->call_id++;
686 if (cib->call_id < 1) {
687 cib->call_id = 1;
688 }
689
691 crm_xml_add(*op_msg, F_TYPE, T_CIB);
692 crm_xml_add(*op_msg, F_CIB_OPERATION, op);
693 crm_xml_add(*op_msg, F_CIB_HOST, host);
694 crm_xml_add(*op_msg, F_CIB_SECTION, section);
695 crm_xml_add(*op_msg, F_CIB_USER, user_name);
696 crm_xml_add(*op_msg, F_CIB_CLIENTNAME, client_name);
697 crm_xml_add_int(*op_msg, F_CIB_CALLID, cib->call_id);
698
699 crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
700 crm_xml_add_int(*op_msg, F_CIB_CALLOPTS, call_options);
701
702 if (data != NULL) {
704 }
705
706 if (pcmk_is_set(call_options, cib_inhibit_bcast)) {
708 free_xml(*op_msg); return -EPROTO);
709 }
710 return pcmk_ok;
711}
712
721static int
722validate_transaction_request(const xmlNode *request)
723{
724 const char *op = crm_element_value(request, F_CIB_OPERATION);
725 const char *host = crm_element_value(request, F_CIB_HOST);
726 const cib__operation_t *operation = NULL;
727 int rc = cib__get_operation(op, &operation);
728
729 if (rc != pcmk_rc_ok) {
730 // cib__get_operation() logs error
731 return rc;
732 }
733
734 if (!pcmk_is_set(operation->flags, cib__op_attr_transaction)) {
735 crm_err("Operation %s is not supported in CIB transactions", op);
736 return EOPNOTSUPP;
737 }
738
739 if (host != NULL) {
740 crm_err("Operation targeting a specific node (%s) is not supported in "
741 "a CIB transaction",
742 host);
743 return EOPNOTSUPP;
744 }
745 return pcmk_rc_ok;
746}
747
757int
758cib__extend_transaction(cib_t *cib, xmlNode *request)
759{
760 int rc = pcmk_rc_ok;
761
762 CRM_ASSERT((cib != NULL) && (request != NULL));
763
764 rc = validate_transaction_request(request);
765
766 if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
768 }
769
770 if (rc == pcmk_rc_ok) {
771 add_node_copy(cib->transaction, request);
772
773 } else {
774 const char *op = crm_element_value(request, F_CIB_OPERATION);
775 const char *client_id = NULL;
776
777 cib->cmds->client_id(cib, NULL, &client_id);
778 crm_err("Failed to add '%s' operation to transaction for client %s: %s",
779 op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
780 crm_log_xml_info(request, "failed");
781 }
782 return pcmk_rc2legacy(rc);
783}
784
785void
786cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
787{
788 xmlNode *output = NULL;
789 cib_callback_client_t *blob = NULL;
790
791 if (msg != NULL) {
793 crm_element_value_int(msg, F_CIB_CALLID, &call_id);
794 output = get_message_xml(msg, F_CIB_CALLDATA);
795 }
796
797 blob = cib__lookup_id(call_id);
798
799 if (blob == NULL) {
800 crm_trace("No callback found for call %d", call_id);
801 }
802
803 if (cib == NULL) {
804 crm_debug("No cib object supplied");
805 }
806
807 if (rc == -pcmk_err_diff_resync) {
808 /* This is an internal value that clients do not and should not care about */
809 rc = pcmk_ok;
810 }
811
812 if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
813 crm_trace("Invoking callback %s for call %d",
814 pcmk__s(blob->id, "without ID"), call_id);
815 blob->callback(msg, call_id, rc, output, blob->user_data);
816
817 } else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
818 crm_warn("CIB command failed: %s", pcmk_strerror(rc));
819 crm_log_xml_debug(msg, "Failed CIB Update");
820 }
821
822 /* This may free user_data, so do it after the callback */
823 if (blob) {
824 remove_cib_op_callback(call_id, FALSE);
825 }
826
827 if (cib && cib->op_callback != NULL) {
828 crm_trace("Invoking global callback for call %d", call_id);
829 cib->op_callback(msg, call_id, rc, output);
830 }
831 crm_trace("OP callback activated for %d", call_id);
832}
833
834void
835cib_native_notify(gpointer data, gpointer user_data)
836{
837 xmlNode *msg = user_data;
838 cib_notify_client_t *entry = data;
839 const char *event = NULL;
840
841 if (msg == NULL) {
842 crm_warn("Skipping callback - NULL message");
843 return;
844 }
845
846 event = crm_element_value(msg, F_SUBTYPE);
847
848 if (entry == NULL) {
849 crm_warn("Skipping callback - NULL callback client");
850 return;
851
852 } else if (entry->callback == NULL) {
853 crm_warn("Skipping callback - NULL callback");
854 return;
855
856 } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
857 crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
858 return;
859 }
860
861 crm_trace("Invoking callback for %p/%s event...", entry, event);
862 entry->callback(event, msg);
863 crm_trace("Callback invoked...");
864}
865
866static pcmk__cluster_option_t cib_opts[] = {
867 /* name, legacy name, type, allowed values,
868 * default value, validator,
869 * short description,
870 * long description
871 */
872 {
873 "enable-acl", NULL, "boolean", NULL,
874 "false", pcmk__valid_boolean,
875 N_("Enable Access Control Lists (ACLs) for the CIB"),
876 NULL
877 },
878 {
879 "cluster-ipc-limit", NULL, "integer", NULL,
881 N_("Maximum IPC message backlog before disconnecting a cluster daemon"),
882 N_("Raise this if log has \"Evicting client\" messages for cluster daemon"
883 " PIDs (a good value is the number of resources in the cluster"
884 " multiplied by the number of nodes).")
885 },
886};
887
888void
890{
891 const char *desc_short = "Cluster Information Base manager options";
892 const char *desc_long = "Cluster options used by Pacemaker's Cluster "
893 "Information Base manager";
894
895 gchar *s = pcmk__format_option_metadata("pacemaker-based", desc_short,
896 desc_long, cib_opts,
897 PCMK__NELEM(cib_opts));
898 printf("%s", s);
899 g_free(s);
900}
901
902static void
903verify_cib_options(GHashTable *options)
904{
905 pcmk__validate_cluster_options(options, cib_opts, PCMK__NELEM(cib_opts));
906}
907
908const char *
909cib_pref(GHashTable * options, const char *name)
910{
911 return pcmk__cluster_option(options, cib_opts, PCMK__NELEM(cib_opts),
912 name);
913}
914
915gboolean
916cib_read_config(GHashTable * options, xmlNode * current_cib)
917{
918 xmlNode *config = NULL;
919 crm_time_t *now = NULL;
920
921 if (options == NULL || current_cib == NULL) {
922 return FALSE;
923 }
924
925 now = crm_time_new(NULL);
926
927 g_hash_table_remove_all(options);
928
929 config = pcmk_find_cib_element(current_cib, XML_CIB_TAG_CRMCONFIG);
930 if (config) {
931 pe_unpack_nvpairs(current_cib, config, XML_CIB_TAG_PROPSET, NULL,
932 options, CIB_OPTIONS_FIRST, TRUE, now, NULL);
933 }
934
935 verify_cib_options(options);
936
937 crm_time_free(now);
938
939 return TRUE;
940}
941
942int
943cib_internal_op(cib_t * cib, const char *op, const char *host,
944 const char *section, xmlNode * data,
945 xmlNode ** output_data, int call_options, const char *user_name)
946{
947 int (*delegate) (cib_t * cib, const char *op, const char *host,
948 const char *section, xmlNode * data,
949 xmlNode ** output_data, int call_options, const char *user_name) =
950 cib->delegate_fn;
951
952 if(user_name == NULL) {
953 user_name = getenv("CIB_user");
954 }
955
956 return delegate(cib, op, host, section, data, output_data, call_options, user_name);
957}
958
970int
971cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
972 int level)
973{
974 int rc = pcmk_err_generic;
975
976 xmlNode *diff = NULL;
977
978 CRM_ASSERT(event);
980 CRM_ASSERT(output);
981
982 crm_element_value_int(event, F_CIB_RC, &rc);
984
985 if (rc < pcmk_ok || diff == NULL) {
986 return rc;
987 }
988
989 if (level > LOG_CRIT) {
990 pcmk__log_xml_patchset(level, diff);
991 }
992
993 if (input != NULL) {
994 rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
995 NULL);
996
997 if (rc != pcmk_ok) {
998 crm_debug("Update didn't apply: %s (%d) %p",
999 pcmk_strerror(rc), rc, *output);
1000
1001 if (rc == -pcmk_err_old_data) {
1002 crm_trace("Masking error, we already have the supplied update");
1003 return pcmk_ok;
1004 }
1005 free_xml(*output);
1006 *output = NULL;
1007 return rc;
1008 }
1009 }
1010 return rc;
1011}
1012
1013#define log_signon_query_err(out, fmt, args...) do { \
1014 if (out != NULL) { \
1015 out->err(out, fmt, ##args); \
1016 } else { \
1017 crm_err(fmt, ##args); \
1018 } \
1019 } while (0)
1020
1021int
1022cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
1023{
1024 int rc = pcmk_rc_ok;
1025 cib_t *cib_conn = NULL;
1026
1027 CRM_ASSERT(cib_object != NULL);
1028
1029 if (cib == NULL) {
1030 cib_conn = cib_new();
1031 } else {
1032 if (*cib == NULL) {
1033 *cib = cib_new();
1034 }
1035 cib_conn = *cib;
1036 }
1037
1038 if (cib_conn == NULL) {
1039 return ENOMEM;
1040 }
1041
1042 if (cib_conn->state == cib_disconnected) {
1043 rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
1044 rc = pcmk_legacy2rc(rc);
1045 }
1046
1047 if (rc != pcmk_rc_ok) {
1048 log_signon_query_err(out, "Could not connect to the CIB: %s",
1049 pcmk_rc_str(rc));
1050 goto done;
1051 }
1052
1053 if (out != NULL) {
1054 out->transient(out, "Querying CIB...");
1055 }
1056 rc = cib_conn->cmds->query(cib_conn, NULL, cib_object,
1058 rc = pcmk_legacy2rc(rc);
1059
1060 if (rc != pcmk_rc_ok) {
1061 log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
1062 }
1063
1064done:
1065 if (cib == NULL) {
1066 cib__clean_up_connection(&cib_conn);
1067 }
1068
1069 if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
1070 return pcmk_rc_no_input;
1071 }
1072 return rc;
1073}
1074
1075int
1077{
1078 int rc;
1079
1080 if (*cib == NULL) {
1081 return pcmk_rc_ok;
1082 }
1083
1084 rc = (*cib)->cmds->signoff(*cib);
1085 cib_delete(*cib);
1086 *cib = NULL;
1087 return pcmk_legacy2rc(rc);
1088}
1089
1090// Deprecated functions kept only for backward API compatibility
1091// LCOV_EXCL_START
1092
1093#include <crm/cib/util_compat.h>
1094
1095const char *
1096get_object_path(const char *object_type)
1097{
1098 return pcmk_cib_xpath_for(object_type);
1099}
1100
1101const char *
1102get_object_parent(const char *object_type)
1103{
1104 return pcmk_cib_parent_name_for(object_type);
1105}
1106
1107xmlNode *
1108get_object_root(const char *object_type, xmlNode *the_root)
1109{
1110 return pcmk_find_cib_element(the_root, object_type);
1111}
1112
1113// LCOV_EXCL_STOP
1114// End deprecated API
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition acl.c:605
bool pcmk_acl_required(const char *user)
Check whether ACLs are required for a given user.
Definition acl.c:743
void xml_acl_disable(xmlNode *xml)
Definition acl.c:616
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:435
#define F_CIB_CALLID
Definition internal.h:38
#define F_CIB_CALLOPTS
Definition internal.h:37
#define F_CIB_USER
Definition internal.h:59
int cib__get_operation(const char *op, const cib__operation_t **operation)
Definition cib_ops.c:142
#define F_CIB_OPERATION
Definition internal.h:40
#define PCMK__CIB_REQUEST_COMMIT_TRANSACT
Definition internal.h:34
@ cib__op_attr_transaction
Supported in a transaction.
Definition internal.h:85
#define T_CIB
Definition internal.h:64
#define F_CIB_HOST
Definition internal.h:43
#define F_CIB_RC
Definition internal.h:44
#define F_CIB_CLIENTNAME
Definition internal.h:55
#define F_CIB_SECTION
Definition internal.h:42
#define F_CIB_CALLDATA
Definition internal.h:39
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:767
cib_callback_client_t * cib__lookup_id(int call_id)
Definition cib_client.c:825
#define F_CIB_UPDATE_RESULT
Definition internal.h:54
int(* cib__op_fn_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **)
Definition internal.h:120
#define T_CIB_COMMAND
Definition internal.h:65
Deprecated Pacemaker configuration utilities.
const char * name
Definition cib.c:26
void cib_delete(cib_t *cib)
Free all memory used by CIB connection.
Definition cib_client.c:778
cib_t * cib_new(void)
Create a new CIB connection object.
Definition cib_client.c:605
void remove_cib_op_callback(int call_id, gboolean all_callbacks)
Definition cib_client.c:787
const char * pcmk__cib_abs_xpath_for(const char *element)
Definition cib.c:133
@ cib_command
Definition cib_types.h:47
@ cib_scope_local
Definition cib_types.h:82
@ cib_none
Definition cib_types.h:54
@ cib_transaction
Process request when the client commits the active transaction.
Definition cib_types.h:100
@ cib_sync_call
Definition cib_types.h:102
@ cib_dryrun
Definition cib_types.h:84
@ cib_no_mtime
Definition cib_types.h:103
@ cib_inhibit_bcast
Definition cib_types.h:118
@ cib_disconnected
Definition cib_types.h:43
#define XPATH_DIFF_V1
Definition cib_utils.c:119
int cib__extend_transaction(cib_t *cib, xmlNode *request)
Definition cib_utils.c:758
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:971
int cib__clean_up_connection(cib_t **cib)
Definition cib_utils.c:1076
void cib_native_callback(cib_t *cib, xmlNode *msg, int call_id, int rc)
Definition cib_utils.c:786
int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
Definition cib_utils.c:91
#define log_signon_query_err(out, fmt, args...)
Definition cib_utils.c:1013
int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
Definition cib_utils.c:1022
const char * cib_pref(GHashTable *options, const char *name)
Definition cib_utils.c:909
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:673
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:943
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:62
int cib_perform_op(const char *op, int 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:342
const char * get_object_path(const char *object_type)
Definition cib_utils.c:1096
gboolean cib_version_details(xmlNode *cib, int *admin_epoch, int *epoch, int *updates)
Definition cib_utils.c:44
xmlNode * cib_get_generation(cib_t *cib)
Definition cib_utils.c:29
const char * get_object_parent(const char *object_type)
Definition cib_utils.c:1102
xmlNode * createEmptyCib(int cib_epoch)
Create XML for a new (empty) CIB.
Definition cib_utils.c:238
gboolean cib_read_config(GHashTable *options, xmlNode *current_cib)
Definition cib_utils.c:916
xmlNode * get_object_root(const char *object_type, xmlNode *the_root)
Definition cib_utils.c:1108
bool cib__element_in_patchset(const xmlNode *patchset, const char *element)
Definition cib_utils.c:209
void cib_metadata(void)
Definition cib_utils.c:889
void cib_native_notify(gpointer data, gpointer user_data)
Definition cib_utils.c:835
const char * pcmk_cib_parent_name_for(const char *element_name)
Get the parent element name of a given CIB element name.
Definition cib.c:150
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition cib.c:172
const char * pcmk_cib_xpath_for(const char *element_name)
Get the relative XPath needed to find a specified CIB element name.
Definition cib.c:112
#define PCMK__NELEM(a)
Definition internal.h:46
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int compare_version(const char *version1, const char *version2)
Definition utils.c:189
gboolean crm_is_true(const char *s)
Definition strings.c:416
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:99
#define PCMK__RESOURCE_STICKINESS_DEFAULT
Definition config.h:565
pcmk__cpg_host_t host
Definition cpg.c:4
char data[0]
Definition cpg.c:10
A dumping ground.
#define CRM_FEATURE_SET
Definition crm.h:70
char * crm_system_name
Definition utils.c:51
#define N_(String)
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:109
struct crm_time_s crm_time_t
Definition iso8601.h:32
#define crm_log_xml_info(xml, text)
Definition logging.h:391
#define crm_warn(fmt, args...)
Definition logging.h:380
#define CRM_XS
Definition logging.h:56
#define crm_log_xml_debug(xml, text)
Definition logging.h:392
#define CRM_CHECK(expr, failure_action)
Definition logging.h:238
#define crm_debug(fmt, args...)
Definition logging.h:384
#define crm_err(fmt, args...)
Definition logging.h:379
#define crm_log_xml_trace(xml, text)
Definition logging.h:393
#define crm_log_xml_warn(xml, text)
Definition logging.h:389
#define crm_trace(fmt, args...)
Definition logging.h:385
#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)
#define F_SUBTYPE
Definition msg_xml.h:87
#define XML_CIB_TAG_GENERATION_TUPPLE
Definition msg_xml.h:413
#define XML_TAG_CIB
Definition msg_xml.h:137
#define XML_CIB_TAG_RESOURCES
Definition msg_xml.h:205
#define XML_ATTR_CRM_VERSION
Definition msg_xml.h:140
#define PCMK_XA_FORMAT
Definition msg_xml.h:51
#define XML_DIFF_PATH
Definition msg_xml.h:471
#define XML_NVPAIR_ATTR_VALUE
Definition msg_xml.h:394
#define XML_CIB_TAG_RSCCONFIG
Definition msg_xml.h:210
#define XML_DIFF_CHANGE
Definition msg_xml.h:466
#define F_XML_TAGNAME
Definition msg_xml.h:99
#define XML_CIB_TAG_CRMCONFIG
Definition msg_xml.h:208
#define XML_RSC_ATTR_STICKINESS
Definition msg_xml.h:252
#define XML_ATTR_ID
Definition msg_xml.h:156
#define XML_CIB_TAG_CONSTRAINTS
Definition msg_xml.h:207
#define F_ORIG
Definition msg_xml.h:79
#define XML_TAG_META_SETS
Definition msg_xml.h:228
#define XML_ATTR_UPDATE_CLIENT
Definition msg_xml.h:164
#define XML_CIB_TAG_CONFIGURATION
Definition msg_xml.h:203
#define F_TYPE
Definition msg_xml.h:91
#define XML_CIB_TAG_PROPSET
Definition msg_xml.h:226
#define XML_ATTR_VALIDATION
Definition msg_xml.h:142
#define XML_NVPAIR_ATTR_NAME
Definition msg_xml.h:393
#define XML_ATTR_GENERATION_ADMIN
Definition msg_xml.h:148
#define XML_ATTR_NUMUPDATES
Definition msg_xml.h:149
#define CIB_OPTIONS_FIRST
Definition msg_xml.h:110
#define XML_CIB_TAG_NODES
Definition msg_xml.h:206
#define XML_CIB_TAG_NVPAIR
Definition msg_xml.h:224
#define XML_CIB_TAG_STATUS
Definition msg_xml.h:204
#define XML_ATTR_UPDATE_ORIG
Definition msg_xml.h:163
#define XML_ATTR_UPDATE_USER
Definition msg_xml.h:165
#define XML_ATTR_GENERATION
Definition msg_xml.h:147
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:447
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition nvpair.c:483
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition nvpair.c:349
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition nvpair.c:302
bool pcmk__valid_boolean(const char *value)
Definition options.c:187
gchar * pcmk__format_option_metadata(const char *name, const char *desc_short, const char *desc_long, pcmk__cluster_option_t *option_list, int len)
Definition options.c:426
const char * pcmk__cluster_option(GHashTable *options, const pcmk__cluster_option_t *option_list, int len, const char *name)
Definition options.c:350
void pcmk__validate_cluster_options(GHashTable *options, pcmk__cluster_option_t *option_list, int len)
Definition options.c:501
bool pcmk__valid_positive_number(const char *value)
Definition options.c:209
const char * pcmk_strerror(int rc)
Definition results.c:149
#define pcmk_err_old_data
Definition results.h:75
#define pcmk_err_generic
Definition results.h:71
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:501
#define pcmk_err_schema_validation
Definition results.h:73
@ pcmk_rc_no_input
Definition results.h:123
@ pcmk_rc_no_transaction
Definition results.h:113
@ pcmk_rc_ok
Definition results.h:154
#define pcmk_ok
Definition results.h:68
int pcmk_rc2legacy(int rc)
Definition results.c:546
#define pcmk_err_diff_resync
Definition results.h:77
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:559
void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition rules.c:535
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:608
@ pcmk__str_regex
@ pcmk__str_none
@ pcmk__str_casei
uint32_t flags
Group of enum cib__op_attr flags.
Definition internal.h:126
int(* signon)(cib_t *cib, const char *name, enum cib_conn_type type)
Definition cib_types.h:126
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition cib_types.h:156
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:251
const char * id
Definition internal.h:139
void(* callback)(xmlNode *, int, int, xmlNode *, void *)
Definition internal.h:138
const char * event
Definition internal.h:130
void(* callback)(const char *event, xmlNode *msg)
Definition internal.h:133
enum cib_state state
Definition cib_types.h:330
xmlNode * transaction
Definition cib_types.h:347
void * delegate_fn
Definition cib_types.h:337
cib_api_operations_t * cmds
Definition cib_types.h:345
int call_id
Definition cib_types.h:334
void(* op_callback)(const xmlNode *msg, int call_id, int rc, xmlNode *output)
Definition cib_types.h:342
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
Wrappers for and extensions to libxml2.
int get_schema_version(const char *name)
Definition schemas.c:967
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition xml.c:2484
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
Definition xml.c:1718
void copy_in_properties(xmlNode *target, const xmlNode *src)
Definition xml.c:456
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
Definition patchset.c:572
void fix_plus_plus_recursive(xmlNode *target)
Parse integer assignment statements on this node and all its child nodes.
Definition xml.c:489
void xml_accept_changes(xmlNode *xml)
Definition xml.c:359
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
Definition patchset.c:385
const char * xml_latest_schema(void)
Definition schemas.c:113
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition xml.c:2510
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
Definition patchset.c:1099
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition xml.c:2068
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition xpath.c:39
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
Definition messages.c:160
bool xml_tracking_changes(xmlNode *xml)
Definition xml.c:278
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition xml.c:263
void free_xml(xmlNode *child)
Definition xml.c:783
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition schemas.c:673
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version)
Definition patchset.c:328
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition xml.c:622
xmlNode * get_message_xml(const xmlNode *msg, const char *field)
Definition messages.c:154
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path)
Definition xpath.c:139
xmlNode * copy_xml(xmlNode *src_node)
Definition xml.c:789
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:638
void xml_remove_prop(xmlNode *obj, const char *name)
Definition xml.c:1696
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition xml.c:1089
void pcmk__strip_xml_text(xmlNode *xml)
Definition xml.c:962