pacemaker  2.1.5-b7adf64e51
Scalable High-Availability cluster resource manager
xml.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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/parser.h>
22 #include <libxml/tree.h>
23 #include <libxml/xmlIO.h> /* xmlAllocOutputBuffer */
24 
25 #include <crm/crm.h>
26 #include <crm/msg_xml.h>
27 #include <crm/common/xml.h>
28 #include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc.
29 #include "crmcommon_private.h"
30 
31 // Define this as 1 in development to get insanely verbose trace messages
32 #ifndef XML_PARSER_DEBUG
33 #define XML_PARSER_DEBUG 0
34 #endif
35 
36 
37 /* @TODO XML_PARSE_RECOVER allows some XML errors to be silently worked around
38  * by libxml2, which is potentially ambiguous and dangerous. We should drop it
39  * when we can break backward compatibility with configurations that might be
40  * relying on it (i.e. pacemaker 3.0.0).
41  *
42  * It might be a good idea to have a transitional period where we first try
43  * parsing without XML_PARSE_RECOVER, and if that fails, try parsing again with
44  * it, logging a warning if it succeeds.
45  */
46 #define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER)
47 
48 bool
49 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
50 {
51  if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
52  return FALSE;
53  } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
55  return FALSE;
56  } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
57  pcmk__xf_lazy)) {
58  return FALSE;
59  }
60  return TRUE;
61 }
62 
73 static inline void
74 insert_prefix(int options, GString *buffer, int depth)
75 {
76  int spaces = 0;
77 
78  if (!pcmk_is_set(options, xml_log_option_formatted)) {
79  return;
80  }
81  CRM_ASSERT(buffer != NULL);
82  CRM_CHECK(depth >= 0, depth = 0);
83 
84  /* With -O2, this loop is faster than one g_string_append_printf() call with
85  * width == 2 * depth
86  */
87  spaces = 2 * depth;
88  for (int lpc = 0; lpc < spaces; lpc++) {
89  g_string_append_c(buffer, ' ');
90  }
91 }
92 
93 static inline void
94 set_parent_flag(xmlNode *xml, long flag)
95 {
96  for(; xml; xml = xml->parent) {
97  xml_node_private_t *nodepriv = xml->_private;
98 
99  if (nodepriv == NULL) {
100  /* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
101  } else {
102  pcmk__set_xml_flags(nodepriv, flag);
103  }
104  }
105 }
106 
107 void
109 {
110  if(xml && xml->doc && xml->doc->_private){
111  /* During calls to xmlDocCopyNode(), xml->doc may be unset */
112  xml_doc_private_t *docpriv = xml->doc->_private;
113 
114  pcmk__set_xml_flags(docpriv, flag);
115  }
116 }
117 
118 // Mark document, element, and all element's parents as changed
119 static inline void
120 mark_xml_node_dirty(xmlNode *xml)
121 {
123  set_parent_flag(xml, pcmk__xf_dirty);
124 }
125 
126 // Clear flags on XML node and its children
127 static void
128 reset_xml_node_flags(xmlNode *xml)
129 {
130  xmlNode *cIter = NULL;
131  xml_node_private_t *nodepriv = xml->_private;
132 
133  if (nodepriv) {
134  nodepriv->flags = 0;
135  }
136 
137  for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
138  cIter = pcmk__xml_next(cIter)) {
139  reset_xml_node_flags(cIter);
140  }
141 }
142 
143 // Set xpf_created flag on XML node and any children
144 void
146 {
147  xmlNode *cIter = NULL;
148  xml_node_private_t *nodepriv = xml->_private;
149 
150  if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) {
151  if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
153  mark_xml_node_dirty(xml);
154  }
155  for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
156  cIter = pcmk__xml_next(cIter)) {
157  pcmk__mark_xml_created(cIter);
158  }
159  }
160 }
161 
162 void
164 {
165  xmlNode *parent = a->parent;
166  xml_node_private_t *nodepriv = a->_private;
167 
170  mark_xml_node_dirty(parent);
171 }
172 
173 #define XML_DOC_PRIVATE_MAGIC 0x81726354UL
174 #define XML_NODE_PRIVATE_MAGIC 0x54637281UL
175 
176 // Free an XML object previously marked as deleted
177 static void
178 free_deleted_object(void *data)
179 {
180  if(data) {
181  pcmk__deleted_xml_t *deleted_obj = data;
182 
183  free(deleted_obj->path);
184  free(deleted_obj);
185  }
186 }
187 
188 // Free and NULL user, ACLs, and deleted objects in an XML node's private data
189 static void
190 reset_xml_private_data(xml_doc_private_t *docpriv)
191 {
192  if (docpriv != NULL) {
194 
195  free(docpriv->user);
196  docpriv->user = NULL;
197 
198  if (docpriv->acls != NULL) {
199  pcmk__free_acls(docpriv->acls);
200  docpriv->acls = NULL;
201  }
202 
203  if(docpriv->deleted_objs) {
204  g_list_free_full(docpriv->deleted_objs, free_deleted_object);
205  docpriv->deleted_objs = NULL;
206  }
207  }
208 }
209 
210 // Free all private data associated with an XML node
211 static void
212 free_private_data(xmlNode *node)
213 {
214  /* Note:
215 
216  This function frees private data assosciated with an XML node,
217  unless the function is being called as a result of internal
218  XSLT cleanup.
219 
220  That could happen through, for example, the following chain of
221  function calls:
222 
223  xsltApplyStylesheetInternal
224  -> xsltFreeTransformContext
225  -> xsltFreeRVTs
226  -> xmlFreeDoc
227 
228  And in that case, the node would fulfill three conditions:
229 
230  1. It would be a standalone document (i.e. it wouldn't be
231  part of a document)
232  2. It would have a space-prefixed name (for reference, please
233  see xsltInternals.h: XSLT_MARK_RES_TREE_FRAG)
234  3. It would carry its own payload in the _private field.
235 
236  We do not free data in this circumstance to avoid a failed
237  assertion on the XML_*_PRIVATE_MAGIC later.
238 
239  */
240  if (node->name == NULL || node->name[0] != ' ') {
241  if (node->_private) {
242  if (node->type == XML_DOCUMENT_NODE) {
243  reset_xml_private_data(node->_private);
244  } else {
245  CRM_ASSERT(((xml_node_private_t *) node->_private)->check
247  /* nothing dynamically allocated nested */
248  }
249  free(node->_private);
250  node->_private = NULL;
251  }
252  }
253 }
254 
255 // Allocate and initialize private data for an XML node
256 static void
257 new_private_data(xmlNode *node)
258 {
259  switch (node->type) {
260  case XML_DOCUMENT_NODE: {
261  xml_doc_private_t *docpriv = NULL;
262  docpriv = calloc(1, sizeof(xml_doc_private_t));
263  CRM_ASSERT(docpriv != NULL);
264  docpriv->check = XML_DOC_PRIVATE_MAGIC;
265  /* Flags will be reset if necessary when tracking is enabled */
267  node->_private = docpriv;
268  break;
269  }
270  case XML_ELEMENT_NODE:
271  case XML_ATTRIBUTE_NODE:
272  case XML_COMMENT_NODE: {
273  xml_node_private_t *nodepriv = NULL;
274  nodepriv = calloc(1, sizeof(xml_node_private_t));
275  CRM_ASSERT(nodepriv != NULL);
276  nodepriv->check = XML_NODE_PRIVATE_MAGIC;
277  /* Flags will be reset if necessary when tracking is enabled */
279  node->_private = nodepriv;
280  if (pcmk__tracking_xml_changes(node, FALSE)) {
281  /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
282  * not hooked up at the point we are called
283  */
284  mark_xml_node_dirty(node);
285  }
286  break;
287  }
288  case XML_TEXT_NODE:
289  case XML_DTD_NODE:
290  case XML_CDATA_SECTION_NODE:
291  break;
292  default:
293  /* Ignore */
294  crm_trace("Ignoring %p %d", node, node->type);
295  CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
296  break;
297  }
298 }
299 
300 void
301 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
302 {
303  xml_accept_changes(xml);
304  crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
306  if(enforce_acls) {
307  if(acl_source == NULL) {
308  acl_source = xml;
309  }
311  pcmk__unpack_acl(acl_source, xml, user);
312  pcmk__apply_acl(xml);
313  }
314 }
315 
316 bool xml_tracking_changes(xmlNode * xml)
317 {
318  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
319  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
321 }
322 
323 bool xml_document_dirty(xmlNode *xml)
324 {
325  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
326  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
328 }
329 
339 int
340 pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
341 {
342  int position = 0;
343 
344  for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
345  xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
346 
347  if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
348  position++;
349  }
350  }
351 
352  return position;
353 }
354 
355 // This also clears attribute's flags if not marked as deleted
356 static bool
357 marked_as_deleted(xmlAttrPtr a, void *user_data)
358 {
359  xml_node_private_t *nodepriv = a->_private;
360 
361  if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
362  return true;
363  }
364  nodepriv->flags = pcmk__xf_none;
365  return false;
366 }
367 
368 // Remove all attributes marked as deleted from an XML node
369 static void
370 accept_attr_deletions(xmlNode *xml)
371 {
372  // Clear XML node's flags
373  ((xml_node_private_t *) xml->_private)->flags = pcmk__xf_none;
374 
375  // Remove this XML node's attributes that were marked as deleted
376  pcmk__xe_remove_matching_attrs(xml, marked_as_deleted, NULL);
377 
378  // Recursively do the same for this XML node's children
379  for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
380  cIter = pcmk__xml_next(cIter)) {
381  accept_attr_deletions(cIter);
382  }
383 }
384 
393 xmlNode *
394 pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
395 {
396  CRM_CHECK(needle != NULL, return NULL);
397 
398  if (needle->type == XML_COMMENT_NODE) {
399  return pcmk__xc_match(haystack, needle, exact);
400 
401  } else {
402  const char *id = ID(needle);
403  const char *attr = (id == NULL)? NULL : XML_ATTR_ID;
404 
405  return pcmk__xe_match(haystack, crm_element_name(needle), attr, id);
406  }
407 }
408 
409 void
410 xml_log_changes(uint8_t log_level, const char *function, xmlNode * xml)
411 {
412  GList *gIter = NULL;
413  xml_doc_private_t *docpriv = NULL;
414 
415  if (log_level == LOG_NEVER) {
416  return;
417  }
418 
419  CRM_ASSERT(xml);
420  CRM_ASSERT(xml->doc);
421 
422  docpriv = xml->doc->_private;
423  if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
424  return;
425  }
426 
427  for(gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
428  pcmk__deleted_xml_t *deleted_obj = gIter->data;
429 
430  if (deleted_obj->position >= 0) {
431  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)",
432  deleted_obj->path, deleted_obj->position);
433 
434  } else {
435  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s",
436  deleted_obj->path);
437  }
438  }
439 
440  log_data_element(log_level, __FILE__, function, __LINE__, "+ ", xml, 0,
442 }
443 
444 void
445 xml_accept_changes(xmlNode * xml)
446 {
447  xmlNode *top = NULL;
448  xml_doc_private_t *docpriv = NULL;
449 
450  if(xml == NULL) {
451  return;
452  }
453 
454  crm_trace("Accepting changes to %p", xml);
455  docpriv = xml->doc->_private;
456  top = xmlDocGetRootElement(xml->doc);
457 
458  reset_xml_private_data(xml->doc->_private);
459 
460  if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
461  docpriv->flags = pcmk__xf_none;
462  return;
463  }
464 
465  docpriv->flags = pcmk__xf_none;
466  accept_attr_deletions(top);
467 }
468 
469 xmlNode *
470 find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
471 {
472  xmlNode *a_child = NULL;
473  const char *name = "NULL";
474 
475  if (root != NULL) {
476  name = crm_element_name(root);
477  }
478 
479  if (search_path == NULL) {
480  crm_warn("Will never find <NULL>");
481  return NULL;
482  }
483 
484  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
485  a_child = pcmk__xml_next(a_child)) {
486  if (strcmp((const char *)a_child->name, search_path) == 0) {
487 /* crm_trace("returning node (%s).", crm_element_name(a_child)); */
488  return a_child;
489  }
490  }
491 
492  if (must_find) {
493  crm_warn("Could not find %s in %s.", search_path, name);
494  } else if (root != NULL) {
495  crm_trace("Could not find %s in %s.", search_path, name);
496  } else {
497  crm_trace("Could not find %s in <NULL>.", search_path);
498  }
499 
500  return NULL;
501 }
502 
503 #define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \
504  (v), pcmk__str_none)
505 
519 xmlNode *
520 pcmk__xe_match(const xmlNode *parent, const char *node_name,
521  const char *attr_n, const char *attr_v)
522 {
523  /* ensure attr_v specified when attr_n is */
524  CRM_CHECK(attr_n == NULL || attr_v != NULL, return NULL);
525 
526  for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
527  child = pcmk__xml_next(child)) {
528  if (pcmk__str_eq(node_name, (const char *) (child->name),
530  && ((attr_n == NULL) || attr_matches(child, attr_n, attr_v))) {
531  return child;
532  }
533  }
534  crm_trace("XML child node <%s%s%s%s%s> not found in %s",
535  (node_name? node_name : "(any)"),
536  (attr_n? " " : ""),
537  (attr_n? attr_n : ""),
538  (attr_n? "=" : ""),
539  (attr_n? attr_v : ""),
540  crm_element_name(parent));
541  return NULL;
542 }
543 
544 void
545 copy_in_properties(xmlNode * target, xmlNode * src)
546 {
547  if (src == NULL) {
548  crm_warn("No node to copy properties from");
549 
550  } else if (target == NULL) {
551  crm_err("No node to copy properties into");
552 
553  } else {
554  for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
555  const char *p_name = (const char *) a->name;
556  const char *p_value = pcmk__xml_attr_value(a);
557 
558  expand_plus_plus(target, p_name, p_value);
559  if (xml_acl_denied(target)) {
560  crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name);
561  return;
562  }
563  }
564  }
565 
566  return;
567 }
568 
577 void
579 {
580  /* TODO: Remove recursion and use xpath searches for value++ */
581  xmlNode *child = NULL;
582 
583  for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
584  const char *p_name = (const char *) a->name;
585  const char *p_value = pcmk__xml_attr_value(a);
586 
587  expand_plus_plus(target, p_name, p_value);
588  }
589  for (child = pcmk__xml_first_child(target); child != NULL;
590  child = pcmk__xml_next(child)) {
592  }
593 }
594 
611 void
612 expand_plus_plus(xmlNode * target, const char *name, const char *value)
613 {
614  int offset = 1;
615  int name_len = 0;
616  int int_value = 0;
617  int value_len = 0;
618 
619  const char *old_value = NULL;
620 
621  if (target == NULL || value == NULL || name == NULL) {
622  return;
623  }
624 
625  old_value = crm_element_value(target, name);
626 
627  if (old_value == NULL) {
628  /* if no previous value, set unexpanded */
629  goto set_unexpanded;
630 
631  } else if (strstr(value, name) != value) {
632  goto set_unexpanded;
633  }
634 
635  name_len = strlen(name);
636  value_len = strlen(value);
637  if (value_len < (name_len + 2)
638  || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
639  goto set_unexpanded;
640  }
641 
642  /* if we are expanding ourselves,
643  * then no previous value was set and leave int_value as 0
644  */
645  if (old_value != value) {
646  int_value = char2score(old_value);
647  }
648 
649  if (value[name_len + 1] != '+') {
650  const char *offset_s = value + (name_len + 2);
651 
652  offset = char2score(offset_s);
653  }
654  int_value += offset;
655 
656  if (int_value > INFINITY) {
657  int_value = (int)INFINITY;
658  }
659 
660  crm_xml_add_int(target, name, int_value);
661  return;
662 
663  set_unexpanded:
664  if (old_value == value) {
665  /* the old value is already set, nothing to do */
666  return;
667  }
668  crm_xml_add(target, name, value);
669  return;
670 }
671 
681 void
683  bool (*match)(xmlAttrPtr, void *),
684  void *user_data)
685 {
686  xmlAttrPtr next = NULL;
687 
688  for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
689  next = a->next; // Grab now because attribute might get removed
690  if ((match == NULL) || match(a, user_data)) {
691  if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
692  crm_trace("ACLs prevent removal of attributes (%s and "
693  "possibly others) from %s element",
694  (const char *) a->name, (const char *) element->name);
695  return; // ACLs apply to element, not particular attributes
696  }
697 
698  if (pcmk__tracking_xml_changes(element, false)) {
699  // Leave (marked for removal) until after diff is calculated
700  set_parent_flag(element, pcmk__xf_dirty);
701  pcmk__set_xml_flags((xml_node_private_t *) a->_private,
703  } else {
704  xmlRemoveProp(a);
705  }
706  }
707  }
708 }
709 
710 xmlDoc *
711 getDocPtr(xmlNode * node)
712 {
713  xmlDoc *doc = NULL;
714 
715  CRM_CHECK(node != NULL, return NULL);
716 
717  doc = node->doc;
718  if (doc == NULL) {
719  doc = xmlNewDoc((pcmkXmlStr) "1.0");
720  xmlDocSetRootElement(doc, node);
721  xmlSetTreeDoc(node, doc);
722  }
723  return doc;
724 }
725 
726 xmlNode *
727 add_node_copy(xmlNode * parent, xmlNode * src_node)
728 {
729  xmlNode *child = NULL;
730  xmlDoc *doc = getDocPtr(parent);
731 
732  CRM_CHECK(src_node != NULL, return NULL);
733 
734  child = xmlDocCopyNode(src_node, doc, 1);
735  xmlAddChild(parent, child);
736  pcmk__mark_xml_created(child);
737  return child;
738 }
739 
740 int
741 add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child)
742 {
743  add_node_copy(parent, child);
744  free_xml(child);
745  return 1;
746 }
747 
748 xmlNode *
749 create_xml_node(xmlNode * parent, const char *name)
750 {
751  xmlDoc *doc = NULL;
752  xmlNode *node = NULL;
753 
754  if (pcmk__str_empty(name)) {
755  CRM_CHECK(name != NULL && name[0] == 0, return NULL);
756  return NULL;
757  }
758 
759  if (parent == NULL) {
760  doc = xmlNewDoc((pcmkXmlStr) "1.0");
761  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
762  xmlDocSetRootElement(doc, node);
763 
764  } else {
765  doc = getDocPtr(parent);
766  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
767  xmlAddChild(parent, node);
768  }
770  return node;
771 }
772 
773 xmlNode *
774 pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content)
775 {
776  xmlNode *node = create_xml_node(parent, name);
777 
778  if (node != NULL) {
779  xmlNodeSetContent(node, (pcmkXmlStr) content);
780  }
781 
782  return node;
783 }
784 
785 xmlNode *
786 pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id,
787  const char *class_name, const char *text)
788 {
789  xmlNode *node = pcmk_create_xml_text_node(parent, element_name, text);
790 
791  if (class_name != NULL) {
792  crm_xml_add(node, "class", class_name);
793  }
794 
795  if (id != NULL) {
796  crm_xml_add(node, "id", id);
797  }
798 
799  return node;
800 }
801 
807 void
809 {
810  xmlUnlinkNode(xml); // Detaches from parent and siblings
811  xmlFreeNode(xml); // Frees
812 }
813 
814 static void
815 free_xml_with_position(xmlNode * child, int position)
816 {
817  if (child != NULL) {
818  xmlNode *top = NULL;
819  xmlDoc *doc = child->doc;
820  xml_node_private_t *nodepriv = child->_private;
821  xml_doc_private_t *docpriv = NULL;
822 
823  if (doc != NULL) {
824  top = xmlDocGetRootElement(doc);
825  }
826 
827  if (doc != NULL && top == child) {
828  /* Free everything */
829  xmlFreeDoc(doc);
830 
831  } else if (pcmk__check_acl(child, NULL, pcmk__xf_acl_write) == FALSE) {
832  GString *xpath = NULL;
833 
834  pcmk__log_else(LOG_TRACE, return);
835  xpath = pcmk__element_xpath(child);
836  qb_log_from_external_source(__func__, __FILE__,
837  "Cannot remove %s %x", LOG_TRACE,
838  __LINE__, 0, (const char *) xpath->str,
839  nodepriv->flags);
840  g_string_free(xpath, TRUE);
841  return;
842 
843  } else {
844  if (doc && pcmk__tracking_xml_changes(child, FALSE)
845  && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
846 
847  GString *xpath = pcmk__element_xpath(child);
848 
849  if (xpath != NULL) {
850  pcmk__deleted_xml_t *deleted_obj = NULL;
851 
852  crm_trace("Deleting %s %p from %p",
853  (const char *) xpath->str, child, doc);
854 
855  deleted_obj = calloc(1, sizeof(pcmk__deleted_xml_t));
856  deleted_obj->path = strdup((const char *) xpath->str);
857 
858  CRM_ASSERT(deleted_obj->path != NULL);
859  g_string_free(xpath, TRUE);
860 
861  deleted_obj->position = -1;
862  /* Record the "position" only for XML comments for now */
863  if (child->type == XML_COMMENT_NODE) {
864  if (position >= 0) {
865  deleted_obj->position = position;
866 
867  } else {
868  deleted_obj->position = pcmk__xml_position(child,
869  pcmk__xf_skip);
870  }
871  }
872 
873  docpriv = doc->_private;
874  docpriv->deleted_objs = g_list_append(docpriv->deleted_objs, deleted_obj);
876  }
877  }
878  pcmk_free_xml_subtree(child);
879  }
880  }
881 }
882 
883 
884 void
885 free_xml(xmlNode * child)
886 {
887  free_xml_with_position(child, -1);
888 }
889 
890 xmlNode *
891 copy_xml(xmlNode * src)
892 {
893  xmlDoc *doc = xmlNewDoc((pcmkXmlStr) "1.0");
894  xmlNode *copy = xmlDocCopyNode(src, doc, 1);
895 
896  xmlDocSetRootElement(doc, copy);
897  xmlSetTreeDoc(copy, doc);
898  return copy;
899 }
900 
901 static void
902 log_xmllib_err(void *ctx, const char *fmt, ...)
903 G_GNUC_PRINTF(2, 3);
904 
905 // Log an XML library error
906 static void
907 log_xmllib_err(void *ctx, const char *fmt, ...)
908 {
909  va_list ap;
910  static struct qb_log_callsite *xml_error_cs = NULL;
911 
912  if (xml_error_cs == NULL) {
913  xml_error_cs = qb_log_callsite_get(
914  __func__, __FILE__, "xml library error", LOG_TRACE, __LINE__, crm_trace_nonlog);
915  }
916 
917  va_start(ap, fmt);
918  if (xml_error_cs && xml_error_cs->targets) {
919  PCMK__XML_LOG_BASE(LOG_ERR, TRUE,
920  crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, "xml library error",
921  TRUE, TRUE),
922  "XML Error: ", fmt, ap);
923  } else {
924  PCMK__XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
925  }
926  va_end(ap);
927 }
928 
929 xmlNode *
930 string2xml(const char *input)
931 {
932  xmlNode *xml = NULL;
933  xmlDocPtr output = NULL;
934  xmlParserCtxtPtr ctxt = NULL;
935  xmlErrorPtr last_error = NULL;
936 
937  if (input == NULL) {
938  crm_err("Can't parse NULL input");
939  return NULL;
940  }
941 
942  /* create a parser context */
943  ctxt = xmlNewParserCtxt();
944  CRM_CHECK(ctxt != NULL, return NULL);
945 
946  xmlCtxtResetLastError(ctxt);
947  xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
948  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
950  if (output) {
951  xml = xmlDocGetRootElement(output);
952  }
953  last_error = xmlCtxtGetLastError(ctxt);
954  if (last_error && last_error->code != XML_ERR_OK) {
955  /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
956  /*
957  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
958  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
959  */
960  crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
961  last_error->domain, last_error->level, last_error->code, last_error->message);
962 
963  if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
964  CRM_LOG_ASSERT("Cannot parse an empty string");
965 
966  } else if (last_error->code != XML_ERR_DOCUMENT_END) {
967  crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
968  input);
969  if (xml != NULL) {
970  crm_log_xml_err(xml, "Partial");
971  }
972 
973  } else {
974  int len = strlen(input);
975  int lpc = 0;
976 
977  while(lpc < len) {
978  crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
979  lpc += 80;
980  }
981 
982  CRM_LOG_ASSERT("String parsing error");
983  }
984  }
985 
986  xmlFreeParserCtxt(ctxt);
987  return xml;
988 }
989 
990 xmlNode *
992 {
993  size_t data_length = 0;
994  size_t read_chars = 0;
995 
996  char *xml_buffer = NULL;
997  xmlNode *xml_obj = NULL;
998 
999  do {
1000  xml_buffer = pcmk__realloc(xml_buffer, data_length + PCMK__BUFFER_SIZE);
1001  read_chars = fread(xml_buffer + data_length, 1, PCMK__BUFFER_SIZE,
1002  stdin);
1003  data_length += read_chars;
1004  } while (read_chars == PCMK__BUFFER_SIZE);
1005 
1006  if (data_length == 0) {
1007  crm_warn("No XML supplied on stdin");
1008  free(xml_buffer);
1009  return NULL;
1010  }
1011 
1012  xml_buffer[data_length] = '\0';
1013  xml_obj = string2xml(xml_buffer);
1014  free(xml_buffer);
1015 
1016  crm_log_xml_trace(xml_obj, "Created fragment");
1017  return xml_obj;
1018 }
1019 
1020 static char *
1021 decompress_file(const char *filename)
1022 {
1023  char *buffer = NULL;
1024  int rc = 0;
1025  size_t length = 0, read_len = 0;
1026  BZFILE *bz_file = NULL;
1027  FILE *input = fopen(filename, "r");
1028 
1029  if (input == NULL) {
1030  crm_perror(LOG_ERR, "Could not open %s for reading", filename);
1031  return NULL;
1032  }
1033 
1034  bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
1035  if (rc != BZ_OK) {
1036  crm_err("Could not prepare to read compressed %s: %s "
1037  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1038  BZ2_bzReadClose(&rc, bz_file);
1039  fclose(input);
1040  return NULL;
1041  }
1042 
1043  rc = BZ_OK;
1044  // cppcheck seems not to understand the abort-logic in pcmk__realloc
1045  // cppcheck-suppress memleak
1046  while (rc == BZ_OK) {
1047  buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1);
1048  read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
1049 
1050  crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
1051 
1052  if (rc == BZ_OK || rc == BZ_STREAM_END) {
1053  length += read_len;
1054  }
1055  }
1056 
1057  buffer[length] = '\0';
1058 
1059  if (rc != BZ_STREAM_END) {
1060  crm_err("Could not read compressed %s: %s "
1061  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1062  free(buffer);
1063  buffer = NULL;
1064  }
1065 
1066  BZ2_bzReadClose(&rc, bz_file);
1067  fclose(input);
1068  return buffer;
1069 }
1070 
1077 void
1079 {
1080  xmlNode *iter = xml->children;
1081 
1082  while (iter) {
1083  xmlNode *next = iter->next;
1084 
1085  switch (iter->type) {
1086  case XML_TEXT_NODE:
1087  /* Remove it */
1088  pcmk_free_xml_subtree(iter);
1089  break;
1090 
1091  case XML_ELEMENT_NODE:
1092  /* Search it */
1093  pcmk__strip_xml_text(iter);
1094  break;
1095 
1096  default:
1097  /* Leave it */
1098  break;
1099  }
1100 
1101  iter = next;
1102  }
1103 }
1104 
1105 xmlNode *
1106 filename2xml(const char *filename)
1107 {
1108  xmlNode *xml = NULL;
1109  xmlDocPtr output = NULL;
1110  bool uncompressed = true;
1111  xmlParserCtxtPtr ctxt = NULL;
1112  xmlErrorPtr last_error = NULL;
1113 
1114  /* create a parser context */
1115  ctxt = xmlNewParserCtxt();
1116  CRM_CHECK(ctxt != NULL, return NULL);
1117 
1118  xmlCtxtResetLastError(ctxt);
1119  xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1120 
1121  if (filename) {
1122  uncompressed = !pcmk__ends_with_ext(filename, ".bz2");
1123  }
1124 
1125  if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
1126  /* STDIN_FILENO == fileno(stdin) */
1127  output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
1129 
1130  } else if (uncompressed) {
1131  output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
1132 
1133  } else {
1134  char *input = decompress_file(filename);
1135 
1136  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
1138  free(input);
1139  }
1140 
1141  if (output && (xml = xmlDocGetRootElement(output))) {
1142  pcmk__strip_xml_text(xml);
1143  }
1144 
1145  last_error = xmlCtxtGetLastError(ctxt);
1146  if (last_error && last_error->code != XML_ERR_OK) {
1147  /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
1148  /*
1149  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
1150  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
1151  */
1152  crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
1153  last_error->domain, last_error->level, last_error->code, last_error->message);
1154 
1155  if (last_error && last_error->code != XML_ERR_OK) {
1156  crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
1157  if (xml != NULL) {
1158  crm_log_xml_err(xml, "Partial");
1159  }
1160  }
1161  }
1162 
1163  xmlFreeParserCtxt(ctxt);
1164  return xml;
1165 }
1166 
1175 const char *
1177 {
1178  const char *now_str = pcmk__epoch2str(NULL);
1179 
1180  return crm_xml_add(xe, XML_CIB_ATTR_WRITTEN,
1181  now_str ? now_str : "Could not determine current time");
1182 }
1183 
1189 void
1191 {
1192  char *c;
1193 
1194  for (c = id; *c; ++c) {
1195  /* @TODO Sanitize more comprehensively */
1196  switch (*c) {
1197  case ':':
1198  case '#':
1199  *c = '.';
1200  }
1201  }
1202 }
1203 
1211 void
1212 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1213 {
1214  va_list ap;
1215  int len = 0;
1216  char *id = NULL;
1217 
1218  /* equivalent to crm_strdup_printf() */
1219  va_start(ap, format);
1220  len = vasprintf(&id, format, ap);
1221  va_end(ap);
1222  CRM_ASSERT(len > 0);
1223 
1224  crm_xml_sanitize_id(id);
1225  crm_xml_add(xml, XML_ATTR_ID, id);
1226  free(id);
1227 }
1228 
1241 static int
1242 write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
1243  bool compress, unsigned int *nbytes)
1244 {
1245  int rc = pcmk_rc_ok;
1246  char *buffer = NULL;
1247 
1248  *nbytes = 0;
1249  crm_log_xml_trace(xml_node, "writing");
1250 
1251  buffer = dump_xml_formatted(xml_node);
1252  CRM_CHECK(buffer && strlen(buffer),
1253  crm_log_xml_warn(xml_node, "formatting failed");
1254  rc = pcmk_rc_error;
1255  goto bail);
1256 
1257  if (compress) {
1258  unsigned int in = 0;
1259  BZFILE *bz_file = NULL;
1260 
1261  rc = BZ_OK;
1262  bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1263  if (rc != BZ_OK) {
1264  crm_warn("Not compressing %s: could not prepare file stream: %s "
1265  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1266  } else {
1267  BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1268  if (rc != BZ_OK) {
1269  crm_warn("Not compressing %s: could not compress data: %s "
1270  CRM_XS " bzerror=%d errno=%d",
1271  filename, bz2_strerror(rc), rc, errno);
1272  }
1273  }
1274 
1275  if (rc == BZ_OK) {
1276  BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1277  if (rc != BZ_OK) {
1278  crm_warn("Not compressing %s: could not write compressed data: %s "
1279  CRM_XS " bzerror=%d errno=%d",
1280  filename, bz2_strerror(rc), rc, errno);
1281  *nbytes = 0; // retry without compression
1282  } else {
1283  crm_trace("Compressed XML for %s from %u bytes to %u",
1284  filename, in, *nbytes);
1285  }
1286  }
1287  rc = pcmk_rc_ok; // Either true, or we'll retry without compression
1288  }
1289 
1290  if (*nbytes == 0) {
1291  rc = fprintf(stream, "%s", buffer);
1292  if (rc < 0) {
1293  rc = errno;
1294  crm_perror(LOG_ERR, "writing %s", filename);
1295  } else {
1296  *nbytes = (unsigned int) rc;
1297  rc = pcmk_rc_ok;
1298  }
1299  }
1300 
1301  bail:
1302 
1303  if (fflush(stream) != 0) {
1304  rc = errno;
1305  crm_perror(LOG_ERR, "flushing %s", filename);
1306  }
1307 
1308  /* Don't report error if the file does not support synchronization */
1309  if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1310  rc = errno;
1311  crm_perror(LOG_ERR, "synchronizing %s", filename);
1312  }
1313 
1314  fclose(stream);
1315 
1316  crm_trace("Saved %d bytes to %s as XML", *nbytes, filename);
1317  free(buffer);
1318 
1319  return rc;
1320 }
1321 
1332 int
1333 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
1334 {
1335  FILE *stream = NULL;
1336  unsigned int nbytes = 0;
1337  int rc = pcmk_rc_ok;
1338 
1339  CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
1340  stream = fdopen(fd, "w");
1341  if (stream == NULL) {
1342  return -errno;
1343  }
1344  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1345  if (rc != pcmk_rc_ok) {
1346  return pcmk_rc2legacy(rc);
1347  }
1348  return (int) nbytes;
1349 }
1350 
1360 int
1361 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
1362 {
1363  FILE *stream = NULL;
1364  unsigned int nbytes = 0;
1365  int rc = pcmk_rc_ok;
1366 
1367  CRM_CHECK(xml_node && filename, return -EINVAL);
1368  stream = fopen(filename, "w");
1369  if (stream == NULL) {
1370  return -errno;
1371  }
1372  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1373  if (rc != pcmk_rc_ok) {
1374  return pcmk_rc2legacy(rc);
1375  }
1376  return (int) nbytes;
1377 }
1378 
1379 // Replace a portion of a dynamically allocated string (reallocating memory)
1380 static char *
1381 replace_text(char *text, int start, size_t *length, const char *replace)
1382 {
1383  size_t offset = strlen(replace) - 1; // We have space for 1 char already
1384 
1385  *length += offset;
1386  text = pcmk__realloc(text, *length);
1387 
1388  for (size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1389  text[lpc] = text[lpc - offset];
1390  }
1391 
1392  memcpy(text + start, replace, offset + 1);
1393  return text;
1394 }
1395 
1405 char *
1406 crm_xml_escape(const char *text)
1407 {
1408  size_t length;
1409  char *copy;
1410 
1411  /*
1412  * When xmlCtxtReadDoc() parses &lt; and friends in a
1413  * value, it converts them to their human readable
1414  * form.
1415  *
1416  * If one uses xmlNodeDump() to convert it back to a
1417  * string, all is well, because special characters are
1418  * converted back to their escape sequences.
1419  *
1420  * However xmlNodeDump() is randomly dog slow, even with the same
1421  * input. So we need to replicate the escaping in our custom
1422  * version so that the result can be re-parsed by xmlCtxtReadDoc()
1423  * when necessary.
1424  */
1425 
1426  if (text == NULL) {
1427  return NULL;
1428  }
1429 
1430  length = 1 + strlen(text);
1431  copy = strdup(text);
1432  CRM_ASSERT(copy != NULL);
1433  for (size_t index = 0; index < length; index++) {
1434  if(copy[index] & 0x80 && copy[index+1] & 0x80){
1435  index++;
1436  break;
1437  }
1438  switch (copy[index]) {
1439  case 0:
1440  break;
1441  case '<':
1442  copy = replace_text(copy, index, &length, "&lt;");
1443  break;
1444  case '>':
1445  copy = replace_text(copy, index, &length, "&gt;");
1446  break;
1447  case '"':
1448  copy = replace_text(copy, index, &length, "&quot;");
1449  break;
1450  case '\'':
1451  copy = replace_text(copy, index, &length, "&apos;");
1452  break;
1453  case '&':
1454  copy = replace_text(copy, index, &length, "&amp;");
1455  break;
1456  case '\t':
1457  /* Might as well just expand to a few spaces... */
1458  copy = replace_text(copy, index, &length, " ");
1459  break;
1460  case '\n':
1461  copy = replace_text(copy, index, &length, "\\n");
1462  break;
1463  case '\r':
1464  copy = replace_text(copy, index, &length, "\\r");
1465  break;
1466  default:
1467  /* Check for and replace non-printing characters with their octal equivalent */
1468  if(copy[index] < ' ' || copy[index] > '~') {
1469  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
1470 
1471  copy = replace_text(copy, index, &length, replace);
1472  free(replace);
1473  }
1474  }
1475  }
1476  return copy;
1477 }
1478 
1479 /* Keep this inline. De-inlining resulted in a 0.6% average slowdown in
1480  * crm_simulate on cts/scheduler/xml during testing.
1481  */
1482 
1491 static inline void
1492 dump_xml_attr(const xmlAttr *attr, int options, GString *buffer)
1493 {
1494  char *p_value = NULL;
1495  const char *p_name = NULL;
1496  xml_node_private_t *nodepriv = NULL;
1497 
1498  CRM_ASSERT(buffer != NULL);
1499  if (attr == NULL || attr->children == NULL) {
1500  return;
1501  }
1502 
1503  nodepriv = attr->_private;
1504  if (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1505  return;
1506  }
1507 
1508  p_name = (const char *) attr->name;
1509  p_value = crm_xml_escape((const char *)attr->children->content);
1510  pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"",
1511  NULL);
1512 
1513  free(p_value);
1514 }
1515 
1516 // Log an XML element or comment (and any children) in a formatted way
1517 static void
1518 log_xml_node_and_children(GString *buffer, int log_level, const char *file,
1519  const char *function, int line, const char *prefix,
1520  const xmlNode *data, int depth, int options)
1521 {
1522  const char *name = NULL;
1523  const char *hidden = NULL;
1524 
1525  xmlNode *child = NULL;
1526 
1527  CRM_ASSERT(buffer != NULL);
1528 
1529  if ((data == NULL) || (log_level == LOG_NEVER)
1530  || ((data->type != XML_COMMENT_NODE)
1531  && (data->type != XML_ELEMENT_NODE))) {
1532  return;
1533  }
1534 
1535  name = crm_element_name(data);
1536 
1537  g_string_truncate(buffer, 0);
1538 
1539  if (pcmk_is_set(options, xml_log_option_open)) {
1540  insert_prefix(options, buffer, depth);
1541 
1542  if (data->type == XML_COMMENT_NODE) {
1543  pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->",
1544  NULL);
1545 
1546  } else {
1547  pcmk__g_strcat(buffer, "<", name, NULL);
1548 
1549  hidden = crm_element_value(data, "hidden");
1550  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL;
1551  a = a->next) {
1552 
1553  xml_node_private_t *nodepriv = a->_private;
1554  const char *p_name = (const char *) a->name;
1555  const char *p_value = pcmk__xml_attr_value(a);
1556  char *p_copy = NULL;
1557 
1558  if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1559  continue;
1560  } else if (pcmk_any_flags_set(options,
1563  && (strcmp(XML_DIFF_MARKER, p_name) == 0)) {
1564  continue;
1565 
1566  } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1567  p_copy = strdup("*****");
1568 
1569  } else {
1570  p_copy = crm_xml_escape(p_value);
1571  }
1572 
1573  pcmk__g_strcat(buffer, " ", p_name, "=\"",
1574  pcmk__s(p_copy, "<null>"), "\"", NULL);
1575  free(p_copy);
1576  }
1577 
1578  if(xml_has_children(data) == FALSE) {
1579  g_string_append(buffer, "/>");
1580 
1581  } else if (pcmk_is_set(options, xml_log_option_children)) {
1582  g_string_append_c(buffer, '>');
1583 
1584  } else {
1585  g_string_append(buffer, "/>");
1586  }
1587  }
1588 
1589  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix,
1590  (const char *) buffer->str);
1591  }
1592 
1593  if(data->type == XML_COMMENT_NODE) {
1594  return;
1595 
1596  } else if(xml_has_children(data) == FALSE) {
1597  return;
1598 
1599  } else if (pcmk_is_set(options, xml_log_option_children)) {
1600  for (child = pcmk__xml_first_child(data); child != NULL;
1601  child = pcmk__xml_next(child)) {
1602 
1603  log_xml_node_and_children(buffer, log_level, file, function, line,
1604  prefix, child, depth + 1,
1605  options|xml_log_option_open
1607  }
1608  }
1609 
1610  if (pcmk_is_set(options, xml_log_option_close)) {
1611  g_string_truncate(buffer, 0);
1612 
1613  insert_prefix(options, buffer, depth);
1614  pcmk__g_strcat(buffer, "</", name, ">", NULL);
1615 
1616  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix,
1617  (const char *) buffer->str);
1618  }
1619 }
1620 
1621 // Log an XML element or comment (and any children) in a formatted way
1622 void
1623 pcmk__xml_log(int log_level, const char *file, const char *function, int line,
1624  const char *prefix, const xmlNode *data, int depth, int options)
1625 {
1626  /* Allocate a buffer once, for log_xml_node_and_children() to truncate and
1627  * reuse in recursive calls
1628  */
1629  GString *buffer = g_string_sized_new(1024);
1630 
1631  log_xml_node_and_children(buffer, log_level, file, function, line, prefix,
1632  data, depth, options);
1633 
1634  g_string_free(buffer, TRUE);
1635 }
1636 
1637 // Log XML portions that have been marked as changed
1638 static void
1639 log_xml_changes(int log_level, const char *file, const char *function, int line,
1640  const char *prefix, const xmlNode *data, int depth, int options)
1641 {
1642  xml_node_private_t *nodepriv;
1643  char *prefix_m = NULL;
1644  xmlNode *child = NULL;
1645 
1646  if ((data == NULL) || (log_level == LOG_NEVER)) {
1647  return;
1648  }
1649 
1650  nodepriv = data->_private;
1651 
1652  prefix_m = strdup(prefix);
1653  prefix_m[1] = '+';
1654 
1655  if (pcmk_all_flags_set(nodepriv->flags, pcmk__xf_dirty|pcmk__xf_created)) {
1656  /* Continue and log full subtree */
1657  pcmk__xml_log(log_level, file, function, line, prefix_m, data, depth,
1660 
1661  } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
1662  int spaces = 0;
1663  char *prefix_del = NULL;
1664  char *prefix_moved = NULL;
1665  const char *flags = prefix;
1666 
1667  if (pcmk_is_set(options, xml_log_option_formatted)) {
1668  CRM_CHECK(depth >= 0, depth = 0);
1669  spaces = 2 * depth;
1670  }
1671 
1672  prefix_del = strdup(prefix);
1673  prefix_del[0] = '-';
1674  prefix_del[1] = '-';
1675  prefix_moved = strdup(prefix);
1676  prefix_moved[1] = '~';
1677 
1678  if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
1679  flags = prefix_moved;
1680  } else {
1681  flags = prefix;
1682  }
1683 
1684  pcmk__xml_log(log_level, file, function, line, flags, data, depth,
1685  options|xml_log_option_open);
1686 
1687  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1688  const char *aname = (const char*) a->name;
1689 
1690  nodepriv = a->_private;
1691  if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1692  const char *value = crm_element_value(data, aname);
1693  flags = prefix_del;
1694  do_crm_log_alias(log_level, file, function, line,
1695  "%s %*s @%s=%s", flags, spaces, "", aname,
1696  value);
1697 
1698  } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
1699  const char *value = crm_element_value(data, aname);
1700 
1701  if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1702  flags = prefix_m;
1703 
1704  } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_modified)) {
1705  flags = prefix;
1706 
1707  } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
1708  flags = prefix_moved;
1709 
1710  } else {
1711  flags = prefix;
1712  }
1713  do_crm_log_alias(log_level, file, function, line,
1714  "%s %*s @%s=%s", flags, spaces, "", aname,
1715  value);
1716  }
1717  }
1718  free(prefix_moved);
1719  free(prefix_del);
1720 
1721  for (child = pcmk__xml_first_child(data); child != NULL;
1722  child = pcmk__xml_next(child)) {
1723  log_xml_changes(log_level, file, function, line, prefix, child,
1724  depth + 1, options);
1725  }
1726 
1727  pcmk__xml_log(log_level, file, function, line, prefix, data, depth,
1728  options|xml_log_option_close);
1729 
1730  } else {
1731  for (child = pcmk__xml_first_child(data); child != NULL;
1732  child = pcmk__xml_next(child)) {
1733  log_xml_changes(log_level, file, function, line, prefix, child,
1734  depth + 1, options);
1735  }
1736  }
1737 
1738  free(prefix_m);
1739 
1740 }
1741 
1742 void
1743 log_data_element(int log_level, const char *file, const char *function,
1744  int line, const char *prefix, const xmlNode *data, int depth,
1745  int options)
1746 {
1747  xmlNode *a_child = NULL;
1748 
1749  char *prefix_m = NULL;
1750 
1751  if (log_level == LOG_NEVER) {
1752  return;
1753  }
1754 
1755  if (prefix == NULL) {
1756  prefix = "";
1757  }
1758 
1759  /* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
1760  if (data == NULL) {
1761  do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
1762  "No data to dump as XML");
1763  return;
1764  }
1765 
1766  if (pcmk_is_set(options, xml_log_option_dirty_add)) {
1767  log_xml_changes(log_level, file, function, line, prefix, data, depth,
1768  options);
1769  return;
1770  }
1771 
1772  if (pcmk_is_set(options, xml_log_option_formatted)) {
1774  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1775  options |= xml_log_option_diff_all;
1776  prefix_m = strdup(prefix);
1777  prefix_m[1] = '+';
1778  prefix = prefix_m;
1779 
1780  } else if (pcmk_is_set(options, xml_log_option_diff_minus)
1781  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1782  options |= xml_log_option_diff_all;
1783  prefix_m = strdup(prefix);
1784  prefix_m[1] = '-';
1785  prefix = prefix_m;
1786  }
1787  }
1788 
1790  && !pcmk_is_set(options, xml_log_option_diff_all)) {
1791  /* Still searching for the actual change */
1792  for (a_child = pcmk__xml_first_child(data); a_child != NULL;
1793  a_child = pcmk__xml_next(a_child)) {
1794  log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
1795  }
1796  } else {
1797  pcmk__xml_log(log_level, file, function, line, prefix, data, depth,
1800  }
1801  free(prefix_m);
1802 }
1803 
1812 static void
1813 dump_filtered_xml(const xmlNode *data, int options, GString *buffer)
1814 {
1815  CRM_ASSERT(buffer != NULL);
1816 
1817  for (const xmlAttr *a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1818  if (!pcmk__xa_filterable((const char *) (a->name))) {
1819  dump_xml_attr(a, options, buffer);
1820  }
1821  }
1822 }
1823 
1833 static void
1834 dump_xml_element(const xmlNode *data, int options, GString *buffer, int depth)
1835 {
1836  const char *name = NULL;
1837 
1838  CRM_ASSERT(buffer != NULL);
1839 
1840  if (data == NULL) {
1841  crm_trace("Nothing to dump");
1842  return;
1843  }
1844 
1845  name = crm_element_name(data);
1846  CRM_ASSERT(name != NULL);
1847 
1848  insert_prefix(options, buffer, depth);
1849  pcmk__g_strcat(buffer, "<", name, NULL);
1850 
1851  if (options & xml_log_option_filtered) {
1852  dump_filtered_xml(data, options, buffer);
1853 
1854  } else {
1855  for (const xmlAttr *a = pcmk__xe_first_attr(data); a != NULL;
1856  a = a->next) {
1857 
1858  dump_xml_attr(a, options, buffer);
1859  }
1860  }
1861 
1862  if (data->children == NULL) {
1863  g_string_append(buffer, "/>");
1864 
1865  } else {
1866  g_string_append_c(buffer, '>');
1867  }
1868 
1869  if (pcmk_is_set(options, xml_log_option_formatted)) {
1870  g_string_append_c(buffer, '\n');
1871  }
1872 
1873  if (data->children) {
1874  xmlNode *xChild = NULL;
1875  for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1876  pcmk__xml2text(xChild, options, buffer, depth + 1);
1877  }
1878 
1879  insert_prefix(options, buffer, depth);
1880  pcmk__g_strcat(buffer, "</", name, ">", NULL);
1881 
1882  if (pcmk_is_set(options, xml_log_option_formatted)) {
1883  g_string_append_c(buffer, '\n');
1884  }
1885  }
1886 }
1887 
1897 static void
1898 dump_xml_text(const xmlNode *data, int options, GString *buffer, int depth)
1899 {
1900  CRM_ASSERT(buffer != NULL);
1901 
1902  if (data == NULL) {
1903  crm_trace("Nothing to dump");
1904  return;
1905  }
1906 
1907  insert_prefix(options, buffer, depth);
1908  g_string_append(buffer, (const gchar *) data->content);
1909 
1910  if (pcmk_is_set(options, xml_log_option_formatted)) {
1911  g_string_append_c(buffer, '\n');
1912  }
1913 }
1914 
1924 static void
1925 dump_xml_cdata(const xmlNode *data, int options, GString *buffer, int depth)
1926 {
1927  CRM_ASSERT(buffer != NULL);
1928 
1929  if (data == NULL) {
1930  crm_trace("Nothing to dump");
1931  return;
1932  }
1933 
1934  insert_prefix(options, buffer, depth);
1935  pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
1936  NULL);
1937 
1938  if (pcmk_is_set(options, xml_log_option_formatted)) {
1939  g_string_append_c(buffer, '\n');
1940  }
1941 }
1942 
1952 static void
1953 dump_xml_comment(const xmlNode *data, int options, GString *buffer, int depth)
1954 {
1955  CRM_ASSERT(buffer != NULL);
1956 
1957  if (data == NULL) {
1958  crm_trace("Nothing to dump");
1959  return;
1960  }
1961 
1962  insert_prefix(options, buffer, depth);
1963  pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
1964 
1965  if (pcmk_is_set(options, xml_log_option_formatted)) {
1966  g_string_append_c(buffer, '\n');
1967  }
1968 }
1969 
1970 #define PCMK__XMLDUMP_STATS 0
1971 
1981 void
1982 pcmk__xml2text(xmlNodePtr data, int options, GString *buffer, int depth)
1983 {
1984  CRM_ASSERT(buffer != NULL);
1985 
1986  if (data == NULL) {
1987  return;
1988  }
1989 
1990  if (!pcmk_is_set(options, xml_log_option_filtered)
1992  /* libxml's serialization reuse is a good idea, sadly we cannot
1993  apply it for the filtered cases (preceding filtering pass
1994  would preclude further reuse of such in-situ modified XML
1995  in generic context and is likely not a win performance-wise),
1996  and there's also a historically unstable throughput argument
1997  (likely stemming from memory allocation overhead, eventhough
1998  that shall be minimized with defaults preset in crm_xml_init) */
1999 #if (PCMK__XMLDUMP_STATS - 0)
2000  time_t next, new = time(NULL);
2001 #endif
2002  xmlDoc *doc;
2003  xmlOutputBuffer *xml_buffer;
2004 
2005  doc = getDocPtr(data);
2006  /* doc will only be NULL if data is */
2007  CRM_CHECK(doc != NULL, return);
2008 
2009  xml_buffer = xmlAllocOutputBuffer(NULL);
2010  CRM_ASSERT(xml_buffer != NULL);
2011 
2012  /* XXX we could setup custom allocation scheme for the particular
2013  buffer, but it's subsumed with crm_xml_init that needs to
2014  be invoked prior to entering this function as such, since
2015  its other branch vitally depends on it -- what can be done
2016  about this all is to have a facade parsing functions that
2017  would 100% mark entering libxml code for us, since we don't
2018  do anything as crazy as swapping out the binary form of the
2019  parsed tree (but those would need to be strictly used as
2020  opposed to libxml's raw functions) */
2021 
2022  xmlNodeDumpOutput(xml_buffer, doc, data, 0,
2023  (options & xml_log_option_formatted), NULL);
2024  /* attempt adding final NL - failing shouldn't be fatal here */
2025  (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
2026  if (xml_buffer->buffer != NULL) {
2027  g_string_append(buffer,
2028  (const gchar *) xmlBufContent(xml_buffer->buffer));
2029  }
2030 
2031 #if (PCMK__XMLDUMP_STATS - 0)
2032  next = time(NULL);
2033  if ((now + 1) < next) {
2034  crm_log_xml_trace(data, "Long time");
2035  crm_err("xmlNodeDumpOutput() -> %lld bytes took %ds",
2036  (long long) buffer->len, next - now);
2037  }
2038 #endif
2039 
2040  /* asserted allocation before so there should be something to remove */
2041  (void) xmlOutputBufferClose(xml_buffer);
2042  return;
2043  }
2044 
2045  switch(data->type) {
2046  case XML_ELEMENT_NODE:
2047  /* Handle below */
2048  dump_xml_element(data, options, buffer, depth);
2049  break;
2050  case XML_TEXT_NODE:
2051  /* if option xml_log_option_text is enabled, then dump XML_TEXT_NODE */
2052  if (options & xml_log_option_text) {
2053  dump_xml_text(data, options, buffer, depth);
2054  }
2055  break;
2056  case XML_COMMENT_NODE:
2057  dump_xml_comment(data, options, buffer, depth);
2058  break;
2059  case XML_CDATA_SECTION_NODE:
2060  dump_xml_cdata(data, options, buffer, depth);
2061  break;
2062  default:
2063  crm_warn("Unhandled type: %d", data->type);
2064  break;
2065 
2066  /*
2067  XML_ATTRIBUTE_NODE = 2
2068  XML_ENTITY_REF_NODE = 5
2069  XML_ENTITY_NODE = 6
2070  XML_PI_NODE = 7
2071  XML_DOCUMENT_NODE = 9
2072  XML_DOCUMENT_TYPE_NODE = 10
2073  XML_DOCUMENT_FRAG_NODE = 11
2074  XML_NOTATION_NODE = 12
2075  XML_HTML_DOCUMENT_NODE = 13
2076  XML_DTD_NODE = 14
2077  XML_ELEMENT_DECL = 15
2078  XML_ATTRIBUTE_DECL = 16
2079  XML_ENTITY_DECL = 17
2080  XML_NAMESPACE_DECL = 18
2081  XML_XINCLUDE_START = 19
2082  XML_XINCLUDE_END = 20
2083  XML_DOCB_DOCUMENT_NODE = 21
2084  */
2085  }
2086 }
2087 
2088 char *
2089 dump_xml_formatted_with_text(xmlNode * an_xml_node)
2090 {
2091  char *buffer = NULL;
2092  GString *g_buffer = g_string_sized_new(1024);
2093 
2094  pcmk__xml2text(an_xml_node,
2096  g_buffer, 0);
2097 
2098  if (g_buffer != NULL) {
2099  buffer = strdup((const char *) g_buffer->str);
2100  g_string_free(g_buffer, TRUE);
2101  }
2102  return buffer;
2103 }
2104 
2105 char *
2106 dump_xml_formatted(xmlNode * an_xml_node)
2107 {
2108  char *buffer = NULL;
2109  GString *g_buffer = g_string_sized_new(1024);
2110 
2111  pcmk__xml2text(an_xml_node, xml_log_option_formatted, g_buffer, 0);
2112 
2113  if (g_buffer != NULL) {
2114  buffer = strdup((const char *) g_buffer->str);
2115  g_string_free(g_buffer, TRUE);
2116  }
2117  return buffer;
2118 }
2119 
2120 char *
2121 dump_xml_unformatted(xmlNode * an_xml_node)
2122 {
2123  char *buffer = NULL;
2124  GString *g_buffer = g_string_sized_new(1024);
2125 
2126  pcmk__xml2text(an_xml_node, 0, g_buffer, 0);
2127 
2128  if (g_buffer != NULL) {
2129  buffer = strdup((const char *) g_buffer->str);
2130  g_string_free(g_buffer, TRUE);
2131  }
2132  return buffer;
2133 }
2134 
2135 gboolean
2136 xml_has_children(const xmlNode * xml_root)
2137 {
2138  if (xml_root != NULL && xml_root->children != NULL) {
2139  return TRUE;
2140  }
2141  return FALSE;
2142 }
2143 
2144 void
2145 xml_remove_prop(xmlNode * obj, const char *name)
2146 {
2147  if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
2148  crm_trace("Cannot remove %s from %s", name, obj->name);
2149 
2150  } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
2151  /* Leave in place (marked for removal) until after the diff is calculated */
2152  xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
2153  xml_node_private_t *nodepriv = attr->_private;
2154 
2155  set_parent_flag(obj, pcmk__xf_dirty);
2157  } else {
2158  xmlUnsetProp(obj, (pcmkXmlStr) name);
2159  }
2160 }
2161 
2162 void
2163 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
2164 {
2165  char *f = NULL;
2166 
2167  if (filename == NULL) {
2168  char *uuid = crm_generate_uuid();
2169 
2170  f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
2171  filename = f;
2172  free(uuid);
2173  }
2174 
2175  crm_info("Saving %s to %s", desc, filename);
2176  write_xml_file(xml, filename, FALSE);
2177  free(f);
2178 }
2179 
2187 static void
2188 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
2189 {
2190  for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
2191  pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
2192  }
2193 }
2194 
2209 static void
2210 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
2211  const char *old_value)
2212 {
2213  xml_doc_private_t *docpriv = new_xml->doc->_private;
2214  xmlAttr *attr = NULL;
2215  xml_node_private_t *nodepriv;
2216 
2217  // Prevent the dirty flag being set recursively upwards
2219 
2220  // Restore the old value (and the tracking flag)
2221  attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2223 
2224  // Reset flags (so the attribute doesn't appear as newly created)
2225  nodepriv = attr->_private;
2226  nodepriv->flags = 0;
2227 
2228  // Check ACLs and mark restored value for later removal
2229  xml_remove_prop(new_xml, attr_name);
2230 
2231  crm_trace("XML attribute %s=%s was removed from %s",
2232  attr_name, old_value, element);
2233 }
2234 
2235 /*
2236  * \internal
2237  * \brief Check ACLs for a changed XML attribute
2238  */
2239 static void
2240 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
2241  const char *old_value)
2242 {
2243  char *vcopy = crm_element_value_copy(new_xml, attr_name);
2244 
2245  crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
2246  attr_name, old_value, vcopy, element);
2247 
2248  // Restore the original value
2249  xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2250 
2251  // Change it back to the new value, to check ACLs
2252  crm_xml_add(new_xml, attr_name, vcopy);
2253  free(vcopy);
2254 }
2255 
2267 static void
2268 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
2269  xmlAttr *new_attr, int p_old, int p_new)
2270 {
2271  xml_node_private_t *nodepriv = new_attr->_private;
2272 
2273  crm_trace("XML attribute %s moved from position %d to %d in %s",
2274  old_attr->name, p_old, p_new, element);
2275 
2276  // Mark document, element, and all element's parents as changed
2277  mark_xml_node_dirty(new_xml);
2278 
2279  // Mark attribute as changed
2281 
2282  nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
2284 }
2285 
2293 static void
2294 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2295 {
2296  xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
2297 
2298  while (attr_iter != NULL) {
2299  xmlAttr *old_attr = attr_iter;
2300  xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2301  const char *name = (const char *) attr_iter->name;
2302  const char *old_value = crm_element_value(old_xml, name);
2303 
2304  attr_iter = attr_iter->next;
2305  if (new_attr == NULL) {
2306  mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
2307  old_value);
2308 
2309  } else {
2310  xml_node_private_t *nodepriv = new_attr->_private;
2311  int new_pos = pcmk__xml_position((xmlNode*) new_attr,
2312  pcmk__xf_skip);
2313  int old_pos = pcmk__xml_position((xmlNode*) old_attr,
2314  pcmk__xf_skip);
2315  const char *new_value = crm_element_value(new_xml, name);
2316 
2317  // This attribute isn't new
2319 
2320  if (strcmp(new_value, old_value) != 0) {
2321  mark_attr_changed(new_xml, (const char *) old_xml->name, name,
2322  old_value);
2323 
2324  } else if ((old_pos != new_pos)
2325  && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
2326  mark_attr_moved(new_xml, (const char *) old_xml->name,
2327  old_attr, new_attr, old_pos, new_pos);
2328  }
2329  }
2330  }
2331 }
2332 
2342 static void
2343 mark_created_attrs(xmlNode *new_xml)
2344 {
2345  xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
2346 
2347  while (attr_iter != NULL) {
2348  xmlAttr *new_attr = attr_iter;
2349  xml_node_private_t *nodepriv = attr_iter->_private;
2350 
2351  attr_iter = attr_iter->next;
2352  if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
2353  const char *attr_name = (const char *) new_attr->name;
2354 
2355  crm_trace("Created new attribute %s=%s in %s",
2356  attr_name, crm_element_value(new_xml, attr_name),
2357  new_xml->name);
2358 
2359  /* Check ACLs (we can't use the remove-then-create trick because it
2360  * would modify the attribute position).
2361  */
2362  if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
2363  pcmk__mark_xml_attr_dirty(new_attr);
2364  } else {
2365  // Creation was not allowed, so remove the attribute
2366  xmlUnsetProp(new_xml, new_attr->name);
2367  }
2368  }
2369  }
2370 }
2371 
2379 static void
2380 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2381 {
2382  set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
2383  xml_diff_old_attrs(old_xml, new_xml);
2384  mark_created_attrs(new_xml);
2385 }
2386 
2399 static void
2400 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2401 {
2402  // Re-create the child element so we can check ACLs
2403  xmlNode *candidate = add_node_copy(new_parent, old_child);
2404 
2405  // Clear flags on new child and its children
2406  reset_xml_node_flags(candidate);
2407 
2408  // Check whether ACLs allow the deletion
2409  pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
2410 
2411  // Remove the child again (which will track it in document's deleted_objs)
2412  free_xml_with_position(candidate,
2413  pcmk__xml_position(old_child, pcmk__xf_skip));
2414 
2415  if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
2416  pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
2417  pcmk__xf_skip);
2418  }
2419 }
2420 
2421 static void
2422 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2423  int p_old, int p_new)
2424 {
2425  xml_node_private_t *nodepriv = new_child->_private;
2426 
2427  crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
2428  new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
2429  p_old, p_new, new_parent->name);
2430  mark_xml_node_dirty(new_parent);
2432 
2433  if (p_old > p_new) {
2434  nodepriv = old_child->_private;
2435  } else {
2436  nodepriv = new_child->_private;
2437  }
2439 }
2440 
2441 // Given original and new XML, mark new XML portions that have changed
2442 static void
2443 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
2444 {
2445  xmlNode *cIter = NULL;
2446  xml_node_private_t *nodepriv = NULL;
2447 
2448  CRM_CHECK(new_xml != NULL, return);
2449  if (old_xml == NULL) {
2450  pcmk__mark_xml_created(new_xml);
2451  pcmk__apply_creation_acl(new_xml, check_top);
2452  return;
2453  }
2454 
2455  nodepriv = new_xml->_private;
2456  CRM_CHECK(nodepriv != NULL, return);
2457 
2458  if(nodepriv->flags & pcmk__xf_processed) {
2459  /* Avoid re-comparing nodes */
2460  return;
2461  }
2463 
2464  xml_diff_attrs(old_xml, new_xml);
2465 
2466  // Check for differences in the original children
2467  for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2468  xmlNode *old_child = cIter;
2469  xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true);
2470 
2471  cIter = pcmk__xml_next(cIter);
2472  if(new_child) {
2473  mark_xml_changes(old_child, new_child, TRUE);
2474 
2475  } else {
2476  mark_child_deleted(old_child, new_xml);
2477  }
2478  }
2479 
2480  // Check for moved or created children
2481  for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2482  xmlNode *new_child = cIter;
2483  xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true);
2484 
2485  cIter = pcmk__xml_next(cIter);
2486  if(old_child == NULL) {
2487  // This is a newly created child
2488  nodepriv = new_child->_private;
2490  mark_xml_changes(old_child, new_child, TRUE);
2491 
2492  } else {
2493  /* Check for movement, we already checked for differences */
2494  int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
2495  int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
2496 
2497  if(p_old != p_new) {
2498  mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2499  }
2500  }
2501  }
2502 }
2503 
2504 void
2505 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
2506 {
2508  xml_calculate_changes(old_xml, new_xml);
2509 }
2510 
2511 void
2512 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
2513 {
2514  CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
2515  return);
2516  CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
2517 
2518  if(xml_tracking_changes(new_xml) == FALSE) {
2519  xml_track_changes(new_xml, NULL, NULL, FALSE);
2520  }
2521 
2522  mark_xml_changes(old_xml, new_xml, FALSE);
2523 }
2524 
2525 gboolean
2526 can_prune_leaf(xmlNode * xml_node)
2527 {
2528  xmlNode *cIter = NULL;
2529  gboolean can_prune = TRUE;
2530  const char *name = crm_element_name(xml_node);
2531 
2534  return FALSE;
2535  }
2536 
2537  for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2538  const char *p_name = (const char *) a->name;
2539 
2540  if (strcmp(p_name, XML_ATTR_ID) == 0) {
2541  continue;
2542  }
2543  can_prune = FALSE;
2544  }
2545 
2546  cIter = pcmk__xml_first_child(xml_node);
2547  while (cIter) {
2548  xmlNode *child = cIter;
2549 
2550  cIter = pcmk__xml_next(cIter);
2551  if (can_prune_leaf(child)) {
2552  free_xml(child);
2553  } else {
2554  can_prune = FALSE;
2555  }
2556  }
2557  return can_prune;
2558 }
2559 
2568 xmlNode *
2569 pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
2570 {
2571  xmlNode *a_child = NULL;
2572  int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
2573 
2574  CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
2575 
2576  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2577  a_child = pcmk__xml_next(a_child)) {
2578  if (exact) {
2579  int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
2580  xml_node_private_t *nodepriv = a_child->_private;
2581 
2582  if (offset < search_offset) {
2583  continue;
2584 
2585  } else if (offset > search_offset) {
2586  return NULL;
2587  }
2588 
2589  if (pcmk_is_set(nodepriv->flags, pcmk__xf_skip)) {
2590  continue;
2591  }
2592  }
2593 
2594  if (a_child->type == XML_COMMENT_NODE
2595  && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
2596  return a_child;
2597 
2598  } else if (exact) {
2599  return NULL;
2600  }
2601  }
2602 
2603  return NULL;
2604 }
2605 
2617 void
2618 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
2619 {
2620  CRM_CHECK(update != NULL, return);
2621  CRM_CHECK(update->type == XML_COMMENT_NODE, return);
2622 
2623  if (target == NULL) {
2624  target = pcmk__xc_match(parent, update, false);
2625  }
2626 
2627  if (target == NULL) {
2628  add_node_copy(parent, update);
2629 
2630  } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
2631  xmlFree(target->content);
2632  target->content = xmlStrdup(update->content);
2633  }
2634 }
2635 
2648 void
2649 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
2650  bool as_diff)
2651 {
2652  xmlNode *a_child = NULL;
2653  const char *object_name = NULL,
2654  *object_href = NULL,
2655  *object_href_val = NULL;
2656 
2657 #if XML_PARSER_DEBUG
2658  crm_log_xml_trace(update, "update:");
2659  crm_log_xml_trace(target, "target:");
2660 #endif
2661 
2662  CRM_CHECK(update != NULL, return);
2663 
2664  if (update->type == XML_COMMENT_NODE) {
2665  pcmk__xc_update(parent, target, update);
2666  return;
2667  }
2668 
2669  object_name = crm_element_name(update);
2670  object_href_val = ID(update);
2671  if (object_href_val != NULL) {
2672  object_href = XML_ATTR_ID;
2673  } else {
2674  object_href_val = crm_element_value(update, XML_ATTR_IDREF);
2675  object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
2676  }
2677 
2678  CRM_CHECK(object_name != NULL, return);
2679  CRM_CHECK(target != NULL || parent != NULL, return);
2680 
2681  if (target == NULL) {
2682  target = pcmk__xe_match(parent, object_name,
2683  object_href, object_href_val);
2684  }
2685 
2686  if (target == NULL) {
2687  target = create_xml_node(parent, object_name);
2688  CRM_CHECK(target != NULL, return);
2689 #if XML_PARSER_DEBUG
2690  crm_trace("Added <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2691  object_href ? " " : "",
2692  object_href ? object_href : "",
2693  object_href ? "=" : "",
2694  object_href ? object_href_val : "");
2695 
2696  } else {
2697  crm_trace("Found node <%s%s%s%s%s/> to update",
2698  pcmk__s(object_name, "<null>"),
2699  object_href ? " " : "",
2700  object_href ? object_href : "",
2701  object_href ? "=" : "",
2702  object_href ? object_href_val : "");
2703 #endif
2704  }
2705 
2706  CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2707  pcmk__str_casei),
2708  return);
2709 
2710  if (as_diff == FALSE) {
2711  /* So that expand_plus_plus() gets called */
2712  copy_in_properties(target, update);
2713 
2714  } else {
2715  /* No need for expand_plus_plus(), just raw speed */
2716  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2717  a = a->next) {
2718  const char *p_value = pcmk__xml_attr_value(a);
2719 
2720  /* Remove it first so the ordering of the update is preserved */
2721  xmlUnsetProp(target, a->name);
2722  xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
2723  }
2724  }
2725 
2726  for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2727  a_child = pcmk__xml_next(a_child)) {
2728 #if XML_PARSER_DEBUG
2729  crm_trace("Updating child <%s%s%s%s%s/>",
2730  pcmk__s(object_name, "<null>"),
2731  object_href ? " " : "",
2732  object_href ? object_href : "",
2733  object_href ? "=" : "",
2734  object_href ? object_href_val : "");
2735 #endif
2736  pcmk__xml_update(target, NULL, a_child, as_diff);
2737  }
2738 
2739 #if XML_PARSER_DEBUG
2740  crm_trace("Finished with <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2741  object_href ? " " : "",
2742  object_href ? object_href : "",
2743  object_href ? "=" : "",
2744  object_href ? object_href_val : "");
2745 #endif
2746 }
2747 
2748 gboolean
2749 update_xml_child(xmlNode * child, xmlNode * to_update)
2750 {
2751  gboolean can_update = TRUE;
2752  xmlNode *child_of_child = NULL;
2753 
2754  CRM_CHECK(child != NULL, return FALSE);
2755  CRM_CHECK(to_update != NULL, return FALSE);
2756 
2757  if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
2758  can_update = FALSE;
2759 
2760  } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
2761  can_update = FALSE;
2762 
2763  } else if (can_update) {
2764 #if XML_PARSER_DEBUG
2765  crm_log_xml_trace(child, "Update match found...");
2766 #endif
2767  pcmk__xml_update(NULL, child, to_update, false);
2768  }
2769 
2770  for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2771  child_of_child = pcmk__xml_next(child_of_child)) {
2772  /* only update the first one */
2773  if (can_update) {
2774  break;
2775  }
2776  can_update = update_xml_child(child_of_child, to_update);
2777  }
2778 
2779  return can_update;
2780 }
2781 
2782 int
2783 find_xml_children(xmlNode ** children, xmlNode * root,
2784  const char *tag, const char *field, const char *value, gboolean search_matches)
2785 {
2786  int match_found = 0;
2787 
2788  CRM_CHECK(root != NULL, return FALSE);
2789  CRM_CHECK(children != NULL, return FALSE);
2790 
2791  if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
2792 
2793  } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
2794 
2795  } else {
2796  if (*children == NULL) {
2797  *children = create_xml_node(NULL, __func__);
2798  }
2799  add_node_copy(*children, root);
2800  match_found = 1;
2801  }
2802 
2803  if (search_matches || match_found == 0) {
2804  xmlNode *child = NULL;
2805 
2806  for (child = pcmk__xml_first_child(root); child != NULL;
2807  child = pcmk__xml_next(child)) {
2808  match_found += find_xml_children(children, child, tag, field, value, search_matches);
2809  }
2810  }
2811 
2812  return match_found;
2813 }
2814 
2815 gboolean
2816 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2817 {
2818  gboolean can_delete = FALSE;
2819  xmlNode *child_of_child = NULL;
2820 
2821  const char *up_id = NULL;
2822  const char *child_id = NULL;
2823  const char *right_val = NULL;
2824 
2825  CRM_CHECK(child != NULL, return FALSE);
2826  CRM_CHECK(update != NULL, return FALSE);
2827 
2828  up_id = ID(update);
2829  child_id = ID(child);
2830 
2831  if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2832  can_delete = TRUE;
2833  }
2834  if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
2835  can_delete = FALSE;
2836  }
2837  if (can_delete && delete_only) {
2838  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2839  a = a->next) {
2840  const char *p_name = (const char *) a->name;
2841  const char *p_value = pcmk__xml_attr_value(a);
2842 
2843  right_val = crm_element_value(child, p_name);
2844  if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) {
2845  can_delete = FALSE;
2846  }
2847  }
2848  }
2849 
2850  if (can_delete && parent != NULL) {
2851  crm_log_xml_trace(child, "Delete match found...");
2852  if (delete_only || update == NULL) {
2853  free_xml(child);
2854 
2855  } else {
2856  xmlNode *tmp = copy_xml(update);
2857  xmlDoc *doc = tmp->doc;
2858  xmlNode *old = NULL;
2859 
2860  xml_accept_changes(tmp);
2861  old = xmlReplaceNode(child, tmp);
2862 
2863  if(xml_tracking_changes(tmp)) {
2864  /* Replaced sections may have included relevant ACLs */
2865  pcmk__apply_acl(tmp);
2866  }
2867 
2868  xml_calculate_changes(old, tmp);
2869  xmlDocSetRootElement(doc, old);
2870  free_xml(old);
2871  }
2872  child = NULL;
2873  return TRUE;
2874 
2875  } else if (can_delete) {
2876  crm_log_xml_debug(child, "Cannot delete the search root");
2877  can_delete = FALSE;
2878  }
2879 
2880  child_of_child = pcmk__xml_first_child(child);
2881  while (child_of_child) {
2882  xmlNode *next = pcmk__xml_next(child_of_child);
2883 
2884  can_delete = replace_xml_child(child, child_of_child, update, delete_only);
2885 
2886  /* only delete the first one */
2887  if (can_delete) {
2888  child_of_child = NULL;
2889  } else {
2890  child_of_child = next;
2891  }
2892  }
2893 
2894  return can_delete;
2895 }
2896 
2897 xmlNode *
2898 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2899 {
2900  xmlNode *child = NULL;
2901  GSList *nvpairs = NULL;
2902  xmlNode *result = NULL;
2903  const char *name = NULL;
2904 
2905  CRM_CHECK(input != NULL, return NULL);
2906 
2907  name = crm_element_name(input);
2908  CRM_CHECK(name != NULL, return NULL);
2909 
2911  nvpairs = pcmk_xml_attrs2nvpairs(input);
2912  nvpairs = pcmk_sort_nvpairs(nvpairs);
2913  pcmk_nvpairs2xml_attrs(nvpairs, result);
2914  pcmk_free_nvpairs(nvpairs);
2915 
2916  for (child = pcmk__xml_first_child(input); child != NULL;
2917  child = pcmk__xml_next(child)) {
2918 
2919  if (recursive) {
2920  sorted_xml(child, result, recursive);
2921  } else {
2922  add_node_copy(result, child);
2923  }
2924  }
2925 
2926  return result;
2927 }
2928 
2929 xmlNode *
2930 first_named_child(const xmlNode *parent, const char *name)
2931 {
2932  xmlNode *match = NULL;
2933 
2934  for (match = pcmk__xe_first_child(parent); match != NULL;
2935  match = pcmk__xe_next(match)) {
2936  /*
2937  * name == NULL gives first child regardless of name; this is
2938  * semantically incorrect in this function, but may be necessary
2939  * due to prior use of xml_child_iter_filter
2940  */
2941  if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) {
2942  return match;
2943  }
2944  }
2945  return NULL;
2946 }
2947 
2955 xmlNode *
2956 crm_next_same_xml(const xmlNode *sibling)
2957 {
2958  xmlNode *match = pcmk__xe_next(sibling);
2959  const char *name = crm_element_name(sibling);
2960 
2961  while (match != NULL) {
2962  if (!strcmp(crm_element_name(match), name)) {
2963  return match;
2964  }
2965  match = pcmk__xe_next(match);
2966  }
2967  return NULL;
2968 }
2969 
2970 void
2972 {
2973  static bool init = true;
2974 
2975  if(init) {
2976  init = false;
2977  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
2978  * pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
2979  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
2980  * less than 1 second.
2981  */
2982  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2983 
2984  /* Populate and free the _private field when nodes are created and destroyed */
2985  xmlDeregisterNodeDefault(free_private_data);
2986  xmlRegisterNodeDefault(new_private_data);
2987 
2988  crm_schema_init();
2989  }
2990 }
2991 
2992 void
2994 {
2996  xmlCleanupParser();
2997 }
2998 
2999 #define XPATH_MAX 512
3000 
3001 xmlNode *
3002 expand_idref(xmlNode * input, xmlNode * top)
3003 {
3004  const char *tag = NULL;
3005  const char *ref = NULL;
3006  xmlNode *result = input;
3007 
3008  if (result == NULL) {
3009  return NULL;
3010 
3011  } else if (top == NULL) {
3012  top = input;
3013  }
3014 
3015  tag = crm_element_name(result);
3017 
3018  if (ref != NULL) {
3019  char *xpath_string = crm_strdup_printf("//%s[@id='%s']", tag, ref);
3020 
3021  result = get_xpath_object(xpath_string, top, LOG_ERR);
3022  if (result == NULL) {
3023  char *nodePath = (char *)xmlGetNodePath(top);
3024 
3025  crm_err("No match for %s found in %s: Invalid configuration",
3026  xpath_string, pcmk__s(nodePath, "unrecognizable path"));
3027  free(nodePath);
3028  }
3029  free(xpath_string);
3030  }
3031  return result;
3032 }
3033 
3034 char *
3036 {
3037  static const char *base = NULL;
3038  char *ret = NULL;
3039 
3040  if (base == NULL) {
3041  base = getenv("PCMK_schema_directory");
3042  }
3043  if (pcmk__str_empty(base)) {
3044  base = CRM_SCHEMA_DIRECTORY;
3045  }
3046 
3047  switch (ns) {
3050  ret = strdup(base);
3051  break;
3054  ret = crm_strdup_printf("%s/base", base);
3055  break;
3056  default:
3057  crm_err("XML artefact family specified as %u not recognized", ns);
3058  }
3059  return ret;
3060 }
3061 
3062 char *
3063 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
3064 {
3065  char *base = pcmk__xml_artefact_root(ns), *ret = NULL;
3066 
3067  switch (ns) {
3070  ret = crm_strdup_printf("%s/%s.rng", base, filespec);
3071  break;
3074  ret = crm_strdup_printf("%s/%s.xsl", base, filespec);
3075  break;
3076  default:
3077  crm_err("XML artefact family specified as %u not recognized", ns);
3078  }
3079  free(base);
3080 
3081  return ret;
3082 }
3083 
3084 void
3085 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
3086 {
3087  while (true) {
3088  const char *name, *value;
3089 
3090  name = va_arg(pairs, const char *);
3091  if (name == NULL) {
3092  return;
3093  }
3094 
3095  value = va_arg(pairs, const char *);
3096  if (value != NULL) {
3097  crm_xml_add(node, name, value);
3098  }
3099  }
3100 }
3101 
3102 void
3103 pcmk__xe_set_props(xmlNodePtr node, ...)
3104 {
3105  va_list pairs;
3106  va_start(pairs, node);
3107  pcmk__xe_set_propv(node, pairs);
3108  va_end(pairs);
3109 }
3110 
3111 int
3112 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
3113  int (*handler)(xmlNode *xml, void *userdata),
3114  void *userdata)
3115 {
3116  xmlNode *children = (xml? xml->children : NULL);
3117 
3118  CRM_ASSERT(handler != NULL);
3119 
3120  for (xmlNode *node = children; node != NULL; node = node->next) {
3121  if (node->type == XML_ELEMENT_NODE &&
3122  pcmk__str_eq(child_element_name, (const char *) node->name, pcmk__str_null_matches)) {
3123  int rc = handler(node, userdata);
3124 
3125  if (rc != pcmk_rc_ok) {
3126  return rc;
3127  }
3128  }
3129  }
3130 
3131  return pcmk_rc_ok;
3132 }
3133 
3134 // Deprecated functions kept only for backward API compatibility
3135 // LCOV_EXCL_START
3136 
3137 #include <crm/common/xml_compat.h>
3138 
3139 xmlNode *
3140 find_entity(xmlNode *parent, const char *node_name, const char *id)
3141 {
3142  return pcmk__xe_match(parent, node_name,
3143  ((id == NULL)? id : XML_ATTR_ID), id);
3144 }
3145 
3146 void
3148 {
3149  free_xml(data);
3150 }
3151 
3152 // LCOV_EXCL_STOP
3153 // End deprecated API
#define LOG_TRACE
Definition: logging.h:37
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml.c:3112
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:49
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2505
A dumping ground.
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition: nvpair.c:146
void crm_schema_init(void)
Definition: schemas.c:377
#define PCMK__XML_PARSE_OPTS
Definition: xml.c:46
const char * bz2_strerror(int rc)
Definition: results.c:823
void pcmk__xml_log(int log_level, const char *file, const char *function, int line, const char *prefix, const xmlNode *data, int depth, int options)
Definition: xml.c:1623
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:99
char * crm_generate_uuid(void)
Definition: utils.c:509
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:102
void pcmk__free_acls(GList *acls)
Definition: acl.c:44
int pcmk_rc2legacy(int rc)
Definition: results.c:521
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:928
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition: xml.c:301
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2512
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:419
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:1190
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
Definition: xml.c:1333
void fix_plus_plus_recursive(xmlNode *target)
Parse integer assignment statements on this node and all its child nodes.
Definition: xml.c:578
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition: xml.c:2971
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
void crm_schema_cleanup(void)
Definition: schemas.c:554
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:323
void pcmk_free_xml_subtree(xmlNode *xml)
Definition: xml.c:808
#define XML_ATTR_IDREF
Definition: msg_xml.h:136
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml.c:3103
#define pcmk__log_else(level, else_action)
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition: acl.c:571
xmlNode * stdin2xml(void)
Definition: xml.c:991
#define XML_DOC_PRIVATE_MAGIC
Definition: xml.c:173
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:211
#define do_crm_log_alias(level, file, function, line, fmt, args...)
Log a message as if it came from a different code location.
Definition: logging.h:282
unsigned int crm_trace_nonlog
Definition: logging.c:46
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2956
#define LOG_NEVER
Definition: logging.h:47
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:214
Deprecated Pacemaker XML API.
xmlNode * filename2xml(const char *filename)
Definition: xml.c:1106
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:2783
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Update current XML attribute value per parsed integer assignment statement.
Definition: xml.c:612
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
Definition: xml.c:2649
int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition: xml.c:340
xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
Definition: xml.c:394
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition: xml.c:3035
#define crm_warn(fmt, args...)
Definition: logging.h:360
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml.c:3085
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:1078
xmlNode * copy_xml(xmlNode *src)
Definition: xml.c:891
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:3002
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:714
#define XML_ATTR_ID
Definition: msg_xml.h:134
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749
#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
Definition: xml_internal.h:67
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, const xmlNode *data, int depth, int options)
Definition: xml.c:1743
void free_xml(xmlNode *child)
Definition: xml.c:885
#define crm_trace(fmt, args...)
Definition: logging.h:365
#define XML_NODE_PRIVATE_MAGIC
Definition: xml.c:174
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1214
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
#define crm_log_xml_debug(xml, text)
Definition: logging.h:372
xmlNode * pcmk__xe_match(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:520
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:2163
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Definition: xml.c:108
Wrappers for and extensions to libxml2.
#define crm_log_xml_warn(xml, text)
Definition: logging.h:369
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition: xml.c:1212
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:216
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition: xml.c:3063
void crm_xml_cleanup(void)
Definition: xml.c:2993
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition: xml.c:727
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:711
char * dump_xml_formatted(xmlNode *an_xml_node)
Definition: xml.c:2106
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition: acl.c:288
xmlNode * string2xml(const char *input)
Definition: xml.c:930
void xml_log_changes(uint8_t log_level, const char *function, xmlNode *xml)
Definition: xml.c:410
const xmlChar * pcmkXmlStr
Definition: xml.h:50
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
Definition: xml.c:2089
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
Definition: digest.c:237
xml_private_flags
Definition: xml_internal.h:320
const char * target
Definition: pcmk_fence.c:29
#define CRM_XS
Definition: logging.h:55
char * dump_xml_unformatted(xmlNode *an_xml_node)
Definition: xml.c:2121
const char * pcmk__get_tmpdir(void)
Definition: io.c:541
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1361
#define PCMK__BUFFER_SIZE
pcmk__xml_artefact_ns
Definition: xml_internal.h:149
void copy_in_properties(xmlNode *target, xmlNode *src)
Definition: xml.c:545
#define crm_log_xml_err(xml, text)
Definition: logging.h:368
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:310
#define XML_DIFF_MARKER
Definition: msg_xml.h:114
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:2136
#define crm_err(fmt, args...)
Definition: logging.h:359
#define CRM_ASSERT(expr)
Definition: results.h:42
#define CRM_SCHEMA_DIRECTORY
Definition: config.h:45
#define XML_CIB_ATTR_WRITTEN
Definition: msg_xml.h:131
xmlNode * input
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:655
#define XML_ACL_TAG_ROLE_REFv1
Definition: msg_xml.h:428
void pcmk__mark_xml_created(xmlNode *xml)
Definition: xml.c:145
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:224
void xml_accept_changes(xmlNode *xml)
Definition: xml.c:445
void crm_destroy_xml(gpointer data)
Definition: xml.c:3147
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition: nvpair.c:201
void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml.c:163
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition: acl.c:613
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1406
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
Definition: xml.c:786
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2749
GString * pcmk__element_xpath(const xmlNode *xml)
Definition: xpath.c:281
#define XML_CIB_TAG_OBJ_REF
Definition: msg_xml.h:445
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:774
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2898
#define crm_log_xml_trace(xml, text)
Definition: logging.h:373
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:470
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:316
#define XML_ACL_TAG_ROLE_REF
Definition: msg_xml.h:427
void pcmk__xml2text(xmlNodePtr data, int options, GString *buffer, int depth)
Definition: xml.c:1982
#define attr_matches(c, n, v)
Definition: xml.c:503
#define ID(x)
Definition: msg_xml.h:468
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:682
const char * parent
Definition: cib.c:25
const char * pcmk__epoch2str(const time_t *when)
Definition: iso8601.c:1730
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:741
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:397
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2816
void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml.c:2618
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2145
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1176
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:3140
GSList * pcmk_xml_attrs2nvpairs(const xmlNode *xml)
Create a list of name/value pairs from an XML node&#39;s attributes.
Definition: nvpair.c:161
#define crm_info(fmt, args...)
Definition: logging.h:362
xmlNode * pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
Definition: xml.c:2569
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:563
uint64_t flags
Definition: remote.c:215
gboolean can_prune_leaf(xmlNode *xml_node)
Definition: xml.c:2526
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:323