pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
patchset.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 <sys/types.h>
14 #include <unistd.h>
15 #include <time.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <bzlib.h>
20 
21 #include <libxml/tree.h>
22 
23 #include <crm/crm.h>
24 #include <crm/msg_xml.h>
25 #include <crm/common/xml.h>
26 #include <crm/common/xml_internal.h> // CRM_XML_LOG_BASE, etc.
27 #include "crmcommon_private.h"
28 
29 static xmlNode *subtract_xml_comment(xmlNode *parent, xmlNode *left,
30  xmlNode *right, gboolean *changed);
31 
32 /* Add changes for specified XML to patchset.
33  * For patchset format, refer to diff schema.
34  */
35 static void
36 add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
37 {
38  xmlNode *cIter = NULL;
39  xmlAttr *pIter = NULL;
40  xmlNode *change = NULL;
41  xml_node_private_t *nodepriv = xml->_private;
42  const char *value = NULL;
43 
44  if (nodepriv == NULL) {
45  /* Elements that shouldn't occur in a CIB don't have _private set. They
46  * should be stripped out, ignored, or have an error thrown by any code
47  * that processes their parent, so we ignore any changes to them.
48  */
49  return;
50  }
51 
52  // If this XML node is new, just report that
53  if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
54  GString *xpath = pcmk__element_xpath(xml->parent);
55 
56  if (xpath != NULL) {
57  int position = pcmk__xml_position(xml, pcmk__xf_deleted);
58 
59  change = create_xml_node(patchset, XML_DIFF_CHANGE);
60 
61  crm_xml_add(change, XML_DIFF_OP, "create");
62  crm_xml_add(change, XML_DIFF_PATH, (const char *) xpath->str);
63  crm_xml_add_int(change, XML_DIFF_POSITION, position);
64  add_node_copy(change, xml);
65  g_string_free(xpath, TRUE);
66  }
67 
68  return;
69  }
70 
71  // Check each of the XML node's attributes for changes
72  for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
73  pIter = pIter->next) {
74  xmlNode *attr = NULL;
75 
76  nodepriv = pIter->_private;
77  if (!pcmk_any_flags_set(nodepriv->flags, pcmk__xf_deleted|pcmk__xf_dirty)) {
78  continue;
79  }
80 
81  if (change == NULL) {
82  GString *xpath = pcmk__element_xpath(xml);
83 
84  if (xpath != NULL) {
85  change = create_xml_node(patchset, XML_DIFF_CHANGE);
86 
87  crm_xml_add(change, XML_DIFF_OP, "modify");
88  crm_xml_add(change, XML_DIFF_PATH, (const char *) xpath->str);
89 
90  change = create_xml_node(change, XML_DIFF_LIST);
91  g_string_free(xpath, TRUE);
92  }
93  }
94 
95  attr = create_xml_node(change, XML_DIFF_ATTR);
96 
97  crm_xml_add(attr, XML_NVPAIR_ATTR_NAME, (const char *)pIter->name);
98  if (nodepriv->flags & pcmk__xf_deleted) {
99  crm_xml_add(attr, XML_DIFF_OP, "unset");
100 
101  } else {
102  crm_xml_add(attr, XML_DIFF_OP, "set");
103 
104  value = pcmk__xml_attr_value(pIter);
105  crm_xml_add(attr, XML_NVPAIR_ATTR_VALUE, value);
106  }
107  }
108 
109  if (change) {
110  xmlNode *result = NULL;
111 
112  change = create_xml_node(change->parent, XML_DIFF_RESULT);
113  result = create_xml_node(change, (const char *)xml->name);
114 
115  for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
116  pIter = pIter->next) {
117  nodepriv = pIter->_private;
118  if (!pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
119  value = crm_element_value(xml, (const char *) pIter->name);
120  crm_xml_add(result, (const char *)pIter->name, value);
121  }
122  }
123  }
124 
125  // Now recursively do the same for each child node of this node
126  for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
127  cIter = pcmk__xml_next(cIter)) {
128  add_xml_changes_to_patchset(cIter, patchset);
129  }
130 
131  nodepriv = xml->_private;
132  if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
133  GString *xpath = pcmk__element_xpath(xml);
134 
135  crm_trace("%s.%s moved to position %d",
136  xml->name, ID(xml), pcmk__xml_position(xml, pcmk__xf_skip));
137 
138  if (xpath != NULL) {
139  change = create_xml_node(patchset, XML_DIFF_CHANGE);
140 
141  crm_xml_add(change, XML_DIFF_OP, "move");
142  crm_xml_add(change, XML_DIFF_PATH, (const char *) xpath->str);
145  g_string_free(xpath, TRUE);
146  }
147  }
148 }
149 
150 static bool
151 is_config_change(xmlNode *xml)
152 {
153  GList *gIter = NULL;
154  xml_node_private_t *nodepriv = NULL;
155  xml_doc_private_t *docpriv;
156  xmlNode *config = first_named_child(xml, XML_CIB_TAG_CONFIGURATION);
157 
158  if (config) {
159  nodepriv = config->_private;
160  }
161  if ((nodepriv != NULL) && pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
162  return TRUE;
163  }
164 
165  if ((xml->doc != NULL) && (xml->doc->_private != NULL)) {
166  docpriv = xml->doc->_private;
167  for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
168  pcmk__deleted_xml_t *deleted_obj = gIter->data;
169 
170  if (strstr(deleted_obj->path,
171  "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION) != NULL) {
172  return TRUE;
173  }
174  }
175  }
176  return FALSE;
177 }
178 
179 static void
180 xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
181  gboolean changed)
182 {
183  int lpc = 0;
184  xmlNode *cib = NULL;
185  xmlNode *diff_child = NULL;
186 
187  const char *tag = NULL;
188 
189  const char *vfields[] = {
193  };
194 
195  if (local_diff == NULL) {
196  crm_trace("Nothing to do");
197  return;
198  }
199 
200  tag = XML_TAG_DIFF_REMOVED;
201  diff_child = find_xml_node(local_diff, tag, FALSE);
202  if (diff_child == NULL) {
203  diff_child = create_xml_node(local_diff, tag);
204  }
205 
206  tag = XML_TAG_CIB;
207  cib = find_xml_node(diff_child, tag, FALSE);
208  if (cib == NULL) {
209  cib = create_xml_node(diff_child, tag);
210  }
211 
212  for (lpc = 0; (last != NULL) && (lpc < PCMK__NELEM(vfields)); lpc++) {
213  const char *value = crm_element_value(last, vfields[lpc]);
214 
215  crm_xml_add(diff_child, vfields[lpc], value);
216  if (changed || lpc == 2) {
217  crm_xml_add(cib, vfields[lpc], value);
218  }
219  }
220 
221  tag = XML_TAG_DIFF_ADDED;
222  diff_child = find_xml_node(local_diff, tag, FALSE);
223  if (diff_child == NULL) {
224  diff_child = create_xml_node(local_diff, tag);
225  }
226 
227  tag = XML_TAG_CIB;
228  cib = find_xml_node(diff_child, tag, FALSE);
229  if (cib == NULL) {
230  cib = create_xml_node(diff_child, tag);
231  }
232 
233  for (lpc = 0; next && lpc < PCMK__NELEM(vfields); lpc++) {
234  const char *value = crm_element_value(next, vfields[lpc]);
235 
236  crm_xml_add(diff_child, vfields[lpc], value);
237  }
238 
239  for (xmlAttrPtr a = pcmk__xe_first_attr(next); a != NULL; a = a->next) {
240 
241  const char *p_value = pcmk__xml_attr_value(a);
242 
243  xmlSetProp(cib, a->name, (pcmkXmlStr) p_value);
244  }
245 
246  crm_log_xml_explicit(local_diff, "Repaired-diff");
247 }
248 
249 static xmlNode *
250 xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config,
251  bool suppress)
252 {
253  xmlNode *patchset = diff_xml_object(source, target, suppress);
254 
255  if (patchset) {
257  xml_repair_v1_diff(source, target, patchset, config);
258  crm_xml_add(patchset, PCMK_XA_FORMAT, "1");
259  }
260  return patchset;
261 }
262 
263 static xmlNode *
264 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
265 {
266  int lpc = 0;
267  GList *gIter = NULL;
268  xml_doc_private_t *docpriv;
269 
270  xmlNode *v = NULL;
271  xmlNode *version = NULL;
272  xmlNode *patchset = NULL;
273  const char *vfields[] = {
277  };
278 
280  if (!xml_document_dirty(target)) {
281  return NULL;
282  }
283 
284  CRM_ASSERT(target->doc);
285  docpriv = target->doc->_private;
286 
287  patchset = create_xml_node(NULL, XML_TAG_DIFF);
288  crm_xml_add_int(patchset, PCMK_XA_FORMAT, 2);
289 
291 
293  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
294  const char *value = crm_element_value(source, vfields[lpc]);
295 
296  if (value == NULL) {
297  value = "1";
298  }
299  crm_xml_add(v, vfields[lpc], value);
300  }
301 
303  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
304  const char *value = crm_element_value(target, vfields[lpc]);
305 
306  if (value == NULL) {
307  value = "1";
308  }
309  crm_xml_add(v, vfields[lpc], value);
310  }
311 
312  for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
313  pcmk__deleted_xml_t *deleted_obj = gIter->data;
314  xmlNode *change = create_xml_node(patchset, XML_DIFF_CHANGE);
315 
316  crm_xml_add(change, XML_DIFF_OP, "delete");
317  crm_xml_add(change, XML_DIFF_PATH, deleted_obj->path);
318  if (deleted_obj->position >= 0) {
319  crm_xml_add_int(change, XML_DIFF_POSITION, deleted_obj->position);
320  }
321  }
322 
323  add_xml_changes_to_patchset(target, patchset);
324  return patchset;
325 }
326 
327 xmlNode *
328 xml_create_patchset(int format, xmlNode *source, xmlNode *target,
329  bool *config_changed, bool manage_version)
330 {
331  int counter = 0;
332  bool config = FALSE;
333  xmlNode *patch = NULL;
334  const char *version = crm_element_value(source, XML_ATTR_CRM_VERSION);
335 
337  if (!xml_document_dirty(target)) {
338  crm_trace("No change %d", format);
339  return NULL; /* No change */
340  }
341 
342  config = is_config_change(target);
343  if (config_changed) {
344  *config_changed = config;
345  }
346 
347  if (manage_version && config) {
348  crm_trace("Config changed %d", format);
350 
353 
354  } else if (manage_version) {
356  crm_trace("Status changed %d - %d %s", format, counter,
358  crm_xml_add_int(target, XML_ATTR_NUMUPDATES, (counter + 1));
359  }
360 
361  if (format == 0) {
362  if (compare_version("3.0.8", version) < 0) {
363  format = 2;
364  } else {
365  format = 1;
366  }
367  crm_trace("Using patch format %d for version: %s", format, version);
368  }
369 
370  switch (format) {
371  case 1:
372  patch = xml_create_patchset_v1(source, target, config, FALSE);
373  break;
374  case 2:
375  patch = xml_create_patchset_v2(source, target);
376  break;
377  default:
378  crm_err("Unknown patch format: %d", format);
379  return NULL;
380  }
381  return patch;
382 }
383 
384 void
385 patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target,
386  bool with_digest)
387 {
388  int format = 1;
389  const char *version = NULL;
390  char *digest = NULL;
391 
392  if ((patch == NULL) || (source == NULL) || (target == NULL)) {
393  return;
394  }
395 
396  /* We should always call xml_accept_changes() before calculating a digest.
397  * Otherwise, with an on-tracking dirty target, we could get a wrong digest.
398  */
400 
401  crm_element_value_int(patch, PCMK_XA_FORMAT, &format);
402  if ((format > 1) && !with_digest) {
403  return;
404  }
405 
407  digest = calculate_xml_versioned_digest(target, FALSE, TRUE, version);
408 
409  crm_xml_add(patch, XML_ATTR_DIGEST, digest);
410  free(digest);
411 
412  return;
413 }
414 
415 // Return true if attribute name is not "id"
416 static bool
417 not_id(xmlAttrPtr attr, void *user_data)
418 {
419  return strcmp((const char *) attr->name, XML_ATTR_ID) != 0;
420 }
421 
422 // Apply the removals section of an v1 patchset to an XML node
423 static void
424 process_v1_removals(xmlNode *target, xmlNode *patch)
425 {
426  xmlNode *patch_child = NULL;
427  xmlNode *cIter = NULL;
428 
429  char *id = NULL;
430  const char *value = NULL;
431 
432  if ((target == NULL) || (patch == NULL)) {
433  return;
434  }
435 
436  if (target->type == XML_COMMENT_NODE) {
437  gboolean dummy;
438 
439  subtract_xml_comment(target->parent, target, patch, &dummy);
440  }
441 
442  CRM_CHECK(pcmk__xe_is(target, (const char *) patch->name), return);
443  CRM_CHECK(pcmk__str_eq(ID(target), ID(patch), pcmk__str_casei), return);
444 
445  // Check for XML_DIFF_MARKER in a child
447  value = crm_element_value(patch, XML_DIFF_MARKER);
448  if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
449  crm_trace("We are the root of the deletion: %s.id=%s",
450  target->name, id);
451  free_xml(target);
452  free(id);
453  return;
454  }
455 
456  // Removing then restoring id would change ordering of properties
457  pcmk__xe_remove_matching_attrs(patch, not_id, NULL);
458 
459  // Changes to child objects
460  cIter = pcmk__xml_first_child(target);
461  while (cIter) {
462  xmlNode *target_child = cIter;
463 
464  cIter = pcmk__xml_next(cIter);
465  patch_child = pcmk__xml_match(patch, target_child, false);
466  process_v1_removals(target_child, patch_child);
467  }
468  free(id);
469 }
470 
471 // Apply the additions section of an v1 patchset to an XML node
472 static void
473 process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch)
474 {
475  xmlNode *patch_child = NULL;
476  xmlNode *target_child = NULL;
477  xmlAttrPtr xIter = NULL;
478 
479  const char *id = NULL;
480  const char *name = NULL;
481  const char *value = NULL;
482 
483  if (patch == NULL) {
484  return;
485  } else if ((parent == NULL) && (target == NULL)) {
486  return;
487  }
488 
489  // Check for XML_DIFF_MARKER in a child
490  name = (const char *) patch->name;
491  value = crm_element_value(patch, XML_DIFF_MARKER);
492  if ((target == NULL) && (value != NULL)
493  && (strcmp(value, "added:top") == 0)) {
494  id = ID(patch);
495  crm_trace("We are the root of the addition: %s.id=%s", name, id);
496  add_node_copy(parent, patch);
497  return;
498 
499  } else if (target == NULL) {
500  id = ID(patch);
501  crm_err("Could not locate: %s.id=%s", name, id);
502  return;
503  }
504 
505  if (target->type == XML_COMMENT_NODE) {
506  pcmk__xc_update(parent, target, patch);
507  }
508 
509  CRM_CHECK(pcmk__xe_is(target, name), return);
510  CRM_CHECK(pcmk__str_eq(ID(target), ID(patch), pcmk__str_casei), return);
511 
512  for (xIter = pcmk__xe_first_attr(patch); xIter != NULL;
513  xIter = xIter->next) {
514  const char *p_name = (const char *) xIter->name;
515  const char *p_value = pcmk__xml_attr_value(xIter);
516 
517  xml_remove_prop(target, p_name); // Preserve patch order
518  crm_xml_add(target, p_name, p_value);
519  }
520 
521  // Changes to child objects
522  for (patch_child = pcmk__xml_first_child(patch); patch_child != NULL;
523  patch_child = pcmk__xml_next(patch_child)) {
524 
525  target_child = pcmk__xml_match(target, patch_child, false);
526  process_v1_additions(target, target_child, patch_child);
527  }
528 }
529 
541 static bool
542 find_patch_xml_node(const xmlNode *patchset, int format, bool added,
543  xmlNode **patch_node)
544 {
545  xmlNode *cib_node;
546  const char *label;
547 
548  switch (format) {
549  case 1:
550  label = added? XML_TAG_DIFF_ADDED : XML_TAG_DIFF_REMOVED;
551  *patch_node = find_xml_node(patchset, label, FALSE);
552  cib_node = find_xml_node(*patch_node, "cib", FALSE);
553  if (cib_node != NULL) {
554  *patch_node = cib_node;
555  }
556  break;
557  case 2:
558  label = added? "target" : "source";
559  *patch_node = find_xml_node(patchset, "version", FALSE);
560  *patch_node = find_xml_node(*patch_node, label, FALSE);
561  break;
562  default:
563  crm_warn("Unknown patch format: %d", format);
564  *patch_node = NULL;
565  return FALSE;
566  }
567  return TRUE;
568 }
569 
570 // Get CIB versions used for additions and deletions in a patchset
571 bool
572 xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
573 {
574  int lpc = 0;
575  int format = 1;
576  xmlNode *tmp = NULL;
577 
578  const char *vfields[] = {
582  };
583 
584 
585  crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
586 
587  /* Process removals */
588  if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
589  return -EINVAL;
590  }
591  if (tmp != NULL) {
592  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
593  crm_element_value_int(tmp, vfields[lpc], &(del[lpc]));
594  crm_trace("Got %d for del[%s]", del[lpc], vfields[lpc]);
595  }
596  }
597 
598  /* Process additions */
599  if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
600  return -EINVAL;
601  }
602  if (tmp != NULL) {
603  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
604  crm_element_value_int(tmp, vfields[lpc], &(add[lpc]));
605  crm_trace("Got %d for add[%s]", add[lpc], vfields[lpc]);
606  }
607  }
608  return pcmk_ok;
609 }
610 
620 static int
621 xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset)
622 {
623  int lpc = 0;
624  bool changed = FALSE;
625 
626  int this[] = { 0, 0, 0 };
627  int add[] = { 0, 0, 0 };
628  int del[] = { 0, 0, 0 };
629 
630  const char *vfields[] = {
634  };
635 
636  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
637  crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
638  crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
639  if (this[lpc] < 0) {
640  this[lpc] = 0;
641  }
642  }
643 
644  /* Set some defaults in case nothing is present */
645  add[0] = this[0];
646  add[1] = this[1];
647  add[2] = this[2] + 1;
648  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
649  del[lpc] = this[lpc];
650  }
651 
652  xml_patch_versions(patchset, add, del);
653 
654  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
655  if (this[lpc] < del[lpc]) {
656  crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)",
657  vfields[lpc], this[0], this[1], this[2],
658  del[0], del[1], del[2], add[0], add[1], add[2]);
659  return pcmk_rc_diff_resync;
660 
661  } else if (this[lpc] > del[lpc]) {
662  crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p",
663  vfields[lpc], this[0], this[1], this[2],
664  del[0], del[1], del[2], add[0], add[1], add[2], patchset);
665  crm_log_xml_info(patchset, "OldPatch");
666  return pcmk_rc_old_data;
667  }
668  }
669 
670  for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
671  if (add[lpc] > del[lpc]) {
672  changed = TRUE;
673  }
674  }
675 
676  if (!changed) {
677  crm_notice("Versions did not change in patch %d.%d.%d",
678  add[0], add[1], add[2]);
679  return pcmk_rc_old_data;
680  }
681 
682  crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
683  add[0], add[1], add[2], this[0], this[1], this[2]);
684  return pcmk_rc_ok;
685 }
686 
696 static int
697 apply_v1_patchset(xmlNode *xml, const xmlNode *patchset)
698 {
699  int rc = pcmk_rc_ok;
700  int root_nodes_seen = 0;
701 
702  xmlNode *child_diff = NULL;
703  xmlNode *added = find_xml_node(patchset, XML_TAG_DIFF_ADDED, FALSE);
704  xmlNode *removed = find_xml_node(patchset, XML_TAG_DIFF_REMOVED, FALSE);
705  xmlNode *old = copy_xml(xml);
706 
707  crm_trace("Subtraction Phase");
708  for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
709  child_diff = pcmk__xml_next(child_diff)) {
710  CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
711  if (root_nodes_seen == 0) {
712  process_v1_removals(xml, child_diff);
713  }
714  root_nodes_seen++;
715  }
716 
717  if (root_nodes_seen > 1) {
718  crm_err("(-) Diffs cannot contain more than one change set... saw %d",
719  root_nodes_seen);
720  rc = ENOTUNIQ;
721  }
722 
723  root_nodes_seen = 0;
724  crm_trace("Addition Phase");
725  if (rc == pcmk_rc_ok) {
726  xmlNode *child_diff = NULL;
727 
728  for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
729  child_diff = pcmk__xml_next(child_diff)) {
730  CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
731  if (root_nodes_seen == 0) {
732  process_v1_additions(NULL, xml, child_diff);
733  }
734  root_nodes_seen++;
735  }
736  }
737 
738  if (root_nodes_seen > 1) {
739  crm_err("(+) Diffs cannot contain more than one change set... saw %d",
740  root_nodes_seen);
741  rc = ENOTUNIQ;
742  }
743 
744  purge_diff_markers(xml); // Purge prior to checking digest
745 
746  free_xml(old);
747  return rc;
748 }
749 
750 // Return first child matching element name and optionally id or position
751 static xmlNode *
752 first_matching_xml_child(const xmlNode *parent, const char *name,
753  const char *id, int position)
754 {
755  xmlNode *cIter = NULL;
756 
757  for (cIter = pcmk__xml_first_child(parent); cIter != NULL;
758  cIter = pcmk__xml_next(cIter)) {
759  if (strcmp((const char *) cIter->name, name) != 0) {
760  continue;
761  } else if (id) {
762  const char *cid = ID(cIter);
763 
764  if ((cid == NULL) || (strcmp(cid, id) != 0)) {
765  continue;
766  }
767  }
768 
769  // "position" makes sense only for XML comments for now
770  if ((cIter->type == XML_COMMENT_NODE)
771  && (position >= 0)
772  && (pcmk__xml_position(cIter, pcmk__xf_skip) != position)) {
773  continue;
774  }
775 
776  return cIter;
777  }
778  return NULL;
779 }
780 
794 static xmlNode *
795 search_v2_xpath(const xmlNode *top, const char *key, int target_position)
796 {
797  xmlNode *target = (xmlNode *) top->doc;
798  const char *current = key;
799  char *section;
800  char *remainder;
801  char *id;
802  char *tag;
803  char *path = NULL;
804  int rc;
805  size_t key_len;
806 
807  CRM_CHECK(key != NULL, return NULL);
808  key_len = strlen(key);
809 
810  /* These are scanned from key after a slash, so they can't be bigger
811  * than key_len - 1 characters plus a null terminator.
812  */
813 
814  remainder = calloc(key_len, sizeof(char));
815  CRM_ASSERT(remainder != NULL);
816 
817  section = calloc(key_len, sizeof(char));
818  CRM_ASSERT(section != NULL);
819 
820  id = calloc(key_len, sizeof(char));
821  CRM_ASSERT(id != NULL);
822 
823  tag = calloc(key_len, sizeof(char));
824  CRM_ASSERT(tag != NULL);
825 
826  do {
827  // Look for /NEXT_COMPONENT/REMAINING_COMPONENTS
828  rc = sscanf(current, "/%[^/]%s", section, remainder);
829  if (rc > 0) {
830  // Separate FIRST_COMPONENT into TAG[@id='ID']
831  int f = sscanf(section, "%[^[][@" XML_ATTR_ID "='%[^']", tag, id);
832  int current_position = -1;
833 
834  /* The target position is for the final component tag, so only use
835  * it if there is nothing left to search after this component.
836  */
837  if ((rc == 1) && (target_position >= 0)) {
838  current_position = target_position;
839  }
840 
841  switch (f) {
842  case 1:
843  target = first_matching_xml_child(target, tag, NULL,
844  current_position);
845  break;
846  case 2:
847  target = first_matching_xml_child(target, tag, id,
848  current_position);
849  break;
850  default:
851  // This should not be possible
852  target = NULL;
853  break;
854  }
855  current = remainder;
856  }
857 
858  // Continue if something remains to search, and we've matched so far
859  } while ((rc == 2) && target);
860 
861  if (target) {
862  crm_trace("Found %s for %s",
863  (path = (char *) xmlGetNodePath(target)), key);
864  free(path);
865  } else {
866  crm_debug("No match for %s", key);
867  }
868 
869  free(remainder);
870  free(section);
871  free(tag);
872  free(id);
873  return target;
874 }
875 
876 typedef struct xml_change_obj_s {
877  const xmlNode *change;
878  xmlNode *match;
880 
881 static gint
882 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
883 {
884  const xml_change_obj_t *change_obj_a = a;
885  const xml_change_obj_t *change_obj_b = b;
886  int position_a = -1;
887  int position_b = -1;
888 
889  crm_element_value_int(change_obj_a->change, XML_DIFF_POSITION, &position_a);
890  crm_element_value_int(change_obj_b->change, XML_DIFF_POSITION, &position_b);
891 
892  if (position_a < position_b) {
893  return -1;
894 
895  } else if (position_a > position_b) {
896  return 1;
897  }
898 
899  return 0;
900 }
901 
911 static int
912 apply_v2_patchset(xmlNode *xml, const xmlNode *patchset)
913 {
914  int rc = pcmk_rc_ok;
915  const xmlNode *change = NULL;
916  GList *change_objs = NULL;
917  GList *gIter = NULL;
918 
919  for (change = pcmk__xml_first_child(patchset); change != NULL;
920  change = pcmk__xml_next(change)) {
921  xmlNode *match = NULL;
922  const char *op = crm_element_value(change, XML_DIFF_OP);
923  const char *xpath = crm_element_value(change, XML_DIFF_PATH);
924  int position = -1;
925 
926  if (op == NULL) {
927  continue;
928  }
929 
930  crm_trace("Processing %s %s", change->name, op);
931 
932  // "delete" changes for XML comments are generated with "position"
933  if (strcmp(op, "delete") == 0) {
934  crm_element_value_int(change, XML_DIFF_POSITION, &position);
935  }
936  match = search_v2_xpath(xml, xpath, position);
937  crm_trace("Performing %s on %s with %p", op, xpath, match);
938 
939  if ((match == NULL) && (strcmp(op, "delete") == 0)) {
940  crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
941  continue;
942 
943  } else if (match == NULL) {
944  crm_err("No %s match for %s in %p", op, xpath, xml->doc);
945  rc = pcmk_rc_diff_failed;
946  continue;
947 
948  } else if ((strcmp(op, "create") == 0) || (strcmp(op, "move") == 0)) {
949  // Delay the adding of a "create" object
950  xml_change_obj_t *change_obj = calloc(1, sizeof(xml_change_obj_t));
951 
952  CRM_ASSERT(change_obj != NULL);
953 
954  change_obj->change = change;
955  change_obj->match = match;
956 
957  change_objs = g_list_append(change_objs, change_obj);
958 
959  if (strcmp(op, "move") == 0) {
960  // Temporarily put the "move" object after the last sibling
961  if ((match->parent != NULL) && (match->parent->last != NULL)) {
962  xmlAddNextSibling(match->parent->last, match);
963  }
964  }
965 
966  } else if (strcmp(op, "delete") == 0) {
967  free_xml(match);
968 
969  } else if (strcmp(op, "modify") == 0) {
970  xmlNode *attrs = NULL;
971 
972  attrs = pcmk__xml_first_child(first_named_child(change,
973  XML_DIFF_RESULT));
974  if (attrs == NULL) {
975  rc = ENOMSG;
976  continue;
977  }
978  pcmk__xe_remove_matching_attrs(match, NULL, NULL); // Remove all
979 
980  for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
981  pIter = pIter->next) {
982  const char *name = (const char *) pIter->name;
983  const char *value = pcmk__xml_attr_value(pIter);
984 
985  crm_xml_add(match, name, value);
986  }
987 
988  } else {
989  crm_err("Unknown operation: %s", op);
990  rc = pcmk_rc_diff_failed;
991  }
992  }
993 
994  // Changes should be generated in the right order. Double checking.
995  change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
996 
997  for (gIter = change_objs; gIter; gIter = gIter->next) {
998  xml_change_obj_t *change_obj = gIter->data;
999  xmlNode *match = change_obj->match;
1000  const char *op = NULL;
1001  const char *xpath = NULL;
1002 
1003  change = change_obj->change;
1004 
1005  op = crm_element_value(change, XML_DIFF_OP);
1006  xpath = crm_element_value(change, XML_DIFF_PATH);
1007 
1008  crm_trace("Continue performing %s on %s with %p", op, xpath, match);
1009 
1010  if (strcmp(op, "create") == 0) {
1011  int position = 0;
1012  xmlNode *child = NULL;
1013  xmlNode *match_child = NULL;
1014 
1015  match_child = match->children;
1016  crm_element_value_int(change, XML_DIFF_POSITION, &position);
1017 
1018  while ((match_child != NULL)
1019  && (position != pcmk__xml_position(match_child, pcmk__xf_skip))) {
1020  match_child = match_child->next;
1021  }
1022 
1023  child = xmlDocCopyNode(change->children, match->doc, 1);
1024  if (child == NULL) {
1025  return ENOMEM;
1026  }
1027 
1028  if (match_child) {
1029  crm_trace("Adding %s at position %d", child->name, position);
1030  xmlAddPrevSibling(match_child, child);
1031 
1032  } else if (match->last) {
1033  crm_trace("Adding %s at position %d (end)",
1034  child->name, position);
1035  xmlAddNextSibling(match->last, child);
1036 
1037  } else {
1038  crm_trace("Adding %s at position %d (first)",
1039  child->name, position);
1040  CRM_LOG_ASSERT(position == 0);
1041  xmlAddChild(match, child);
1042  }
1043  pcmk__mark_xml_created(child);
1044 
1045  } else if (strcmp(op, "move") == 0) {
1046  int position = 0;
1047 
1048  crm_element_value_int(change, XML_DIFF_POSITION, &position);
1049  if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
1050  xmlNode *match_child = NULL;
1051  int p = position;
1052 
1053  if (p > pcmk__xml_position(match, pcmk__xf_skip)) {
1054  p++; // Skip ourselves
1055  }
1056 
1057  CRM_ASSERT(match->parent != NULL);
1058  match_child = match->parent->children;
1059 
1060  while ((match_child != NULL)
1061  && (p != pcmk__xml_position(match_child, pcmk__xf_skip))) {
1062  match_child = match_child->next;
1063  }
1064 
1065  crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
1066  match->name, position,
1068  match->prev, (match_child? "next":"last"),
1069  (match_child? match_child : match->parent->last));
1070 
1071  if (match_child) {
1072  xmlAddPrevSibling(match_child, match);
1073 
1074  } else {
1075  CRM_ASSERT(match->parent->last != NULL);
1076  xmlAddNextSibling(match->parent->last, match);
1077  }
1078 
1079  } else {
1080  crm_trace("%s is already in position %d",
1081  match->name, position);
1082  }
1083 
1084  if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
1085  crm_err("Moved %s.%s to position %d instead of %d (%p)",
1086  match->name, ID(match),
1088  position, match->prev);
1089  rc = pcmk_rc_diff_failed;
1090  }
1091  }
1092  }
1093 
1094  g_list_free_full(change_objs, free);
1095  return rc;
1096 }
1097 
1098 int
1099 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
1100 {
1101  int format = 1;
1102  int rc = pcmk_ok;
1103  xmlNode *old = NULL;
1104  const char *digest = NULL;
1105 
1106  if (patchset == NULL) {
1107  return rc;
1108  }
1109 
1110  pcmk__log_xml_patchset(LOG_TRACE, patchset);
1111 
1112  if (check_version) {
1113  rc = pcmk_rc2legacy(xml_patch_version_check(xml, patchset));
1114  if (rc != pcmk_ok) {
1115  return rc;
1116  }
1117  }
1118 
1119  digest = crm_element_value(patchset, XML_ATTR_DIGEST);
1120  if (digest != NULL) {
1121  /* Make original XML available for logging in case result doesn't have
1122  * expected digest
1123  */
1124  pcmk__if_tracing(old = copy_xml(xml), {});
1125  }
1126 
1127  if (rc == pcmk_ok) {
1128  crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
1129  switch (format) {
1130  case 1:
1131  rc = pcmk_rc2legacy(apply_v1_patchset(xml, patchset));
1132  break;
1133  case 2:
1134  rc = pcmk_rc2legacy(apply_v2_patchset(xml, patchset));
1135  break;
1136  default:
1137  crm_err("Unknown patch format: %d", format);
1138  rc = -EINVAL;
1139  }
1140  }
1141 
1142  if ((rc == pcmk_ok) && (digest != NULL)) {
1143  char *new_digest = NULL;
1145 
1146  new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
1147  if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
1148  crm_info("v%d digest mis-match: expected %s, calculated %s",
1149  format, digest, new_digest);
1150  rc = -pcmk_err_diff_failed;
1152  {
1153  save_xml_to_file(old, "PatchDigest:input", NULL);
1154  save_xml_to_file(xml, "PatchDigest:result", NULL);
1155  save_xml_to_file(patchset, "PatchDigest:diff", NULL);
1156  },
1157  {}
1158  );
1159 
1160  } else {
1161  crm_trace("v%d digest matched: expected %s, calculated %s",
1162  format, digest, new_digest);
1163  }
1164  free(new_digest);
1165  free(version);
1166  }
1167  free_xml(old);
1168  return rc;
1169 }
1170 
1171 void
1172 purge_diff_markers(xmlNode *a_node)
1173 {
1174  xmlNode *child = NULL;
1175 
1176  CRM_CHECK(a_node != NULL, return);
1177 
1179  for (child = pcmk__xml_first_child(a_node); child != NULL;
1180  child = pcmk__xml_next(child)) {
1181  purge_diff_markers(child);
1182  }
1183 }
1184 
1185 xmlNode *
1186 diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
1187 {
1188  xmlNode *tmp1 = NULL;
1189  xmlNode *diff = create_xml_node(NULL, XML_TAG_DIFF);
1190  xmlNode *removed = create_xml_node(diff, XML_TAG_DIFF_REMOVED);
1191  xmlNode *added = create_xml_node(diff, XML_TAG_DIFF_ADDED);
1192 
1194 
1195  tmp1 = subtract_xml_object(removed, old, new, FALSE, NULL, "removed:top");
1196  if (suppress && (tmp1 != NULL) && can_prune_leaf(tmp1)) {
1197  free_xml(tmp1);
1198  }
1199 
1200  tmp1 = subtract_xml_object(added, new, old, TRUE, NULL, "added:top");
1201  if (suppress && (tmp1 != NULL) && can_prune_leaf(tmp1)) {
1202  free_xml(tmp1);
1203  }
1204 
1205  if ((added->children == NULL) && (removed->children == NULL)) {
1206  free_xml(diff);
1207  diff = NULL;
1208  }
1209 
1210  return diff;
1211 }
1212 
1213 static xmlNode *
1214 subtract_xml_comment(xmlNode *parent, xmlNode *left, xmlNode *right,
1215  gboolean *changed)
1216 {
1217  CRM_CHECK(left != NULL, return NULL);
1218  CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL);
1219 
1220  if ((right == NULL) || !pcmk__str_eq((const char *)left->content,
1221  (const char *)right->content,
1222  pcmk__str_casei)) {
1223  xmlNode *deleted = NULL;
1224 
1225  deleted = add_node_copy(parent, left);
1226  *changed = TRUE;
1227 
1228  return deleted;
1229  }
1230 
1231  return NULL;
1232 }
1233 
1234 xmlNode *
1235 subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
1236  gboolean full, gboolean *changed, const char *marker)
1237 {
1238  gboolean dummy = FALSE;
1239  xmlNode *diff = NULL;
1240  xmlNode *right_child = NULL;
1241  xmlNode *left_child = NULL;
1242  xmlAttrPtr xIter = NULL;
1243 
1244  const char *id = NULL;
1245  const char *name = NULL;
1246  const char *value = NULL;
1247  const char *right_val = NULL;
1248 
1249  if (changed == NULL) {
1250  changed = &dummy;
1251  }
1252 
1253  if (left == NULL) {
1254  return NULL;
1255  }
1256 
1257  if (left->type == XML_COMMENT_NODE) {
1258  return subtract_xml_comment(parent, left, right, changed);
1259  }
1260 
1261  id = ID(left);
1262  name = (const char *) left->name;
1263  if (right == NULL) {
1264  xmlNode *deleted = NULL;
1265 
1266  crm_trace("Processing <%s " XML_ATTR_ID "=%s> (complete copy)",
1267  name, id);
1268  deleted = add_node_copy(parent, left);
1269  crm_xml_add(deleted, XML_DIFF_MARKER, marker);
1270 
1271  *changed = TRUE;
1272  return deleted;
1273  }
1274 
1275  CRM_CHECK(name != NULL, return NULL);
1276  CRM_CHECK(pcmk__xe_is(left, (const char *) right->name), return NULL);
1277 
1278  // Check for XML_DIFF_MARKER in a child
1279  value = crm_element_value(right, XML_DIFF_MARKER);
1280  if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
1281  crm_trace("We are the root of the deletion: %s.id=%s", name, id);
1282  *changed = TRUE;
1283  return NULL;
1284  }
1285 
1286  // @TODO Avoiding creating the full hierarchy would save work here
1287  diff = create_xml_node(parent, name);
1288 
1289  // Changes to child objects
1290  for (left_child = pcmk__xml_first_child(left); left_child != NULL;
1291  left_child = pcmk__xml_next(left_child)) {
1292  gboolean child_changed = FALSE;
1293 
1294  right_child = pcmk__xml_match(right, left_child, false);
1295  subtract_xml_object(diff, left_child, right_child, full, &child_changed,
1296  marker);
1297  if (child_changed) {
1298  *changed = TRUE;
1299  }
1300  }
1301 
1302  if (!*changed) {
1303  /* Nothing to do */
1304 
1305  } else if (full) {
1306  xmlAttrPtr pIter = NULL;
1307 
1308  for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
1309  pIter = pIter->next) {
1310  const char *p_name = (const char *)pIter->name;
1311  const char *p_value = pcmk__xml_attr_value(pIter);
1312 
1313  xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
1314  }
1315 
1316  // We have everything we need
1317  goto done;
1318  }
1319 
1320  // Changes to name/value pairs
1321  for (xIter = pcmk__xe_first_attr(left); xIter != NULL;
1322  xIter = xIter->next) {
1323  const char *prop_name = (const char *) xIter->name;
1324  xmlAttrPtr right_attr = NULL;
1325  xml_node_private_t *nodepriv = NULL;
1326 
1327  if (strcmp(prop_name, XML_ATTR_ID) == 0) {
1328  // id already obtained when present ~ this case, so just reuse
1329  xmlSetProp(diff, (pcmkXmlStr) XML_ATTR_ID, (pcmkXmlStr) id);
1330  continue;
1331  }
1332 
1333  if (pcmk__xa_filterable(prop_name)) {
1334  continue;
1335  }
1336 
1337  right_attr = xmlHasProp(right, (pcmkXmlStr) prop_name);
1338  if (right_attr) {
1339  nodepriv = right_attr->_private;
1340  }
1341 
1342  right_val = crm_element_value(right, prop_name);
1343  if ((right_val == NULL) || (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted))) {
1344  /* new */
1345  *changed = TRUE;
1346  if (full) {
1347  xmlAttrPtr pIter = NULL;
1348 
1349  for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
1350  pIter = pIter->next) {
1351  const char *p_name = (const char *) pIter->name;
1352  const char *p_value = pcmk__xml_attr_value(pIter);
1353 
1354  xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
1355  }
1356  break;
1357 
1358  } else {
1359  const char *left_value = pcmk__xml_attr_value(xIter);
1360 
1361  xmlSetProp(diff, (pcmkXmlStr) prop_name, (pcmkXmlStr) value);
1362  crm_xml_add(diff, prop_name, left_value);
1363  }
1364 
1365  } else {
1366  /* Only now do we need the left value */
1367  const char *left_value = pcmk__xml_attr_value(xIter);
1368 
1369  if (strcmp(left_value, right_val) == 0) {
1370  /* unchanged */
1371 
1372  } else {
1373  *changed = TRUE;
1374  if (full) {
1375  xmlAttrPtr pIter = NULL;
1376 
1377  crm_trace("Changes detected to %s in "
1378  "<%s " XML_ATTR_ID "=%s>", prop_name, name, id);
1379  for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
1380  pIter = pIter->next) {
1381  const char *p_name = (const char *) pIter->name;
1382  const char *p_value = pcmk__xml_attr_value(pIter);
1383 
1384  xmlSetProp(diff, (pcmkXmlStr) p_name,
1385  (pcmkXmlStr) p_value);
1386  }
1387  break;
1388 
1389  } else {
1390  crm_trace("Changes detected to %s (%s -> %s) in "
1391  "<%s " XML_ATTR_ID "=%s>",
1392  prop_name, left_value, right_val, name, id);
1393  crm_xml_add(diff, prop_name, left_value);
1394  }
1395  }
1396  }
1397  }
1398 
1399  if (!*changed) {
1400  free_xml(diff);
1401  return NULL;
1402 
1403  } else if (!full && (id != NULL)) {
1404  crm_xml_add(diff, XML_ATTR_ID, id);
1405  }
1406  done:
1407  return diff;
1408 }
1409 
1410 // Deprecated functions kept only for backward API compatibility
1411 // LCOV_EXCL_START
1412 
1413 #include <crm/common/xml_compat.h>
1414 
1415 gboolean
1416 apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
1417 {
1418  gboolean result = TRUE;
1419  int root_nodes_seen = 0;
1420  const char *digest = crm_element_value(diff, XML_ATTR_DIGEST);
1421  const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION);
1422 
1423  xmlNode *child_diff = NULL;
1424  xmlNode *added = find_xml_node(diff, XML_TAG_DIFF_ADDED, FALSE);
1425  xmlNode *removed = find_xml_node(diff, XML_TAG_DIFF_REMOVED, FALSE);
1426 
1427  CRM_CHECK(new_xml != NULL, return FALSE);
1428 
1429  crm_trace("Subtraction Phase");
1430  for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
1431  child_diff = pcmk__xml_next(child_diff)) {
1432  CRM_CHECK(root_nodes_seen == 0, result = FALSE);
1433  if (root_nodes_seen == 0) {
1434  *new_xml = subtract_xml_object(NULL, old_xml, child_diff, FALSE,
1435  NULL, NULL);
1436  }
1437  root_nodes_seen++;
1438  }
1439 
1440  if (root_nodes_seen == 0) {
1441  *new_xml = copy_xml(old_xml);
1442 
1443  } else if (root_nodes_seen > 1) {
1444  crm_err("(-) Diffs cannot contain more than one change set... saw %d",
1445  root_nodes_seen);
1446  result = FALSE;
1447  }
1448 
1449  root_nodes_seen = 0;
1450  crm_trace("Addition Phase");
1451  if (result) {
1452  xmlNode *child_diff = NULL;
1453 
1454  for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
1455  child_diff = pcmk__xml_next(child_diff)) {
1456  CRM_CHECK(root_nodes_seen == 0, result = FALSE);
1457  if (root_nodes_seen == 0) {
1458  pcmk__xml_update(NULL, *new_xml, child_diff, true);
1459  }
1460  root_nodes_seen++;
1461  }
1462  }
1463 
1464  if (root_nodes_seen > 1) {
1465  crm_err("(+) Diffs cannot contain more than one change set... saw %d",
1466  root_nodes_seen);
1467  result = FALSE;
1468 
1469  } else if (result && (digest != NULL)) {
1470  char *new_digest = NULL;
1471 
1472  purge_diff_markers(*new_xml); // Purge now so diff is ok
1473  new_digest = calculate_xml_versioned_digest(*new_xml, FALSE, TRUE,
1474  version);
1475  if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
1476  crm_info("Digest mis-match: expected %s, calculated %s",
1477  digest, new_digest);
1478  result = FALSE;
1479 
1481  {
1482  save_xml_to_file(old_xml, "diff:original", NULL);
1483  save_xml_to_file(diff, "diff:input", NULL);
1484  save_xml_to_file(*new_xml, "diff:new", NULL);
1485  },
1486  {}
1487  );
1488 
1489  } else {
1490  crm_trace("Digest matched: expected %s, calculated %s",
1491  digest, new_digest);
1492  }
1493  free(new_digest);
1494 
1495  } else if (result) {
1496  purge_diff_markers(*new_xml); // Purge now so diff is ok
1497  }
1498 
1499  return result;
1500 }
1501 
1502 // LCOV_EXCL_STOP
1503 // End deprecated API
#define LOG_TRACE
Definition: logging.h:38
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
#define XML_DIFF_RESULT
Definition: msg_xml.h:469
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:383
#define XML_TAG_DIFF
Definition: msg_xml.h:462
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
Definition: patchset.c:385
#define pcmk__if_tracing(if_action, else_action)
#define XML_ATTR_NUMUPDATES
Definition: msg_xml.h:149
int pcmk_rc2legacy(int rc)
Definition: results.c:546
pcmk_resource_t * dummy
const char * name
Definition: cib.c:26
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
G_GNUC_INTERNAL int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition: xml.c:302
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:349
#define CRM_FEATURE_SET
Definition: crm.h:70
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:384
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
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:393
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:222
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
Deprecated Pacemaker XML API.
#define XML_ATTR_GENERATION
Definition: msg_xml.h:147
#define XML_TAG_DIFF_ADDED
Definition: msg_xml.h:423
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
#define crm_warn(fmt, args...)
Definition: logging.h:382
#define pcmk_err_diff_failed
Definition: results.h:76
G_GNUC_INTERNAL void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml.c:2178
#define XML_DIFF_OP
Definition: msg_xml.h:470
#define crm_debug(fmt, args...)
Definition: logging.h:386
#define XML_DIFF_ATTR
Definition: msg_xml.h:468
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_DIFF_VERSION
Definition: msg_xml.h:463
#define XML_ATTR_ID
Definition: msg_xml.h:156
struct xml_change_obj_s xml_change_obj_t
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:593
#define crm_trace(fmt, args...)
Definition: logging.h:387
#define crm_log_xml_explicit(xml, text)
Definition: logging.h:397
#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.
#define PCMK_XA_FORMAT
Definition: msg_xml.h:51
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:285
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
#define PCMK__NELEM(a)
Definition: internal.h:46
#define XML_DIFF_POSITION
Definition: msg_xml.h:472
void purge_diff_markers(xmlNode *a_node)
Definition: patchset.c:1172
void free_xml(xmlNode *child)
Definition: xml.c:783
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version)
Definition: patchset.c:328
G_GNUC_INTERNAL xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
Definition: xml.c:343
const xmlChar * pcmkXmlStr
Definition: xml.h:50
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
Definition: digest.c:234
#define XML_DIFF_VSOURCE
Definition: msg_xml.h:464
const char * target
Definition: pcmk_fence.c:29
#define XML_TAG_CIB
Definition: msg_xml.h:137
gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
Definition: patchset.c:1416
#define XML_DIFF_CHANGE
Definition: msg_xml.h:466
#define XML_DIFF_PATH
Definition: msg_xml.h:471
#define XML_DIFF_VTARGET
Definition: msg_xml.h:465
#define XML_DIFF_LIST
Definition: msg_xml.h:467
xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
Definition: patchset.c:1186
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:1718
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define XML_DIFF_MARKER
Definition: msg_xml.h:136
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:381
#define CRM_ASSERT(expr)
Definition: results.h:42
#define ENOTUNIQ
Definition: portability.h:81
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1696
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
gboolean can_prune_leaf(xmlNode *xml_node)
Definition: xml.c:2083
void xml_acl_disable(xmlNode *xml)
Definition: acl.c:616
int compare_version(const char *version1, const char *version2)
Definition: utils.c:189
#define crm_log_xml_info(xml, text)
Definition: logging.h:393
#define XML_ATTR_GENERATION_ADMIN
Definition: msg_xml.h:148
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:394
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:140
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
Definition: patchset.c:572
#define pcmk_ok
Definition: results.h:68
GString * pcmk__element_xpath(const xmlNode *xml)
Definition: xpath.c:278
#define pcmk__log_xml_patchset(level, patchset)
#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
xmlNode * subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, gboolean *changed, const char *marker)
Definition: patchset.c:1235
const char * parent
Definition: cib.c:27
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
Definition: patchset.c:1099
G_GNUC_INTERNAL void pcmk__mark_xml_created(xmlNode *xml)
Definition: xml.c:115
G_GNUC_INTERNAL void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
Definition: xml.c:2209
#define crm_info(fmt, args...)
Definition: logging.h:384
uint32_t version
Definition: remote.c:213
#define XML_ATTR_DIGEST
Definition: msg_xml.h:141