pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
cib_ops.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <time.h>
18 
19 #include <sys/param.h>
20 #include <sys/types.h>
21 
22 #include <glib.h>
23 #include <libxml/tree.h>
24 
25 #include <crm/crm.h>
26 #include <crm/cib/internal.h>
27 #include <crm/msg_xml.h>
28 
29 #include <crm/common/xml.h>
31 
32 // @TODO: Free this via crm_exit() when libcib gets merged with libcrmcommon
33 static GHashTable *operation_table = NULL;
34 
35 static const cib__operation_t cib_ops[] = {
36  {
39  },
40  {
45  },
46  {
51  },
52  {
58  },
59  {
64  },
65  {
70  },
71  {
77  },
78  {
81  },
82  {
87  },
88  {
90  },
91  {
93  },
94  {
95  // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support
98  },
99  {
101  },
102  {
109  },
110  {
113  },
114  {
116  },
117  {
119  },
120  {
122  },
123  {
129  },
130 };
131 
141 int
142 cib__get_operation(const char *op, const cib__operation_t **operation)
143 {
144  CRM_ASSERT((op != NULL) && (operation != NULL));
145 
146  if (operation_table == NULL) {
147  operation_table = pcmk__strkey_table(NULL, NULL);
148 
149  for (int lpc = 0; lpc < PCMK__NELEM(cib_ops); lpc++) {
150  const cib__operation_t *oper = &(cib_ops[lpc]);
151 
152  g_hash_table_insert(operation_table, (gpointer) oper->name,
153  (gpointer) oper);
154  }
155  }
156 
157  *operation = g_hash_table_lookup(operation_table, op);
158  if (*operation == NULL) {
159  crm_err("Operation %s is invalid", op);
160  return EINVAL;
161  }
162  return pcmk_rc_ok;
163 }
164 
165 int
166 cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
167  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
168 {
169  xmlNode *obj_root = NULL;
170  int result = pcmk_ok;
171 
172  crm_trace("Processing %s for %s section",
173  op, pcmk__s(section, "unspecified"));
174 
175  if (options & cib_xpath) {
176  return cib_process_xpath(op, options, section, req, input,
177  existing_cib, result_cib, answer);
178  }
179 
180  CRM_CHECK(*answer == NULL, free_xml(*answer));
181  *answer = NULL;
182 
183  if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
184  section = NULL;
185  }
186 
187  obj_root = pcmk_find_cib_element(existing_cib, section);
188 
189  if (obj_root == NULL) {
190  result = -ENXIO;
191 
192  } else if (options & cib_no_children) {
193  xmlNode *shallow = create_xml_node(*answer,
194  (const char *) obj_root->name);
195 
196  copy_in_properties(shallow, obj_root);
197  *answer = shallow;
198 
199  } else {
200  *answer = obj_root;
201  }
202 
203  if (result == pcmk_ok && *answer == NULL) {
204  crm_err("Error creating query response");
205  result = -ENOMSG;
206  }
207 
208  return result;
209 }
210 
211 static int
212 update_counter(xmlNode *xml_obj, const char *field, bool reset)
213 {
214  char *new_value = NULL;
215  char *old_value = NULL;
216  int int_value = -1;
217 
218  if (!reset && crm_element_value(xml_obj, field) != NULL) {
219  old_value = crm_element_value_copy(xml_obj, field);
220  }
221  if (old_value != NULL) {
222  int_value = atoi(old_value);
223  new_value = pcmk__itoa(++int_value);
224  } else {
225  new_value = strdup("1");
226  CRM_ASSERT(new_value != NULL);
227  }
228 
229  crm_trace("Update %s from %s to %s",
230  field, pcmk__s(old_value, "unset"), new_value);
231  crm_xml_add(xml_obj, field, new_value);
232 
233  free(new_value);
234  free(old_value);
235 
236  return pcmk_ok;
237 }
238 
239 int
240 cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
241  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
242 {
243  int result = pcmk_ok;
244 
245  crm_trace("Processing \"%s\" event", op);
246 
247  if (*result_cib != existing_cib) {
248  free_xml(*result_cib);
249  }
250  *result_cib = createEmptyCib(0);
251  copy_in_properties(*result_cib, existing_cib);
252  update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false);
253  *answer = NULL;
254 
255  return result;
256 }
257 
258 int
259 cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req,
260  xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
261  xmlNode ** answer)
262 {
263  int rc = 0;
264  int new_version = 0;
265  int current_version = 0;
266  int max_version = 0;
267  const char *max = crm_element_value(req, F_CIB_SCHEMA_MAX);
268  const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION);
269 
270  *answer = NULL;
271  crm_trace("Processing \"%s\" event with max=%s", op, max);
272 
273  if (value != NULL) {
274  current_version = get_schema_version(value);
275  }
276 
277  if (max) {
278  max_version = get_schema_version(max);
279  }
280 
281  rc = update_validation(result_cib, &new_version, max_version, TRUE,
282  !(options & cib_verbose));
283  if (new_version > current_version) {
284  update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false);
285  update_counter(*result_cib, XML_ATTR_GENERATION, true);
286  update_counter(*result_cib, XML_ATTR_NUMUPDATES, true);
287  return pcmk_ok;
288  }
289 
290  return rc;
291 }
292 
293 int
294 cib_process_bump(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
295  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
296 {
297  int result = pcmk_ok;
298 
299  crm_trace("Processing %s for epoch='%s'", op,
300  pcmk__s(crm_element_value(existing_cib, XML_ATTR_GENERATION), ""));
301 
302  *answer = NULL;
303  update_counter(*result_cib, XML_ATTR_GENERATION, false);
304 
305  return result;
306 }
307 
308 int
309 cib_process_replace(const char *op, int options, const char *section, xmlNode * req,
310  xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
311  xmlNode ** answer)
312 {
313  int result = pcmk_ok;
314 
315  crm_trace("Processing %s for %s section",
316  op, pcmk__s(section, "unspecified"));
317 
318  if (options & cib_xpath) {
319  return cib_process_xpath(op, options, section, req, input,
320  existing_cib, result_cib, answer);
321  }
322 
323  *answer = NULL;
324 
325  if (input == NULL) {
326  return -EINVAL;
327  }
328 
329  if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
330  section = NULL;
331 
332  } else if (pcmk__xe_is(input, section)) {
333  section = NULL;
334  }
335 
336  if (pcmk__xe_is(input, XML_TAG_CIB)) {
337  int updates = 0;
338  int epoch = 0;
339  int admin_epoch = 0;
340 
341  int replace_updates = 0;
342  int replace_epoch = 0;
343  int replace_admin_epoch = 0;
344 
345  const char *reason = NULL;
346  const char *peer = crm_element_value(req, F_ORIG);
347  const char *digest = crm_element_value(req, XML_ATTR_DIGEST);
348 
349  if (digest) {
350  const char *version = crm_element_value(req, XML_ATTR_CRM_VERSION);
351  char *digest_verify = calculate_xml_versioned_digest(input, FALSE, TRUE,
352  version ? version :
354 
355  if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) {
356  crm_err("Digest mis-match on replace from %s: %s vs. %s (expected)", peer,
357  digest_verify, digest);
358  reason = "digest mismatch";
359 
360  } else {
361  crm_info("Digest matched on replace from %s: %s", peer, digest);
362  }
363  free(digest_verify);
364 
365  } else {
366  crm_trace("No digest to verify");
367  }
368 
369  cib_version_details(existing_cib, &admin_epoch, &epoch, &updates);
370  cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates);
371 
372  if (replace_admin_epoch < admin_epoch) {
373  reason = XML_ATTR_GENERATION_ADMIN;
374 
375  } else if (replace_admin_epoch > admin_epoch) {
376  /* no more checks */
377 
378  } else if (replace_epoch < epoch) {
379  reason = XML_ATTR_GENERATION;
380 
381  } else if (replace_epoch > epoch) {
382  /* no more checks */
383 
384  } else if (replace_updates < updates) {
385  reason = XML_ATTR_NUMUPDATES;
386  }
387 
388  if (reason != NULL) {
389  crm_info("Replacement %d.%d.%d from %s not applied to %d.%d.%d:"
390  " current %s is greater than the replacement",
391  replace_admin_epoch, replace_epoch,
392  replace_updates, peer, admin_epoch, epoch, updates, reason);
394  } else {
395  crm_info("Replaced %d.%d.%d with %d.%d.%d from %s",
396  admin_epoch, epoch, updates,
397  replace_admin_epoch, replace_epoch, replace_updates, peer);
398  }
399 
400  if (*result_cib != existing_cib) {
401  free_xml(*result_cib);
402  }
403  *result_cib = copy_xml(input);
404 
405  } else {
406  xmlNode *obj_root = NULL;
407  gboolean ok = TRUE;
408 
409  obj_root = pcmk_find_cib_element(*result_cib, section);
410  ok = replace_xml_child(NULL, obj_root, input, FALSE);
411  if (ok == FALSE) {
412  crm_trace("No matching object to replace");
413  result = -ENXIO;
414  }
415  }
416 
417  return result;
418 }
419 
420 int
421 cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
422  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
423 {
424  xmlNode *obj_root = NULL;
425 
426  crm_trace("Processing \"%s\" event", op);
427 
428  if (options & cib_xpath) {
429  return cib_process_xpath(op, options, section, req, input,
430  existing_cib, result_cib, answer);
431  }
432 
433  if (input == NULL) {
434  crm_err("Cannot perform modification with no data");
435  return -EINVAL;
436  }
437 
438  obj_root = pcmk_find_cib_element(*result_cib, section);
439  if (pcmk__xe_is(input, section)) {
440  xmlNode *child = NULL;
441  for (child = pcmk__xml_first_child(input); child;
442  child = pcmk__xml_next(child)) {
443  if (replace_xml_child(NULL, obj_root, child, TRUE) == FALSE) {
444  crm_trace("No matching object to delete: %s=%s", child->name, ID(child));
445  }
446  }
447 
448  } else if (replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) {
449  crm_trace("No matching object to delete: %s=%s", input->name, ID(input));
450  }
451 
452  return pcmk_ok;
453 }
454 
455 int
456 cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
457  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
458 {
459  xmlNode *obj_root = NULL;
460 
461  crm_trace("Processing \"%s\" event", op);
462 
463  if (options & cib_xpath) {
464  return cib_process_xpath(op, options, section, req, input,
465  existing_cib, result_cib, answer);
466  }
467 
468  if (input == NULL) {
469  crm_err("Cannot perform modification with no data");
470  return -EINVAL;
471  }
472 
473  obj_root = pcmk_find_cib_element(*result_cib, section);
474  if (obj_root == NULL) {
475  xmlNode *tmp_section = NULL;
476  const char *path = pcmk_cib_parent_name_for(section);
477 
478  if (path == NULL) {
479  return -EINVAL;
480  }
481 
482  tmp_section = create_xml_node(NULL, section);
483  cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section,
484  NULL, result_cib, answer);
485  free_xml(tmp_section);
486 
487  obj_root = pcmk_find_cib_element(*result_cib, section);
488  }
489 
490  CRM_CHECK(obj_root != NULL, return -EINVAL);
491 
492  if (update_xml_child(obj_root, input) == FALSE) {
493  if (options & cib_can_create) {
494  add_node_copy(obj_root, input);
495  } else {
496  return -ENXIO;
497  }
498  }
499 
500  // @COMPAT cib_mixed_update is deprecated as of 2.1.7
501  if (pcmk_is_set(options, cib_mixed_update)) {
502  int max = 0, lpc;
503  xmlXPathObjectPtr xpathObj = xpath_search(*result_cib, "//@__delete__");
504 
505  if (xpathObj) {
506  max = numXpathResults(xpathObj);
507  crm_log_xml_trace(*result_cib, "Mixed result");
508  }
509 
510  for (lpc = 0; lpc < max; lpc++) {
511  xmlNode *match = getXpathResult(xpathObj, lpc);
512  xmlChar *match_path = xmlGetNodePath(match);
513 
514  crm_debug("Destroying %s", match_path);
515  free(match_path);
516  free_xml(match);
517  }
518 
519  freeXpathObject(xpathObj);
520  }
521  return pcmk_ok;
522 }
523 
524 static int
525 update_cib_object(xmlNode * parent, xmlNode * update)
526 {
527  int result = pcmk_ok;
528  xmlNode *target = NULL;
529  xmlNode *a_child = NULL;
530  const char *replace = NULL;
531  const char *object_id = NULL;
532  const char *object_name = NULL;
533 
534  CRM_CHECK(update != NULL, return -EINVAL);
535  CRM_CHECK(parent != NULL, return -EINVAL);
536 
537  object_name = (const char *) update->name;
538  CRM_CHECK(object_name != NULL, return -EINVAL);
539 
540  object_id = ID(update);
541  crm_trace("Processing update for <%s%s%s%s>", object_name,
542  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
543  pcmk__s(object_id, ""),
544  ((object_id == NULL)? "" : "'"));
545 
546  if (object_id == NULL) {
547  /* placeholder object */
548  target = find_xml_node(parent, object_name, FALSE);
549 
550  } else {
551  target = pcmk__xe_match(parent, object_name, XML_ATTR_ID, object_id);
552  }
553 
554  if (target == NULL) {
555  target = create_xml_node(parent, object_name);
556  }
557 
558  crm_trace("Found node <%s%s%s%s> to update", object_name,
559  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
560  pcmk__s(object_id, ""),
561  ((object_id == NULL)? "" : "'"));
562 
563  // @COMPAT: XML_CIB_ATTR_REPLACE is unused internally. Remove at break.
564  replace = crm_element_value(update, XML_CIB_ATTR_REPLACE);
565  if (replace != NULL) {
566  int last = 0;
567  int len = strlen(replace);
568 
569  for (int lpc = 0; lpc <= len; ++lpc) {
570  if (replace[lpc] == ',' || replace[lpc] == 0) {
571  if (last != lpc) {
572  char *replace_item = strndup(replace + last, lpc - last);
573  xmlNode *remove = find_xml_node(target, replace_item,
574  FALSE);
575 
576  if (remove != NULL) {
577  crm_trace("Replacing node <%s> in <%s>",
578  replace_item, target->name);
579  free_xml(remove);
580  }
581  free(replace_item);
582  }
583  last = lpc + 1;
584  }
585  }
588  }
589 
590  copy_in_properties(target, update);
591 
592  if (xml_acl_denied(target)) {
593  crm_notice("Cannot update <%s " XML_ATTR_ID "=%s>",
594  pcmk__s(object_name, "<null>"),
595  pcmk__s(object_id, "<null>"));
596  return -EACCES;
597  }
598 
599  crm_trace("Processing children of <%s%s%s%s>", object_name,
600  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
601  pcmk__s(object_id, ""),
602  ((object_id == NULL)? "" : "'"));
603 
604  for (a_child = pcmk__xml_first_child(update); a_child != NULL;
605  a_child = pcmk__xml_next(a_child)) {
606  int tmp_result = 0;
607 
608  crm_trace("Updating child <%s%s%s%s>", a_child->name,
609  ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
610  pcmk__s(ID(a_child), ""), ((ID(a_child) == NULL)? "" : "'"));
611 
612  tmp_result = update_cib_object(target, a_child);
613 
614  /* only the first error is likely to be interesting */
615  if (tmp_result != pcmk_ok) {
616  crm_err("Error updating child <%s%s%s%s>",
617  a_child->name,
618  ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
619  pcmk__s(ID(a_child), ""),
620  ((ID(a_child) == NULL)? "" : "'"));
621 
622  if (result == pcmk_ok) {
623  result = tmp_result;
624  }
625  }
626  }
627 
628  crm_trace("Finished handling update for <%s%s%s%s>", object_name,
629  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
630  pcmk__s(object_id, ""),
631  ((object_id == NULL)? "" : "'"));
632 
633  return result;
634 }
635 
636 static int
637 add_cib_object(xmlNode * parent, xmlNode * new_obj)
638 {
639  const char *object_name = NULL;
640  const char *object_id = NULL;
641  xmlNode *equiv_node = NULL;
642 
643  if ((parent == NULL) || (new_obj == NULL)) {
644  return -EINVAL;
645  }
646 
647  object_name = (const char *) new_obj->name;
648  if (object_name == NULL) {
649  return -EINVAL;
650  }
651 
652  object_id = ID(new_obj);
653 
654  crm_trace("Processing creation of <%s%s%s%s>", object_name,
655  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
656  pcmk__s(object_id, ""),
657  ((object_id == NULL)? "" : "'"));
658 
659  if (object_id == NULL) {
660  equiv_node = find_xml_node(parent, object_name, FALSE);
661  } else {
662  equiv_node = pcmk__xe_match(parent, object_name, XML_ATTR_ID,
663  object_id);
664  }
665  if (equiv_node != NULL) {
666  return -EEXIST;
667  }
668 
669  return update_cib_object(parent, new_obj);
670 }
671 
672 static bool
673 update_results(xmlNode *failed, xmlNode *target, const char *operation,
674  int return_code)
675 {
676  xmlNode *xml_node = NULL;
677  bool was_error = false;
678  const char *error_msg = NULL;
679 
680  if (return_code != pcmk_ok) {
681  error_msg = pcmk_strerror(return_code);
682 
683  was_error = true;
684  xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB);
685  add_node_copy(xml_node, target);
686 
689  (const char *) target->name);
690  crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation);
691  crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg);
692 
693  crm_warn("Action %s failed: %s (cde=%d)",
694  operation, error_msg, return_code);
695  }
696 
697  return was_error;
698 }
699 
700 int
701 cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
702  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
703 {
704  xmlNode *failed = NULL;
705  int result = pcmk_ok;
706  xmlNode *update_section = NULL;
707 
708  crm_trace("Processing %s for %s section",
709  op, pcmk__s(section, "unspecified"));
710  if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
711  section = NULL;
712 
713  } else if (pcmk__str_eq(XML_TAG_CIB, section, pcmk__str_casei)) {
714  section = NULL;
715 
716  } else if (pcmk__xe_is(input, XML_TAG_CIB)) {
717  section = NULL;
718  }
719 
720  CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL);
721 
722  if (input == NULL) {
723  crm_err("Cannot perform modification with no data");
724  return -EINVAL;
725  }
726 
727  if (section == NULL) {
728  return cib_process_modify(op, options, section, req, input, existing_cib, result_cib,
729  answer);
730  }
731 
732  failed = create_xml_node(NULL, XML_TAG_FAILED);
733 
734  update_section = pcmk_find_cib_element(*result_cib, section);
735  if (pcmk__xe_is(input, section)) {
736  xmlNode *a_child = NULL;
737 
738  for (a_child = pcmk__xml_first_child(input); a_child != NULL;
739  a_child = pcmk__xml_next(a_child)) {
740  result = add_cib_object(update_section, a_child);
741  if (update_results(failed, a_child, op, result)) {
742  break;
743  }
744  }
745 
746  } else {
747  result = add_cib_object(update_section, input);
748  update_results(failed, input, op, result);
749  }
750 
751  if ((result == pcmk_ok) && (failed->children != NULL)) {
752  result = -EINVAL;
753  }
754 
755  if (result != pcmk_ok) {
756  crm_log_xml_err(failed, "CIB Update failures");
757  *answer = failed;
758 
759  } else {
760  free_xml(failed);
761  }
762 
763  return result;
764 }
765 
766 int
767 cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
768  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
769 {
770  const char *originator = NULL;
771 
772  if (req != NULL) {
773  originator = crm_element_value(req, F_ORIG);
774  }
775 
776  crm_trace("Processing \"%s\" event from %s%s",
777  op, originator,
778  (pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
779 
780  if (*result_cib != existing_cib) {
781  free_xml(*result_cib);
782  }
783  *result_cib = copy_xml(existing_cib);
784 
785  return xml_apply_patchset(*result_cib, input, TRUE);
786 }
787 
788 // @COMPAT: v1-only
789 bool
790 cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
791 {
792  int lpc = 0, max = 0;
793  bool config_changes = false;
794  xmlXPathObject *xpathObj = NULL;
795  int format = 1;
796 
797  CRM_ASSERT(diff != NULL);
798 
799  if (*diff == NULL && last != NULL && next != NULL) {
800  *diff = diff_xml_object(last, next, FALSE);
801  }
802 
803  if (*diff == NULL) {
804  goto done;
805  }
806 
807  crm_element_value_int(*diff, PCMK_XA_FORMAT, &format);
808  CRM_LOG_ASSERT(format == 1);
809 
810  xpathObj = xpath_search(*diff, "//" XML_CIB_TAG_CONFIGURATION);
811  if (numXpathResults(xpathObj) > 0) {
812  config_changes = true;
813  goto done;
814  }
815  freeXpathObject(xpathObj);
816 
817  /*
818  * Do not check XML_TAG_DIFF_ADDED "//" XML_TAG_CIB
819  * This always contains every field and would produce a false positive
820  * every time if the checked value existed
821  */
822  xpathObj = xpath_search(*diff, "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_CIB);
823  max = numXpathResults(xpathObj);
824 
825  for (lpc = 0; lpc < max; lpc++) {
826  xmlNode *top = getXpathResult(xpathObj, lpc);
827 
828  if (crm_element_value(top, XML_ATTR_GENERATION) != NULL) {
829  config_changes = true;
830  goto done;
831  }
832  if (crm_element_value(top, XML_ATTR_GENERATION_ADMIN) != NULL) {
833  config_changes = true;
834  goto done;
835  }
836 
837  if (crm_element_value(top, XML_ATTR_VALIDATION) != NULL) {
838  config_changes = true;
839  goto done;
840  }
841  if (crm_element_value(top, XML_ATTR_CRM_VERSION) != NULL) {
842  config_changes = true;
843  goto done;
844  }
845  if (crm_element_value(top, "remote-clear-port") != NULL) {
846  config_changes = true;
847  goto done;
848  }
849  if (crm_element_value(top, "remote-tls-port") != NULL) {
850  config_changes = true;
851  goto done;
852  }
853  }
854 
855  done:
856  freeXpathObject(xpathObj);
857  return config_changes;
858 }
859 
860 int
861 cib_process_xpath(const char *op, int options, const char *section,
862  const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
863  xmlNode **result_cib, xmlNode **answer)
864 {
865  int lpc = 0;
866  int max = 0;
867  int rc = pcmk_ok;
868  bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none);
869 
870  xmlXPathObjectPtr xpathObj = NULL;
871 
872  crm_trace("Processing \"%s\" event", op);
873 
874  if (is_query) {
875  xpathObj = xpath_search(existing_cib, section);
876  } else {
877  xpathObj = xpath_search(*result_cib, section);
878  }
879 
880  max = numXpathResults(xpathObj);
881 
882  if ((max < 1)
883  && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
884  crm_debug("%s was already removed", section);
885 
886  } else if (max < 1) {
887  crm_debug("%s: %s does not exist", op, section);
888  rc = -ENXIO;
889 
890  } else if (is_query) {
891  if (max > 1) {
892  *answer = create_xml_node(NULL, "xpath-query");
893  }
894  }
895 
896  if (pcmk_is_set(options, cib_multiple)
897  && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
898  dedupXpathResults(xpathObj);
899  }
900 
901  for (lpc = 0; lpc < max; lpc++) {
902  xmlChar *path = NULL;
903  xmlNode *match = getXpathResult(xpathObj, lpc);
904 
905  if (match == NULL) {
906  continue;
907  }
908 
909  path = xmlGetNodePath(match);
910  crm_debug("Processing %s op for %s with %s", op, section, path);
911  free(path);
912 
913  if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
914  if (match == *result_cib) {
915  /* Attempting to delete the whole "/cib" */
916  crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
917  rc = -EINVAL;
918  break;
919  }
920 
921  free_xml(match);
922  if ((options & cib_multiple) == 0) {
923  break;
924  }
925 
926  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) {
927  if (update_xml_child(match, input) == FALSE) {
928  rc = -ENXIO;
929  } else if ((options & cib_multiple) == 0) {
930  break;
931  }
932 
933  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) {
934  add_node_copy(match, input);
935  break;
936 
937  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
938 
939  if (options & cib_no_children) {
940  xmlNode *shallow = create_xml_node(*answer,
941  (const char *) match->name);
942 
943  copy_in_properties(shallow, match);
944 
945  if (*answer == NULL) {
946  *answer = shallow;
947  }
948 
949  } else if (options & cib_xpath_address) {
950  char *path = NULL;
951  xmlNode *parent = match;
952 
953  while (parent && parent->type == XML_ELEMENT_NODE) {
954  const char *id = crm_element_value(parent, XML_ATTR_ID);
955  char *new_path = NULL;
956 
957  if (id) {
958  new_path = crm_strdup_printf("/%s[@" XML_ATTR_ID "='%s']"
959  "%s",
960  parent->name, id,
961  pcmk__s(path, ""));
962  } else {
963  new_path = crm_strdup_printf("/%s%s", parent->name,
964  pcmk__s(path, ""));
965  }
966  free(path);
967  path = new_path;
968  parent = parent->parent;
969  }
970  crm_trace("Got: %s", path);
971 
972  if (*answer == NULL) {
973  *answer = create_xml_node(NULL, "xpath-query");
974  }
975  parent = create_xml_node(*answer, "xpath-query-path");
977  free(path);
978 
979  } else if (*answer) {
980  add_node_copy(*answer, match);
981 
982  } else {
983  *answer = match;
984  }
985 
986  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE,
987  pcmk__str_none)) {
988  xmlNode *parent = match->parent;
989 
990  free_xml(match);
991  if (input != NULL) {
993  }
994 
995  if ((options & cib_multiple) == 0) {
996  break;
997  }
998  }
999  }
1000 
1001  freeXpathObject(xpathObj);
1002  return rc;
1003 }
#define pcmk_err_old_data
Definition: results.h:75
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
A dumping ground.
int cib__get_operation(const char *op, const cib__operation_t **operation)
Definition: cib_ops.c:142
bool cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
Definition: cib_ops.c:790
#define crm_notice(fmt, args...)
Definition: logging.h:383
const char * pcmk_strerror(int rc)
Definition: results.c:149
gboolean cib_version_details(xmlNode *cib, int *admin_epoch, int *epoch, int *updates)
Definition: cib_utils.c:44
#define XML_CIB_TAG_SECTION_ALL
Definition: msg_xml.h:202
int cib_process_create(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:701
int cib_process_modify(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:456
#define XML_ATTR_NUMUPDATES
Definition: msg_xml.h:149
xmlNode * pcmk__xe_match(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:429
#define PCMK__CIB_REQUEST_SYNC_TO_ONE
Definition: internal.h:20
#define PCMK__CIB_REQUEST_PRIMARY
Definition: internal.h:18
#define PCMK__CIB_REQUEST_CREATE
Definition: internal.h:24
int cib_process_delete(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:421
#define PCMK__CIB_REQUEST_QUERY
Definition: internal.h:23
#define CRM_FEATURE_SET
Definition: crm.h:70
#define XML_FAIL_TAG_CIB
Definition: msg_xml.h:194
void copy_in_properties(xmlNode *target, const xmlNode *src)
Definition: xml.c:456
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:384
#define PCMK__CIB_REQUEST_COMMIT_TRANSACT
Definition: internal.h:34
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
int get_schema_version(const char *name)
Definition: schemas.c:967
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:222
Supported in a transaction.
Definition: internal.h:85
void dedupXpathResults(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:101
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
#define XML_ATTR_GENERATION
Definition: msg_xml.h:147
#define PCMK__CIB_REQUEST_ERASE
Definition: internal.h:27
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
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
#define PCMK__CIB_REQUEST_ABS_DELETE
Definition: internal.h:31
#define crm_warn(fmt, args...)
Definition: logging.h:382
Writes to disk on success.
Definition: internal.h:84
Modifies CIB.
Definition: internal.h:80
#define crm_debug(fmt, args...)
Definition: logging.h:386
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
Definition: patchset.c:1099
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:644
#define XML_TAG_FAILED
Definition: msg_xml.h:138
#define XML_ATTR_ID
Definition: msg_xml.h:156
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
#define PCMK__CIB_REQUEST_IS_PRIMARY
Definition: internal.h:21
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
int cib_process_upgrade(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:259
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2307
#define crm_trace(fmt, args...)
Definition: logging.h:387
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int cib_process_xpath(const char *op, int options, const char *section, const xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:861
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition: xml.c:622
Wrappers for and extensions to libxml2.
int cib_process_erase(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:240
#define PCMK_XA_FORMAT
Definition: msg_xml.h:51
int cib_process_query(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:166
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
#define PCMK__NELEM(a)
Definition: internal.h:46
#define XML_ATTR_VALIDATION
Definition: msg_xml.h:142
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
Must only be processed locally.
Definition: internal.h:82
#define F_ORIG
Definition: msg_xml.h:79
#define XML_FAILCIB_ATTR_OP
Definition: msg_xml.h:198
void free_xml(xmlNode *child)
Definition: xml.c:783
#define XML_FAILCIB_ATTR_OBJTYPE
Definition: msg_xml.h:197
#define PCMK__CIB_REQUEST_UPGRADE
Definition: internal.h:30
#define PCMK__CIB_REQUEST_BUMP
Definition: internal.h:22
const char * target
Definition: pcmk_fence.c:29
Replaces CIB.
Definition: internal.h:83
xmlNode * createEmptyCib(int cib_epoch)
Create XML for a new (empty) CIB.
Definition: cib_utils.c:238
int cib_process_replace(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:309
#define XML_TAG_CIB
Definition: msg_xml.h:137
Requires privileges.
Definition: internal.h:81
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path)
Definition: xpath.c:139
#define crm_log_xml_err(xml, text)
Definition: logging.h:390
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:608
pcmk__action_result_t result
Definition: pcmk_fence.c:35
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:381
#define CRM_ASSERT(expr)
Definition: results.h:42
Prefer stderr to logs.
Definition: cib_types.h:55
#define PCMK__CIB_REQUEST_REPLACE
Definition: internal.h:28
#define XML_FAILCIB_ATTR_REASON
Definition: msg_xml.h:199
xmlNode * input
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1696
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition: xpath.c:58
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
Definition: digest.c:167
#define PCMK__CIB_REQUEST_SYNC_TO_ALL
Definition: internal.h:19
#define XML_ATTR_GENERATION_ADMIN
Definition: msg_xml.h:148
#define XML_CIB_ATTR_REPLACE
Definition: msg_xml.h:284
#define PCMK__CIB_REQUEST_SECONDARY
Definition: internal.h:17
#define PCMK__CIB_REQUEST_NOOP
Definition: internal.h:32
const char * name
Definition: internal.h:124
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:140
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition: acl.c:605
#define pcmk_ok
Definition: results.h:68
int update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform, gboolean to_logs)
Update CIB XML to most recent schema version.
Definition: schemas.c:984
xmlNode * diff_xml_object(xmlNode *left, xmlNode *right, gboolean suppress)
Definition: patchset.c:1186
#define crm_log_xml_trace(xml, text)
Definition: logging.h:395
#define PCMK__CIB_REQUEST_MODIFY
Definition: internal.h:25
#define CRM_OP_PING
Definition: crm.h:133
#define XML_TAG_DIFF_REMOVED
Definition: msg_xml.h:426
#define XML_CIB_TAG_CONFIGURATION
Definition: msg_xml.h:203
#define ID(x)
Definition: msg_xml.h:474
#define XML_FAILCIB_ATTR_ID
Definition: msg_xml.h:196
const char * parent
Definition: cib.c:27
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:39
No special attributes.
Definition: internal.h:79
#define PCMK__CIB_REQUEST_SHUTDOWN
Definition: internal.h:33
#define crm_info(fmt, args...)
Definition: logging.h:384
#define PCMK__CIB_REQUEST_APPLY_PATCH
Definition: internal.h:29
uint32_t version
Definition: remote.c:213
int cib_process_bump(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer)
Definition: cib_ops.c:294
#define F_CIB_SCHEMA_MAX
Definition: internal.h:62
#define PCMK__CIB_REQUEST_DELETE
Definition: internal.h:26
#define XML_ATTR_DIGEST
Definition: msg_xml.h:141
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2374