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