pacemaker  2.1.6-802a72226b
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 <crm/crm.h>
23 #include <crm/cib/internal.h>
24 #include <crm/msg_xml.h>
25 
26 #include <crm/common/xml.h>
28 
29 int
30 cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
31  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
32 {
33  xmlNode *obj_root = NULL;
34  int result = pcmk_ok;
35 
36  crm_trace("Processing %s for %s section",
37  op, pcmk__s(section, "unspecified"));
38 
39  if (options & cib_xpath) {
40  return cib_process_xpath(op, options, section, req, input,
41  existing_cib, result_cib, answer);
42  }
43 
44  CRM_CHECK(*answer == NULL, free_xml(*answer));
45  *answer = NULL;
46 
47  if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
48  section = NULL;
49  }
50 
51  obj_root = pcmk_find_cib_element(existing_cib, section);
52 
53  if (obj_root == NULL) {
54  result = -ENXIO;
55 
56  } else if (options & cib_no_children) {
57  const char *tag = TYPE(obj_root);
58  xmlNode *shallow = create_xml_node(*answer, tag);
59 
60  copy_in_properties(shallow, obj_root);
61  *answer = shallow;
62 
63  } else {
64  *answer = obj_root;
65  }
66 
67  if (result == pcmk_ok && *answer == NULL) {
68  crm_err("Error creating query response");
69  result = -ENOMSG;
70  }
71 
72  return result;
73 }
74 
75 static int
76 update_counter(xmlNode *xml_obj, const char *field, bool reset)
77 {
78  char *new_value = NULL;
79  char *old_value = NULL;
80  int int_value = -1;
81 
82  if (!reset && crm_element_value(xml_obj, field) != NULL) {
83  old_value = crm_element_value_copy(xml_obj, field);
84  }
85  if (old_value != NULL) {
86  int_value = atoi(old_value);
87  new_value = pcmk__itoa(++int_value);
88  } else {
89  new_value = strdup("1");
90  CRM_ASSERT(new_value != NULL);
91  }
92 
93  crm_trace("Update %s from %s to %s",
94  field, pcmk__s(old_value, "unset"), new_value);
95  crm_xml_add(xml_obj, field, new_value);
96 
97  free(new_value);
98  free(old_value);
99 
100  return pcmk_ok;
101 }
102 
103 int
104 cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
105  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
106 {
107  int result = pcmk_ok;
108 
109  crm_trace("Processing \"%s\" event", op);
110  *answer = NULL;
111  free_xml(*result_cib);
112  *result_cib = createEmptyCib(0);
113 
114  copy_in_properties(*result_cib, existing_cib);
115  update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false);
116 
117  return result;
118 }
119 
120 int
121 cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req,
122  xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
123  xmlNode ** answer)
124 {
125  int rc = 0;
126  int new_version = 0;
127  int current_version = 0;
128  int max_version = 0;
129  const char *max = crm_element_value(req, F_CIB_SCHEMA_MAX);
130  const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION);
131 
132  *answer = NULL;
133  crm_trace("Processing \"%s\" event with max=%s", op, max);
134 
135  if (value != NULL) {
136  current_version = get_schema_version(value);
137  }
138 
139  if (max) {
140  max_version = get_schema_version(max);
141  }
142 
143  rc = update_validation(result_cib, &new_version, max_version, TRUE,
144  !(options & cib_verbose));
145  if (new_version > current_version) {
146  update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, false);
147  update_counter(*result_cib, XML_ATTR_GENERATION, true);
148  update_counter(*result_cib, XML_ATTR_NUMUPDATES, true);
149  return pcmk_ok;
150  }
151 
152  return rc;
153 }
154 
155 int
156 cib_process_bump(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
157  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
158 {
159  int result = pcmk_ok;
160 
161  crm_trace("Processing %s for epoch='%s'", op,
162  pcmk__s(crm_element_value(existing_cib, XML_ATTR_GENERATION), ""));
163 
164  *answer = NULL;
165  update_counter(*result_cib, XML_ATTR_GENERATION, false);
166 
167  return result;
168 }
169 
170 int
171 cib_process_replace(const char *op, int options, const char *section, xmlNode * req,
172  xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
173  xmlNode ** answer)
174 {
175  const char *tag = NULL;
176  int result = pcmk_ok;
177 
178  crm_trace("Processing %s for %s section",
179  op, pcmk__s(section, "unspecified"));
180 
181  if (options & cib_xpath) {
182  return cib_process_xpath(op, options, section, req, input,
183  existing_cib, result_cib, answer);
184  }
185 
186  *answer = NULL;
187 
188  if (input == NULL) {
189  return -EINVAL;
190  }
191 
192  tag = crm_element_name(input);
193 
194  if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
195  section = NULL;
196 
197  } else if (pcmk__str_eq(tag, section, pcmk__str_casei)) {
198  section = NULL;
199  }
200 
201  if (pcmk__str_eq(tag, XML_TAG_CIB, pcmk__str_casei)) {
202  int updates = 0;
203  int epoch = 0;
204  int admin_epoch = 0;
205 
206  int replace_updates = 0;
207  int replace_epoch = 0;
208  int replace_admin_epoch = 0;
209 
210  const char *reason = NULL;
211  const char *peer = crm_element_value(req, F_ORIG);
212  const char *digest = crm_element_value(req, XML_ATTR_DIGEST);
213 
214  if (digest) {
215  const char *version = crm_element_value(req, XML_ATTR_CRM_VERSION);
216  char *digest_verify = calculate_xml_versioned_digest(input, FALSE, TRUE,
217  version ? version :
219 
220  if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) {
221  crm_err("Digest mis-match on replace from %s: %s vs. %s (expected)", peer,
222  digest_verify, digest);
223  reason = "digest mismatch";
224 
225  } else {
226  crm_info("Digest matched on replace from %s: %s", peer, digest);
227  }
228  free(digest_verify);
229 
230  } else {
231  crm_trace("No digest to verify");
232  }
233 
234  cib_version_details(existing_cib, &admin_epoch, &epoch, &updates);
235  cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates);
236 
237  if (replace_admin_epoch < admin_epoch) {
238  reason = XML_ATTR_GENERATION_ADMIN;
239 
240  } else if (replace_admin_epoch > admin_epoch) {
241  /* no more checks */
242 
243  } else if (replace_epoch < epoch) {
244  reason = XML_ATTR_GENERATION;
245 
246  } else if (replace_epoch > epoch) {
247  /* no more checks */
248 
249  } else if (replace_updates < updates) {
250  reason = XML_ATTR_NUMUPDATES;
251  }
252 
253  if (reason != NULL) {
254  crm_info("Replacement %d.%d.%d from %s not applied to %d.%d.%d:"
255  " current %s is greater than the replacement",
256  replace_admin_epoch, replace_epoch,
257  replace_updates, peer, admin_epoch, epoch, updates, reason);
259  } else {
260  crm_info("Replaced %d.%d.%d with %d.%d.%d from %s",
261  admin_epoch, epoch, updates,
262  replace_admin_epoch, replace_epoch, replace_updates, peer);
263  }
264 
265  free_xml(*result_cib);
266  *result_cib = copy_xml(input);
267 
268  } else {
269  xmlNode *obj_root = NULL;
270  gboolean ok = TRUE;
271 
272  obj_root = pcmk_find_cib_element(*result_cib, section);
273  ok = replace_xml_child(NULL, obj_root, input, FALSE);
274  if (ok == FALSE) {
275  crm_trace("No matching object to replace");
276  result = -ENXIO;
277  }
278  }
279 
280  return result;
281 }
282 
283 int
284 cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
285  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
286 {
287  xmlNode *obj_root = NULL;
288 
289  crm_trace("Processing \"%s\" event", op);
290 
291  if (options & cib_xpath) {
292  return cib_process_xpath(op, options, section, req, input,
293  existing_cib, result_cib, answer);
294  }
295 
296  if (input == NULL) {
297  crm_err("Cannot perform modification with no data");
298  return -EINVAL;
299  }
300 
301  obj_root = pcmk_find_cib_element(*result_cib, section);
302  if(pcmk__str_eq(crm_element_name(input), section, pcmk__str_casei)) {
303  xmlNode *child = NULL;
304  for (child = pcmk__xml_first_child(input); child;
305  child = pcmk__xml_next(child)) {
306  if (replace_xml_child(NULL, obj_root, child, TRUE) == FALSE) {
307  crm_trace("No matching object to delete: %s=%s", child->name, ID(child));
308  }
309  }
310 
311  } else if (replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) {
312  crm_trace("No matching object to delete: %s=%s", input->name, ID(input));
313  }
314 
315  return pcmk_ok;
316 }
317 
318 int
319 cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
320  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
321 {
322  xmlNode *obj_root = NULL;
323 
324  crm_trace("Processing \"%s\" event", op);
325 
326  if (options & cib_xpath) {
327  return cib_process_xpath(op, options, section, req, input,
328  existing_cib, result_cib, answer);
329  }
330 
331  if (input == NULL) {
332  crm_err("Cannot perform modification with no data");
333  return -EINVAL;
334  }
335 
336  obj_root = pcmk_find_cib_element(*result_cib, section);
337  if (obj_root == NULL) {
338  xmlNode *tmp_section = NULL;
339  const char *path = pcmk_cib_parent_name_for(section);
340 
341  if (path == NULL) {
342  return -EINVAL;
343  }
344 
345  tmp_section = create_xml_node(NULL, section);
346  cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section,
347  NULL, result_cib, answer);
348  free_xml(tmp_section);
349 
350  obj_root = pcmk_find_cib_element(*result_cib, section);
351  }
352 
353  CRM_CHECK(obj_root != NULL, return -EINVAL);
354 
355  if (update_xml_child(obj_root, input) == FALSE) {
356  if (options & cib_can_create) {
357  add_node_copy(obj_root, input);
358  } else {
359  return -ENXIO;
360  }
361  }
362 
363  if(options & cib_mixed_update) {
364  int max = 0, lpc;
365  xmlXPathObjectPtr xpathObj = xpath_search(*result_cib, "//@__delete__");
366 
367  if (xpathObj) {
368  max = numXpathResults(xpathObj);
369  crm_log_xml_trace(*result_cib, "Mixed result");
370  }
371 
372  for (lpc = 0; lpc < max; lpc++) {
373  xmlNode *match = getXpathResult(xpathObj, lpc);
374  xmlChar *match_path = xmlGetNodePath(match);
375 
376  crm_debug("Destroying %s", match_path);
377  free(match_path);
378  free_xml(match);
379  }
380 
381  freeXpathObject(xpathObj);
382  }
383  return pcmk_ok;
384 }
385 
386 static int
387 update_cib_object(xmlNode * parent, xmlNode * update)
388 {
389  int result = pcmk_ok;
390  xmlNode *target = NULL;
391  xmlNode *a_child = NULL;
392  const char *replace = NULL;
393  const char *object_id = NULL;
394  const char *object_name = NULL;
395 
396  CRM_CHECK(update != NULL, return -EINVAL);
397  CRM_CHECK(parent != NULL, return -EINVAL);
398 
399  object_name = crm_element_name(update);
400  CRM_CHECK(object_name != NULL, return -EINVAL);
401 
402  object_id = ID(update);
403  crm_trace("Processing update for <%s%s%s%s>", object_name,
404  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
405  pcmk__s(object_id, ""),
406  ((object_id == NULL)? "" : "'"));
407 
408  if (object_id == NULL) {
409  /* placeholder object */
410  target = find_xml_node(parent, object_name, FALSE);
411 
412  } else {
413  target = pcmk__xe_match(parent, object_name, XML_ATTR_ID, object_id);
414  }
415 
416  if (target == NULL) {
417  target = create_xml_node(parent, object_name);
418  }
419 
420  crm_trace("Found node <%s%s%s%s> to update", object_name,
421  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
422  pcmk__s(object_id, ""),
423  ((object_id == NULL)? "" : "'"));
424 
425  // @COMPAT: XML_CIB_ATTR_REPLACE is unused internally. Remove at break.
426  replace = crm_element_value(update, XML_CIB_ATTR_REPLACE);
427  if (replace != NULL) {
428  xmlNode *remove = NULL;
429  int last = 0, lpc = 0, len = 0;
430 
431  len = strlen(replace);
432  while (lpc <= len) {
433  if (replace[lpc] == ',' || replace[lpc] == 0) {
434  char *replace_item = NULL;
435 
436  if (last == lpc) {
437  /* nothing to do */
438  last = lpc + 1;
439  goto incr;
440  }
441 
442  replace_item = strndup(replace + last, lpc - last);
443  remove = find_xml_node(target, replace_item, FALSE);
444  if (remove != NULL) {
445  crm_trace("Replacing node <%s> in <%s>",
446  replace_item, crm_element_name(target));
447  free_xml(remove);
448  remove = NULL;
449  }
450  free(replace_item);
451  last = lpc + 1;
452  }
453  incr:
454  lpc++;
455  }
458  }
459 
460  copy_in_properties(target, update);
461 
462  if (xml_acl_denied(target)) {
463  crm_notice("Cannot update <%s " XML_ATTR_ID "=%s>",
464  pcmk__s(object_name, "<null>"),
465  pcmk__s(object_id, "<null>"));
466  return -EACCES;
467  }
468 
469  crm_trace("Processing children of <%s%s%s%s>", object_name,
470  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
471  pcmk__s(object_id, ""),
472  ((object_id == NULL)? "" : "'"));
473 
474  for (a_child = pcmk__xml_first_child(update); a_child != NULL;
475  a_child = pcmk__xml_next(a_child)) {
476  int tmp_result = 0;
477 
478  crm_trace("Updating child <%s%s%s%s>", crm_element_name(a_child),
479  ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
480  pcmk__s(ID(a_child), ""), ((ID(a_child) == NULL)? "" : "'"));
481 
482  tmp_result = update_cib_object(target, a_child);
483 
484  /* only the first error is likely to be interesting */
485  if (tmp_result != pcmk_ok) {
486  crm_err("Error updating child <%s%s%s%s>",
487  crm_element_name(a_child),
488  ((ID(a_child) == NULL)? "" : " " XML_ATTR_ID "='"),
489  pcmk__s(ID(a_child), ""),
490  ((ID(a_child) == NULL)? "" : "'"));
491 
492  if (result == pcmk_ok) {
493  result = tmp_result;
494  }
495  }
496  }
497 
498  crm_trace("Finished handling update for <%s%s%s%s>", object_name,
499  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
500  pcmk__s(object_id, ""),
501  ((object_id == NULL)? "" : "'"));
502 
503  return result;
504 }
505 
506 static int
507 add_cib_object(xmlNode * parent, xmlNode * new_obj)
508 {
509  const char *object_name = NULL;
510  const char *object_id = NULL;
511  xmlNode *equiv_node = NULL;
512 
513  if ((parent == NULL) || (new_obj == NULL)) {
514  return -EINVAL;
515  }
516 
517  object_name = crm_element_name(new_obj);
518  if (object_name == NULL) {
519  return -EINVAL;
520  }
521 
522  object_id = ID(new_obj);
523 
524  crm_trace("Processing creation of <%s%s%s%s>", object_name,
525  ((object_id == NULL)? "" : " " XML_ATTR_ID "='"),
526  pcmk__s(object_id, ""),
527  ((object_id == NULL)? "" : "'"));
528 
529  if (object_id == NULL) {
530  equiv_node = find_xml_node(parent, object_name, FALSE);
531  } else {
532  equiv_node = pcmk__xe_match(parent, object_name, XML_ATTR_ID,
533  object_id);
534  }
535  if (equiv_node != NULL) {
536  return -EEXIST;
537  }
538 
539  return update_cib_object(parent, new_obj);
540 }
541 
542 static bool
543 update_results(xmlNode *failed, xmlNode *target, const char *operation,
544  int return_code)
545 {
546  xmlNode *xml_node = NULL;
547  bool was_error = false;
548  const char *error_msg = NULL;
549 
550  if (return_code != pcmk_ok) {
551  error_msg = pcmk_strerror(return_code);
552 
553  was_error = true;
554  xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB);
555  add_node_copy(xml_node, target);
556 
559  crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation);
560  crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg);
561 
562  crm_warn("Action %s failed: %s (cde=%d)",
563  operation, error_msg, return_code);
564  }
565 
566  return was_error;
567 }
568 
569 int
570 cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
571  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
572 {
573  xmlNode *failed = NULL;
574  int result = pcmk_ok;
575  xmlNode *update_section = NULL;
576 
577  crm_trace("Processing %s for %s section",
578  op, pcmk__s(section, "unspecified"));
579  if (pcmk__str_eq(XML_CIB_TAG_SECTION_ALL, section, pcmk__str_casei)) {
580  section = NULL;
581 
582  } else if (pcmk__str_eq(XML_TAG_CIB, section, pcmk__str_casei)) {
583  section = NULL;
584 
585  } else if (pcmk__str_eq(crm_element_name(input), XML_TAG_CIB, pcmk__str_casei)) {
586  section = NULL;
587  }
588 
589  CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL);
590 
591  if (input == NULL) {
592  crm_err("Cannot perform modification with no data");
593  return -EINVAL;
594  }
595 
596  if (section == NULL) {
597  return cib_process_modify(op, options, section, req, input, existing_cib, result_cib,
598  answer);
599  }
600 
601  failed = create_xml_node(NULL, XML_TAG_FAILED);
602 
603  update_section = pcmk_find_cib_element(*result_cib, section);
604  if (pcmk__str_eq(crm_element_name(input), section, pcmk__str_casei)) {
605  xmlNode *a_child = NULL;
606 
607  for (a_child = pcmk__xml_first_child(input); a_child != NULL;
608  a_child = pcmk__xml_next(a_child)) {
609  result = add_cib_object(update_section, a_child);
610  if (update_results(failed, a_child, op, result)) {
611  break;
612  }
613  }
614 
615  } else {
616  result = add_cib_object(update_section, input);
617  update_results(failed, input, op, result);
618  }
619 
620  if ((result == pcmk_ok) && xml_has_children(failed)) {
621  result = -EINVAL;
622  }
623 
624  if (result != pcmk_ok) {
625  crm_log_xml_err(failed, "CIB Update failures");
626  *answer = failed;
627 
628  } else {
629  free_xml(failed);
630  }
631 
632  return result;
633 }
634 
635 int
636 cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
637  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
638 {
639  const char *originator = NULL;
640 
641  if (req != NULL) {
642  originator = crm_element_value(req, F_ORIG);
643  }
644 
645  crm_trace("Processing \"%s\" event from %s%s",
646  op, originator,
647  (pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
648 
649  free_xml(*result_cib);
650  *result_cib = copy_xml(existing_cib);
651  return xml_apply_patchset(*result_cib, input, TRUE);
652 }
653 
654 // @COMPAT: v1-only
655 bool
656 cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
657 {
658  int lpc = 0, max = 0;
659  bool config_changes = false;
660  xmlXPathObject *xpathObj = NULL;
661  int format = 1;
662 
663  CRM_ASSERT(diff != NULL);
664 
665  if (*diff == NULL && last != NULL && next != NULL) {
666  *diff = diff_xml_object(last, next, FALSE);
667  }
668 
669  if (*diff == NULL) {
670  goto done;
671  }
672 
673  crm_element_value_int(*diff, "format", &format);
674  CRM_LOG_ASSERT(format == 1);
675 
676  xpathObj = xpath_search(*diff, "//" XML_CIB_TAG_CONFIGURATION);
677  if (numXpathResults(xpathObj) > 0) {
678  config_changes = true;
679  goto done;
680  }
681  freeXpathObject(xpathObj);
682 
683  /*
684  * Do not check XML_TAG_DIFF_ADDED "//" XML_TAG_CIB
685  * This always contains every field and would produce a false positive
686  * every time if the checked value existed
687  */
688  xpathObj = xpath_search(*diff, "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_CIB);
689  max = numXpathResults(xpathObj);
690 
691  for (lpc = 0; lpc < max; lpc++) {
692  xmlNode *top = getXpathResult(xpathObj, lpc);
693 
694  if (crm_element_value(top, XML_ATTR_GENERATION) != NULL) {
695  config_changes = true;
696  goto done;
697  }
698  if (crm_element_value(top, XML_ATTR_GENERATION_ADMIN) != NULL) {
699  config_changes = true;
700  goto done;
701  }
702 
703  if (crm_element_value(top, XML_ATTR_VALIDATION) != NULL) {
704  config_changes = true;
705  goto done;
706  }
707  if (crm_element_value(top, XML_ATTR_CRM_VERSION) != NULL) {
708  config_changes = true;
709  goto done;
710  }
711  if (crm_element_value(top, "remote-clear-port") != NULL) {
712  config_changes = true;
713  goto done;
714  }
715  if (crm_element_value(top, "remote-tls-port") != NULL) {
716  config_changes = true;
717  goto done;
718  }
719  }
720 
721  done:
722  freeXpathObject(xpathObj);
723  return config_changes;
724 }
725 
726 int
727 cib_process_xpath(const char *op, int options, const char *section,
728  const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
729  xmlNode **result_cib, xmlNode **answer)
730 {
731  int lpc = 0;
732  int max = 0;
733  int rc = pcmk_ok;
734  bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none);
735 
736  xmlXPathObjectPtr xpathObj = NULL;
737 
738  crm_trace("Processing \"%s\" event", op);
739 
740  if (is_query) {
741  xpathObj = xpath_search(existing_cib, section);
742  } else {
743  xpathObj = xpath_search(*result_cib, section);
744  }
745 
746  max = numXpathResults(xpathObj);
747 
748  if ((max < 1)
749  && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
750  crm_debug("%s was already removed", section);
751 
752  } else if (max < 1) {
753  crm_debug("%s: %s does not exist", op, section);
754  rc = -ENXIO;
755 
756  } else if (is_query) {
757  if (max > 1) {
758  *answer = create_xml_node(NULL, "xpath-query");
759  }
760  }
761 
762  if (pcmk_is_set(options, cib_multiple)
763  && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
764  dedupXpathResults(xpathObj);
765  }
766 
767  for (lpc = 0; lpc < max; lpc++) {
768  xmlChar *path = NULL;
769  xmlNode *match = getXpathResult(xpathObj, lpc);
770 
771  if (match == NULL) {
772  continue;
773  }
774 
775  path = xmlGetNodePath(match);
776  crm_debug("Processing %s op for %s with %s", op, section, path);
777  free(path);
778 
779  if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
780  if (match == *result_cib) {
781  /* Attempting to delete the whole "/cib" */
782  crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
783  rc = -EINVAL;
784  break;
785  }
786 
787  free_xml(match);
788  if ((options & cib_multiple) == 0) {
789  break;
790  }
791 
792  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) {
793  if (update_xml_child(match, input) == FALSE) {
794  rc = -ENXIO;
795  } else if ((options & cib_multiple) == 0) {
796  break;
797  }
798 
799  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) {
800  add_node_copy(match, input);
801  break;
802 
803  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
804 
805  if (options & cib_no_children) {
806  const char *tag = TYPE(match);
807  xmlNode *shallow = create_xml_node(*answer, tag);
808 
809  copy_in_properties(shallow, match);
810 
811  if (*answer == NULL) {
812  *answer = shallow;
813  }
814 
815  } else if (options & cib_xpath_address) {
816  char *path = NULL;
817  xmlNode *parent = match;
818 
819  while (parent && parent->type == XML_ELEMENT_NODE) {
820  const char *id = crm_element_value(parent, XML_ATTR_ID);
821  char *new_path = NULL;
822 
823  if (id) {
824  new_path = crm_strdup_printf("/%s[@" XML_ATTR_ID "='%s']"
825  "%s",
826  parent->name, id,
827  pcmk__s(path, ""));
828  } else {
829  new_path = crm_strdup_printf("/%s%s", parent->name,
830  pcmk__s(path, ""));
831  }
832  free(path);
833  path = new_path;
834  parent = parent->parent;
835  }
836  crm_trace("Got: %s", path);
837 
838  if (*answer == NULL) {
839  *answer = create_xml_node(NULL, "xpath-query");
840  }
841  parent = create_xml_node(*answer, "xpath-query-path");
843  free(path);
844 
845  } else if (*answer) {
846  add_node_copy(*answer, match);
847 
848  } else {
849  *answer = match;
850  }
851 
852  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE,
853  pcmk__str_none)) {
854  xmlNode *parent = match->parent;
855 
856  free_xml(match);
857  if (input != NULL) {
859  }
860 
861  if ((options & cib_multiple) == 0) {
862  break;
863  }
864  }
865  }
866 
867  freeXpathObject(xpathObj);
868  return rc;
869 }
#define pcmk_err_old_data
Definition: results.h:75
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:235
A dumping ground.
bool cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
Definition: cib_ops.c:656
#define crm_notice(fmt, args...)
Definition: logging.h:379
const char * pcmk_strerror(int rc)
Definition: results.c:148
gboolean cib_version_details(xmlNode *cib, int *admin_epoch, int *epoch, int *updates)
Definition: cib_utils.c:43
#define XML_CIB_TAG_SECTION_ALL
Definition: msg_xml.h:196
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:570
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:319
#define XML_ATTR_NUMUPDATES
Definition: msg_xml.h:140
xmlNode * pcmk__xe_match(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:454
#define PCMK__CIB_REQUEST_CREATE
Definition: internal.h:25
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:284
#define PCMK__CIB_REQUEST_QUERY
Definition: internal.h:24
#define CRM_FEATURE_SET
Definition: crm.h:69
#define XML_FAIL_TAG_CIB
Definition: msg_xml.h:188
void copy_in_properties(xmlNode *target, const xmlNode *src)
Definition: xml.c:481
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:404
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:1033
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:219
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:532
char * strndup(const char *str, size_t len)
#define XML_ATTR_GENERATION
Definition: msg_xml.h:138
#define TYPE(x)
Definition: msg_xml.h:481
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:636
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:819
#define crm_warn(fmt, args...)
Definition: logging.h:378
#define crm_debug(fmt, args...)
Definition: logging.h:382
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
Definition: patchset.c:1096
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:693
#define XML_TAG_FAILED
Definition: msg_xml.h:129
#define XML_ATTR_ID
Definition: msg_xml.h:147
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:496
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:153
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:121
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2340
#define crm_trace(fmt, args...)
Definition: logging.h:383
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:727
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition: xml.c:663
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:104
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:30
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:677
#define XML_ATTR_VALIDATION
Definition: msg_xml.h:133
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:131
#define F_ORIG
Definition: msg_xml.h:70
#define XML_FAILCIB_ATTR_OP
Definition: msg_xml.h:192
void free_xml(xmlNode *child)
Definition: xml.c:813
gboolean xml_has_children(const xmlNode *root)
Definition: xml.c:1726
#define XML_FAILCIB_ATTR_OBJTYPE
Definition: msg_xml.h:191
const char * target
Definition: pcmk_fence.c:29
xmlNode * createEmptyCib(int cib_epoch)
Create XML for a new (empty) CIB.
Definition: cib_utils.c:89
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:171
#define XML_TAG_CIB
Definition: msg_xml.h:128
#define crm_log_xml_err(xml, text)
Definition: logging.h:386
pcmk__action_result_t result
Definition: pcmk_fence.c:35
const char * path
Definition: cib.c:26
#define crm_err(fmt, args...)
Definition: logging.h:377
#define CRM_ASSERT(expr)
Definition: results.h:42
xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path)
Definition: xpath.c:139
Prefer stderr to logs.
Definition: cib_types.h:55
#define PCMK__CIB_REQUEST_REPLACE
Definition: internal.h:29
#define XML_FAILCIB_ATTR_REASON
Definition: msg_xml.h:193
xmlNode * input
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1735
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 XML_ATTR_GENERATION_ADMIN
Definition: msg_xml.h:139
#define XML_CIB_ATTR_REPLACE
Definition: msg_xml.h:286
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:131
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition: acl.c:608
#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:1050
xmlNode * diff_xml_object(xmlNode *left, xmlNode *right, gboolean suppress)
Definition: patchset.c:1195
#define crm_log_xml_trace(xml, text)
Definition: logging.h:391
#define PCMK__CIB_REQUEST_MODIFY
Definition: internal.h:26
#define XML_TAG_DIFF_REMOVED
Definition: msg_xml.h:432
#define XML_CIB_TAG_CONFIGURATION
Definition: msg_xml.h:197
#define ID(x)
Definition: msg_xml.h:480
#define XML_FAILCIB_ATTR_ID
Definition: msg_xml.h:190
const char * parent
Definition: cib.c:25
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:39
#define crm_info(fmt, args...)
Definition: logging.h:380
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:156
#define F_CIB_SCHEMA_MAX
Definition: internal.h:62
#define PCMK__CIB_REQUEST_DELETE
Definition: internal.h:27
#define XML_ATTR_DIGEST
Definition: msg_xml.h:132
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2407