pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
cib_ops.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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 
28 #include <crm/common/xml.h>
30 
31 // @TODO: Free this via crm_exit() when libcib gets merged with libcrmcommon
32 static GHashTable *operation_table = NULL;
33 
34 static const cib__operation_t cib_ops[] = {
35  {
38  },
39  {
44  },
45  {
50  },
51  {
57  },
58  {
63  },
64  {
69  },
70  {
76  },
77  {
80  },
81  {
86  },
87  {
89  },
90  {
92  },
93  {
94  // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support
97  },
98  {
100  },
101  {
108  },
109  {
112  },
113  {
115  },
116  {
118  },
119  {
121  },
122  {
128  },
129  {
131  }
132 };
133 
143 int
144 cib__get_operation(const char *op, const cib__operation_t **operation)
145 {
146  CRM_ASSERT((op != NULL) && (operation != NULL));
147 
148  if (operation_table == NULL) {
149  operation_table = pcmk__strkey_table(NULL, NULL);
150 
151  for (int lpc = 0; lpc < PCMK__NELEM(cib_ops); lpc++) {
152  const cib__operation_t *oper = &(cib_ops[lpc]);
153 
154  g_hash_table_insert(operation_table, (gpointer) oper->name,
155  (gpointer) oper);
156  }
157  }
158 
159  *operation = g_hash_table_lookup(operation_table, op);
160  if (*operation == NULL) {
161  crm_err("Operation %s is invalid", op);
162  return EINVAL;
163  }
164  return pcmk_rc_ok;
165 }
166 
167 int
168 cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
169  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
170 {
171  xmlNode *obj_root = NULL;
172  int result = pcmk_ok;
173 
174  crm_trace("Processing %s for %s section",
175  op, pcmk__s(section, "unspecified"));
176 
177  if (options & cib_xpath) {
178  return cib_process_xpath(op, options, section, req, input,
179  existing_cib, result_cib, answer);
180  }
181 
182  CRM_CHECK(*answer == NULL, free_xml(*answer));
183  *answer = NULL;
184 
185  if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
186  section = NULL;
187  }
188 
189  obj_root = pcmk_find_cib_element(existing_cib, section);
190 
191  if (obj_root == NULL) {
192  result = -ENXIO;
193 
194  } else if (options & cib_no_children) {
195  xmlNode *shallow = pcmk__xe_create(*answer,
196  (const char *) obj_root->name);
197 
198  pcmk__xe_copy_attrs(shallow, obj_root, pcmk__xaf_none);
199  *answer = shallow;
200 
201  } else {
202  *answer = obj_root;
203  }
204 
205  if (result == pcmk_ok && *answer == NULL) {
206  crm_err("Error creating query response");
207  result = -ENOMSG;
208  }
209 
210  return result;
211 }
212 
213 static int
214 update_counter(xmlNode *xml_obj, const char *field, bool reset)
215 {
216  char *new_value = NULL;
217  char *old_value = NULL;
218  int int_value = -1;
219 
220  if (!reset && crm_element_value(xml_obj, field) != NULL) {
221  old_value = crm_element_value_copy(xml_obj, field);
222  }
223  if (old_value != NULL) {
224  int_value = atoi(old_value);
225  new_value = pcmk__itoa(++int_value);
226  } else {
227  new_value = pcmk__str_copy("1");
228  }
229 
230  crm_trace("Update %s from %s to %s",
231  field, pcmk__s(old_value, "unset"), new_value);
232  crm_xml_add(xml_obj, field, new_value);
233 
234  free(new_value);
235  free(old_value);
236 
237  return pcmk_ok;
238 }
239 
240 int
241 cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
242  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
243 {
244  int result = pcmk_ok;
245 
246  crm_trace("Processing \"%s\" event", op);
247 
248  if (*result_cib != existing_cib) {
249  free_xml(*result_cib);
250  }
251  *result_cib = createEmptyCib(0);
252  pcmk__xe_copy_attrs(*result_cib, existing_cib, pcmk__xaf_none);
253  update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false);
254  *answer = NULL;
255 
256  return result;
257 }
258 
259 int
260 cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req,
261  xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
262  xmlNode ** answer)
263 {
264  int rc = 0;
265  const char *max_schema = crm_element_value(req, PCMK__XA_CIB_SCHEMA_MAX);
266  const char *original_schema = NULL;
267  const char *new_schema = NULL;
268 
269  *answer = NULL;
270  crm_trace("Processing \"%s\" event with max=%s", op, max_schema);
271 
272  original_schema = crm_element_value(existing_cib, PCMK_XA_VALIDATE_WITH);
273  rc = pcmk__update_schema(result_cib, max_schema, true,
274  !pcmk_is_set(options, cib_verbose));
275  rc = pcmk_rc2legacy(rc);
276  new_schema = crm_element_value(*result_cib, PCMK_XA_VALIDATE_WITH);
277 
278  if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) {
279  update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false);
280  update_counter(*result_cib, PCMK_XA_EPOCH, true);
281  update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true);
282  return pcmk_ok;
283  }
284 
285  return rc;
286 }
287 
288 int
289 cib_process_bump(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
290  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
291 {
292  int result = pcmk_ok;
293 
294  crm_trace("Processing %s for epoch='%s'", op,
295  pcmk__s(crm_element_value(existing_cib, PCMK_XA_EPOCH), ""));
296 
297  *answer = NULL;
298  update_counter(*result_cib, PCMK_XA_EPOCH, false);
299 
300  return result;
301 }
302 
303 int
304 cib_process_replace(const char *op, int options, const char *section, xmlNode * req,
305  xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
306  xmlNode ** answer)
307 {
308  int result = pcmk_ok;
309 
310  crm_trace("Processing %s for %s section",
311  op, pcmk__s(section, "unspecified"));
312 
313  if (options & cib_xpath) {
314  return cib_process_xpath(op, options, section, req, input,
315  existing_cib, result_cib, answer);
316  }
317 
318  *answer = NULL;
319 
320  if (input == NULL) {
321  return -EINVAL;
322  }
323 
324  if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
325  section = NULL;
326 
327  } else if (pcmk__xe_is(input, section)) {
328  section = NULL;
329  }
330 
331  if (pcmk__xe_is(input, PCMK_XE_CIB)) {
332  int updates = 0;
333  int epoch = 0;
334  int admin_epoch = 0;
335 
336  int replace_updates = 0;
337  int replace_epoch = 0;
338  int replace_admin_epoch = 0;
339 
340  const char *reason = NULL;
341  const char *peer = crm_element_value(req, PCMK__XA_SRC);
342  const char *digest = crm_element_value(req, PCMK__XA_DIGEST);
343 
344  if (digest) {
345  const char *version = crm_element_value(req,
347  char *digest_verify = calculate_xml_versioned_digest(input, FALSE, TRUE,
348  version ? version :
350 
351  if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) {
352  crm_err("Digest mis-match on replace from %s: %s vs. %s (expected)", peer,
353  digest_verify, digest);
354  reason = "digest mismatch";
355 
356  } else {
357  crm_info("Digest matched on replace from %s: %s", peer, digest);
358  }
359  free(digest_verify);
360 
361  } else {
362  crm_trace("No digest to verify");
363  }
364 
365  cib_version_details(existing_cib, &admin_epoch, &epoch, &updates);
366  cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates);
367 
368  if (replace_admin_epoch < admin_epoch) {
369  reason = PCMK_XA_ADMIN_EPOCH;
370 
371  } else if (replace_admin_epoch > admin_epoch) {
372  /* no more checks */
373 
374  } else if (replace_epoch < epoch) {
375  reason = PCMK_XA_EPOCH;
376 
377  } else if (replace_epoch > epoch) {
378  /* no more checks */
379 
380  } else if (replace_updates < updates) {
381  reason = PCMK_XA_NUM_UPDATES;
382  }
383 
384  if (reason != NULL) {
385  crm_info("Replacement %d.%d.%d from %s not applied to %d.%d.%d:"
386  " current %s is greater than the replacement",
387  replace_admin_epoch, replace_epoch,
388  replace_updates, peer, admin_epoch, epoch, updates, reason);
390  } else {
391  crm_info("Replaced %d.%d.%d with %d.%d.%d from %s",
392  admin_epoch, epoch, updates,
393  replace_admin_epoch, replace_epoch, replace_updates, peer);
394  }
395 
396  if (*result_cib != existing_cib) {
397  free_xml(*result_cib);
398  }
399  *result_cib = pcmk__xml_copy(NULL, input);
400 
401  } else {
402  xmlNode *obj_root = NULL;
403 
404  obj_root = pcmk_find_cib_element(*result_cib, section);
405  result = pcmk__xe_replace_match(obj_root, input);
407  if (result != pcmk_ok) {
408  crm_trace("No matching object to replace");
409  }
410  }
411 
412  return result;
413 }
414 
415 static int
416 delete_child(xmlNode *child, void *userdata)
417 {
418  xmlNode *obj_root = userdata;
419 
420  if (pcmk__xe_delete_match(obj_root, child) != pcmk_rc_ok) {
421  crm_trace("No matching object to delete: %s=%s",
422  child->name, pcmk__xe_id(child));
423  }
424 
425  return pcmk_rc_ok;
426 }
427 
428 int
429 cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
430  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
431 {
432  xmlNode *obj_root = NULL;
433 
434  crm_trace("Processing \"%s\" event", op);
435 
436  if (options & cib_xpath) {
437  return cib_process_xpath(op, options, section, req, input,
438  existing_cib, result_cib, answer);
439  }
440 
441  if (input == NULL) {
442  crm_err("Cannot perform modification with no data");
443  return -EINVAL;
444  }
445 
446  obj_root = pcmk_find_cib_element(*result_cib, section);
447  if (pcmk__xe_is(input, section)) {
448  pcmk__xe_foreach_child(input, NULL, delete_child, obj_root);
449  } else {
450  delete_child(input, obj_root);
451  }
452 
453  return pcmk_ok;
454 }
455 
456 int
457 cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
458  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
459 {
460  xmlNode *obj_root = NULL;
461  uint32_t flags = pcmk__xaf_none;
462 
463  crm_trace("Processing \"%s\" event", op);
464 
465  if (options & cib_xpath) {
466  return cib_process_xpath(op, options, section, req, input,
467  existing_cib, result_cib, answer);
468  }
469 
470  if (input == NULL) {
471  crm_err("Cannot perform modification with no data");
472  return -EINVAL;
473  }
474 
475  obj_root = pcmk_find_cib_element(*result_cib, section);
476  if (obj_root == NULL) {
477  xmlNode *tmp_section = NULL;
478  const char *path = pcmk_cib_parent_name_for(section);
479 
480  if (path == NULL) {
481  return -EINVAL;
482  }
483 
484  tmp_section = pcmk__xe_create(NULL, section);
485  cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section,
486  NULL, result_cib, answer);
487  free_xml(tmp_section);
488 
489  obj_root = pcmk_find_cib_element(*result_cib, section);
490  }
491 
492  CRM_CHECK(obj_root != NULL, return -EINVAL);
493 
494  if (pcmk_is_set(options, cib_score_update)) {
496  }
497 
498  if (pcmk__xe_update_match(obj_root, input, flags) != pcmk_rc_ok) {
499  if (options & cib_can_create) {
500  pcmk__xml_copy(obj_root, input);
501  } else {
502  return -ENXIO;
503  }
504  }
505 
506  // @COMPAT cib_mixed_update is deprecated as of 2.1.7
507  if (pcmk_is_set(options, cib_mixed_update)) {
508  int max = 0, lpc;
509  xmlXPathObjectPtr xpathObj = xpath_search(*result_cib, "//@__delete__");
510 
511  if (xpathObj) {
512  max = numXpathResults(xpathObj);
513  crm_log_xml_trace(*result_cib, "Mixed result");
514  }
515 
516  for (lpc = 0; lpc < max; lpc++) {
517  xmlNode *match = getXpathResult(xpathObj, lpc);
518  xmlChar *match_path = xmlGetNodePath(match);
519 
520  crm_debug("Destroying %s", match_path);
521  free(match_path);
522  free_xml(match);
523  }
524 
525  freeXpathObject(xpathObj);
526  }
527  return pcmk_ok;
528 }
529 
530 static int
531 add_cib_object(xmlNode * parent, xmlNode * new_obj)
532 {
533  const char *object_name = NULL;
534  const char *object_id = NULL;
535 
536  if ((parent == NULL) || (new_obj == NULL)) {
537  return -EINVAL;
538  }
539 
540  object_name = (const char *) new_obj->name;
541  if (object_name == NULL) {
542  return -EINVAL;
543  }
544 
545  object_id = pcmk__xe_id(new_obj);
546  if (pcmk__xe_first_child(parent, object_name,
547  ((object_id != NULL)? PCMK_XA_ID : NULL),
548  object_id)) {
549  return -EEXIST;
550  }
551 
552  if (object_id != NULL) {
553  crm_trace("Processing creation of <%s " PCMK_XA_ID "='%s'>",
554  object_name, object_id);
555  } else {
556  crm_trace("Processing creation of <%s>", object_name);
557  }
558 
559  /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. Due to a legacy use
560  * case, PCMK__XA_REPLACE has special meaning and should not be included in
561  * the newly created object until we can break behavioral backward
562  * compatibility.
563  *
564  * At a compatibility break, drop this and drop the definition of
565  * PCMK__XA_REPLACE. Treat it like any other attribute.
566  */
568  (void *) PCMK__XA_REPLACE);
569 
570  pcmk__xml_copy(parent, new_obj);
571  return pcmk_ok;
572 }
573 
574 static bool
575 update_results(xmlNode *failed, xmlNode *target, const char *operation,
576  int return_code)
577 {
578  xmlNode *xml_node = NULL;
579  bool was_error = false;
580  const char *error_msg = NULL;
581 
582  if (return_code != pcmk_ok) {
583  error_msg = pcmk_strerror(return_code);
584 
585  was_error = true;
586  xml_node = pcmk__xe_create(failed, PCMK__XE_FAILED_UPDATE);
587  pcmk__xml_copy(xml_node, target);
588 
589  crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(target));
590  crm_xml_add(xml_node, PCMK_XA_OBJECT_TYPE, (const char *) target->name);
591  crm_xml_add(xml_node, PCMK_XA_OPERATION, operation);
592  crm_xml_add(xml_node, PCMK_XA_REASON, error_msg);
593 
594  crm_warn("Action %s failed: %s (cde=%d)",
595  operation, error_msg, return_code);
596  }
597 
598  return was_error;
599 }
600 
601 int
602 cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
603  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
604 {
605  xmlNode *failed = NULL;
606  int result = pcmk_ok;
607  xmlNode *update_section = NULL;
608 
609  crm_trace("Processing %s for %s section",
610  op, pcmk__s(section, "unspecified"));
611  if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
612  section = NULL;
613 
614  } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) {
615  section = NULL;
616 
617  } else if (pcmk__xe_is(input, PCMK_XE_CIB)) {
618  section = NULL;
619  }
620 
621  CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL);
622 
623  if (input == NULL) {
624  crm_err("Cannot perform modification with no data");
625  return -EINVAL;
626  }
627 
628  if (section == NULL) {
629  return cib_process_modify(op, options, section, req, input, existing_cib, result_cib,
630  answer);
631  }
632 
633  // @COMPAT Deprecated since 2.1.8
634  failed = pcmk__xe_create(NULL, PCMK__XE_FAILED);
635 
636  update_section = pcmk_find_cib_element(*result_cib, section);
637  if (pcmk__xe_is(input, section)) {
638  xmlNode *a_child = NULL;
639 
640  for (a_child = pcmk__xml_first_child(input); a_child != NULL;
641  a_child = pcmk__xml_next(a_child)) {
642  result = add_cib_object(update_section, a_child);
643  if (update_results(failed, a_child, op, result)) {
644  break;
645  }
646  }
647 
648  } else {
649  result = add_cib_object(update_section, input);
650  update_results(failed, input, op, result);
651  }
652 
653  if ((result == pcmk_ok) && (failed->children != NULL)) {
654  result = -EINVAL;
655  }
656 
657  if (result != pcmk_ok) {
658  crm_log_xml_err(failed, "CIB Update failures");
659  *answer = failed;
660 
661  } else {
662  free_xml(failed);
663  }
664 
665  return result;
666 }
667 
668 int
669 cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
670  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
671 {
672  const char *originator = NULL;
673 
674  if (req != NULL) {
675  originator = crm_element_value(req, PCMK__XA_SRC);
676  }
677 
678  crm_trace("Processing \"%s\" event from %s%s",
679  op, originator,
680  (pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
681 
682  if (*result_cib != existing_cib) {
683  free_xml(*result_cib);
684  }
685  *result_cib = pcmk__xml_copy(NULL, existing_cib);
686 
687  return xml_apply_patchset(*result_cib, input, TRUE);
688 }
689 
690 // @COMPAT: v1-only
691 bool
692 cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
693 {
694  int lpc = 0, max = 0;
695  bool config_changes = false;
696  xmlXPathObject *xpathObj = NULL;
697  int format = 1;
698 
699  CRM_ASSERT(diff != NULL);
700 
701  if (*diff == NULL && last != NULL && next != NULL) {
702  *diff = pcmk__diff_v1_xml_object(last, next, false);
703  }
704 
705  if (*diff == NULL) {
706  goto done;
707  }
708 
709  crm_element_value_int(*diff, PCMK_XA_FORMAT, &format);
710  CRM_LOG_ASSERT(format == 1);
711 
712  xpathObj = xpath_search(*diff, "//" PCMK_XE_CONFIGURATION);
713  if (numXpathResults(xpathObj) > 0) {
714  config_changes = true;
715  goto done;
716  }
717  freeXpathObject(xpathObj);
718 
719  /*
720  * Do not check PCMK__XE_DIFF_ADDED "//" PCMK_XE_CIB
721  * This always contains every field and would produce a false positive
722  * every time if the checked value existed
723  */
724  xpathObj = xpath_search(*diff, "//" PCMK__XE_DIFF_REMOVED "//" PCMK_XE_CIB);
725  max = numXpathResults(xpathObj);
726 
727  for (lpc = 0; lpc < max; lpc++) {
728  xmlNode *top = getXpathResult(xpathObj, lpc);
729 
730  if (crm_element_value(top, PCMK_XA_EPOCH) != NULL) {
731  config_changes = true;
732  goto done;
733  }
734  if (crm_element_value(top, PCMK_XA_ADMIN_EPOCH) != NULL) {
735  config_changes = true;
736  goto done;
737  }
738 
739  if (crm_element_value(top, PCMK_XA_VALIDATE_WITH) != NULL) {
740  config_changes = true;
741  goto done;
742  }
743  if (crm_element_value(top, PCMK_XA_CRM_FEATURE_SET) != NULL) {
744  config_changes = true;
745  goto done;
746  }
747  if (crm_element_value(top, PCMK_XA_REMOTE_CLEAR_PORT) != NULL) {
748  config_changes = true;
749  goto done;
750  }
751  if (crm_element_value(top, PCMK_XA_REMOTE_TLS_PORT) != NULL) {
752  config_changes = true;
753  goto done;
754  }
755  }
756 
757  done:
758  freeXpathObject(xpathObj);
759  return config_changes;
760 }
761 
762 int
763 cib_process_xpath(const char *op, int options, const char *section,
764  const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
765  xmlNode **result_cib, xmlNode **answer)
766 {
767  int lpc = 0;
768  int max = 0;
769  int rc = pcmk_ok;
770  bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none);
771 
772  xmlXPathObjectPtr xpathObj = NULL;
773 
774  crm_trace("Processing \"%s\" event", op);
775 
776  if (is_query) {
777  xpathObj = xpath_search(existing_cib, section);
778  } else {
779  xpathObj = xpath_search(*result_cib, section);
780  }
781 
782  max = numXpathResults(xpathObj);
783 
784  if ((max < 1)
785  && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
786  crm_debug("%s was already removed", section);
787 
788  } else if (max < 1) {
789  crm_debug("%s: %s does not exist", op, section);
790  rc = -ENXIO;
791 
792  } else if (is_query) {
793  if (max > 1) {
794  *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
795  }
796  }
797 
798  if (pcmk_is_set(options, cib_multiple)
799  && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
800  dedupXpathResults(xpathObj);
801  }
802 
803  for (lpc = 0; lpc < max; lpc++) {
804  xmlChar *path = NULL;
805  xmlNode *match = getXpathResult(xpathObj, lpc);
806 
807  if (match == NULL) {
808  continue;
809  }
810 
811  path = xmlGetNodePath(match);
812  crm_debug("Processing %s op for %s with %s", op, section, path);
813  free(path);
814 
815  if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
816  if (match == *result_cib) {
817  /* Attempting to delete the whole "/cib" */
818  crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
819  rc = -EINVAL;
820  break;
821  }
822 
823  free_xml(match);
824  if ((options & cib_multiple) == 0) {
825  break;
826  }
827 
828  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) {
829  uint32_t flags = pcmk__xaf_none;
830 
831  if (pcmk_is_set(options, cib_score_update)) {
833  }
834 
835  if (pcmk__xe_update_match(match, input, flags) != pcmk_rc_ok) {
836  rc = -ENXIO;
837  } else if ((options & cib_multiple) == 0) {
838  break;
839  }
840 
841  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) {
842  pcmk__xml_copy(match, input);
843  break;
844 
845  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
846 
847  if (options & cib_no_children) {
848  xmlNode *shallow = pcmk__xe_create(*answer,
849  (const char *) match->name);
850 
851  pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none);
852 
853  if (*answer == NULL) {
854  *answer = shallow;
855  }
856 
857  } else if (options & cib_xpath_address) {
858  char *path = NULL;
859  xmlNode *parent = match;
860 
861  while (parent && parent->type == XML_ELEMENT_NODE) {
862  const char *id = crm_element_value(parent, PCMK_XA_ID);
863  char *new_path = NULL;
864 
865  if (id) {
866  new_path = crm_strdup_printf("/%s[@" PCMK_XA_ID "='%s']"
867  "%s",
868  parent->name, id,
869  pcmk__s(path, ""));
870  } else {
871  new_path = crm_strdup_printf("/%s%s", parent->name,
872  pcmk__s(path, ""));
873  }
874  free(path);
875  path = new_path;
876  parent = parent->parent;
877  }
878  crm_trace("Got: %s", path);
879 
880  if (*answer == NULL) {
881  *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
882  }
885  free(path);
886 
887  } else if (*answer) {
888  pcmk__xml_copy(*answer, match);
889 
890  } else {
891  *answer = match;
892  }
893 
894  } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE,
895  pcmk__str_none)) {
896  xmlNode *parent = match->parent;
897 
898  free_xml(match);
900 
901  if ((options & cib_multiple) == 0) {
902  break;
903  }
904  }
905  }
906 
907  freeXpathObject(xpathObj);
908  return rc;
909 }
#define pcmk_err_old_data
Definition: results.h:77
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
#define PCMK__XE_FAILED
#define PCMK__XA_DIGEST
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:883
#define PCMK__XE_DIFF_REMOVED
A dumping ground.
int cib__get_operation(const char *op, const cib__operation_t **operation)
Definition: cib_ops.c:144
bool cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
Definition: cib_ops.c:692
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:28
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:602
int pcmk__cmp_schemas_by_name(const char *schema1_name, const char *schema2_name)
Definition: schemas.c:691
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:457
int pcmk_rc2legacy(int rc)
Definition: results.c:546
int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
Definition: xml.c:584
#define PCMK__CIB_REQUEST_SYNC_TO_ONE
Definition: internal.h:20
#define PCMK__CIB_REQUEST_PRIMARY
Definition: internal.h:18
#define PCMK_XE_CIB
Definition: xml_names.h:79
#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:429
xmlNode * pcmk__diff_v1_xml_object(xmlNode *left, xmlNode *right, bool suppress)
Definition: patchset.c:1447
#define PCMK_XA_REMOTE_CLEAR_PORT
Definition: xml_names.h:370
int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
Definition: xml.c:1952
#define PCMK__CIB_REQUEST_QUERY
Definition: internal.h:23
#define CRM_FEATURE_SET
Definition: crm.h:72
#define PCMK_XA_NUM_UPDATES
Definition: xml_names.h:336
#define PCMK__XE_FAILED_UPDATE
#define PCMK__CIB_REQUEST_COMMIT_TRANSACT
Definition: internal.h:34
#define PCMK__XE_XPATH_QUERY
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:301
#define PCMK_XA_FORMAT
Definition: xml_names.h:286
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:228
Supported in a transaction.
Definition: internal.h:49
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:482
#define PCMK__XA_CIB_SCHEMA_MAX
#define PCMK_XA_OPERATION
Definition: xml_names.h:344
#define PCMK__CIB_REQUEST_ERASE
Definition: internal.h:27
int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
Definition: xml.c:1839
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:669
#define PCMK__CIB_REQUEST_ABS_DELETE
Definition: internal.h:31
#define crm_warn(fmt, args...)
Definition: logging.h:394
Writes to disk on success.
Definition: internal.h:48
Modifies CIB.
Definition: internal.h:44
#define crm_debug(fmt, args...)
Definition: logging.h:402
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
Definition: patchset.c:1330
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:674
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
#define PCMK__CIB_REQUEST_IS_PRIMARY
Definition: internal.h:21
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:440
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:260
#define PCMK_XA_REASON
Definition: xml_names.h:366
#define crm_trace(fmt, args...)
Definition: logging.h:404
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:763
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:98
#define PCMK_XA_EPOCH
Definition: xml_names.h:263
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:241
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:168
#define PCMK_XA_REMOTE_TLS_PORT
Definition: xml_names.h:372
#define PCMK__NELEM(a)
Definition: internal.h:48
bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
Definition: xml.c:674
Flag has no effect.
Definition: xml_internal.h:438
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
#define PCMK__XE_ALL
#define PCMK_XA_ID
Definition: xml_names.h:296
Must only be processed locally.
Definition: internal.h:46
void free_xml(xmlNode *child)
Definition: xml.c:867
#define PCMK_XE_CONFIGURATION
Definition: xml_names.h:87
int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
Definition: xml.c:2046
#define pcmk__str_copy(str)
#define PCMK_XA_VALIDATE_WITH
Definition: xml_names.h:436
#define PCMK__XE_XPATH_QUERY_PATH
int pcmk__update_schema(xmlNode **xml, const char *max_schema_name, bool transform, bool to_logs)
Update CIB XML to latest schema that validates it.
Definition: schemas.c:1141
#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
#define PCMK_XA_CRM_FEATURE_SET
Definition: xml_names.h:249
Replaces CIB.
Definition: internal.h:47
xmlNode * createEmptyCib(int cib_epoch)
Create XML for a new (empty) CIB.
Definition: cib_utils.c:228
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:304
Requires privileges.
Definition: internal.h:45
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path)
Definition: xpath.c:139
#define crm_log_xml_err(xml, text)
Definition: logging.h:407
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:683
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define PCMK__CIB_REQUEST_SCHEMAS
Definition: internal.h:35
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml.c:2288
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:391
#define CRM_ASSERT(expr)
Definition: results.h:42
Prefer stderr to logs.
Definition: cib_types.h:62
#define PCMK__CIB_REQUEST_REPLACE
Definition: internal.h:28
xmlNode * input
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:165
#define PCMK__CIB_REQUEST_SYNC_TO_ALL
Definition: internal.h:19
#define PCMK__CIB_REQUEST_SECONDARY
Definition: internal.h:17
#define PCMK__CIB_REQUEST_NOOP
Definition: internal.h:32
const char * name
Definition: internal.h:89
#define pcmk_ok
Definition: results.h:69
bool pcmk__xml_tree_foreach(xmlNode *xml, bool(*fn)(xmlNode *, void *), void *user_data)
Definition: xml.c:42
#define crm_log_xml_trace(xml, text)
Definition: logging.h:412
#define PCMK__CIB_REQUEST_MODIFY
Definition: internal.h:25
#define CRM_OP_PING
Definition: crm.h:119
const char * parent
Definition: cib.c:27
#define PCMK_XA_OBJECT_TYPE
Definition: xml_names.h:339
#define PCMK_XA_ADMIN_EPOCH
Definition: xml_names.h:227
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:720
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:39
Treat new attribute values as atomic score updates where possible.
Definition: cib_types.h:130
No special attributes.
Definition: internal.h:43
#define PCMK__CIB_REQUEST_SHUTDOWN
Definition: internal.h:33
#define crm_info(fmt, args...)
Definition: logging.h:399
#define PCMK__XA_REPLACE
#define PCMK__CIB_REQUEST_APPLY_PATCH
Definition: internal.h:29
uint32_t version
Definition: remote.c:213
#define PCMK__XA_SRC
uint64_t flags
Definition: remote.c:215
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:289
#define PCMK__CIB_REQUEST_DELETE
Definition: internal.h:26