pacemaker  2.1.6-802a72226b
Scalable High-Availability cluster resource manager
xml.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/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 /* @TODO XML_PARSE_RECOVER allows some XML errors to be silently worked around
37  * by libxml2, which is potentially ambiguous and dangerous. We should drop it
38  * when we can break backward compatibility with configurations that might be
39  * relying on it (i.e. pacemaker 3.0.0).
40  *
41  * It might be a good idea to have a transitional period where we first try
42  * parsing without XML_PARSE_RECOVER, and if that fails, try parsing again with
43  * it, logging a warning if it succeeds.
44  */
45 #define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER)
46 
47 bool
48 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
49 {
50  if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
51  return FALSE;
52  } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
54  return FALSE;
55  } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
56  pcmk__xf_lazy)) {
57  return FALSE;
58  }
59  return TRUE;
60 }
61 
62 static inline void
63 set_parent_flag(xmlNode *xml, long flag)
64 {
65  for(; xml; xml = xml->parent) {
66  xml_node_private_t *nodepriv = xml->_private;
67 
68  if (nodepriv == NULL) {
69  /* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
70  } else {
71  pcmk__set_xml_flags(nodepriv, flag);
72  }
73  }
74 }
75 
76 void
77 pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
78 {
79  if(xml && xml->doc && xml->doc->_private){
80  /* During calls to xmlDocCopyNode(), xml->doc may be unset */
81  xml_doc_private_t *docpriv = xml->doc->_private;
82 
83  pcmk__set_xml_flags(docpriv, flag);
84  }
85 }
86 
87 // Mark document, element, and all element's parents as changed
88 static inline void
89 mark_xml_node_dirty(xmlNode *xml)
90 {
92  set_parent_flag(xml, pcmk__xf_dirty);
93 }
94 
95 // Clear flags on XML node and its children
96 static void
97 reset_xml_node_flags(xmlNode *xml)
98 {
99  xmlNode *cIter = NULL;
100  xml_node_private_t *nodepriv = xml->_private;
101 
102  if (nodepriv) {
103  nodepriv->flags = 0;
104  }
105 
106  for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
107  cIter = pcmk__xml_next(cIter)) {
108  reset_xml_node_flags(cIter);
109  }
110 }
111 
112 // Set xpf_created flag on XML node and any children
113 void
115 {
116  xmlNode *cIter = NULL;
117  xml_node_private_t *nodepriv = xml->_private;
118 
119  if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) {
120  if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
122  mark_xml_node_dirty(xml);
123  }
124  for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
125  cIter = pcmk__xml_next(cIter)) {
126  pcmk__mark_xml_created(cIter);
127  }
128  }
129 }
130 
131 void
133 {
134  xmlNode *parent = a->parent;
135  xml_node_private_t *nodepriv = a->_private;
136 
139  mark_xml_node_dirty(parent);
140 }
141 
142 #define XML_DOC_PRIVATE_MAGIC 0x81726354UL
143 #define XML_NODE_PRIVATE_MAGIC 0x54637281UL
144 
145 // Free an XML object previously marked as deleted
146 static void
147 free_deleted_object(void *data)
148 {
149  if(data) {
150  pcmk__deleted_xml_t *deleted_obj = data;
151 
152  free(deleted_obj->path);
153  free(deleted_obj);
154  }
155 }
156 
157 // Free and NULL user, ACLs, and deleted objects in an XML node's private data
158 static void
159 reset_xml_private_data(xml_doc_private_t *docpriv)
160 {
161  if (docpriv != NULL) {
163 
164  free(docpriv->user);
165  docpriv->user = NULL;
166 
167  if (docpriv->acls != NULL) {
168  pcmk__free_acls(docpriv->acls);
169  docpriv->acls = NULL;
170  }
171 
172  if(docpriv->deleted_objs) {
173  g_list_free_full(docpriv->deleted_objs, free_deleted_object);
174  docpriv->deleted_objs = NULL;
175  }
176  }
177 }
178 
179 // Free all private data associated with an XML node
180 static void
181 free_private_data(xmlNode *node)
182 {
183  /* Note:
184 
185  This function frees private data assosciated with an XML node,
186  unless the function is being called as a result of internal
187  XSLT cleanup.
188 
189  That could happen through, for example, the following chain of
190  function calls:
191 
192  xsltApplyStylesheetInternal
193  -> xsltFreeTransformContext
194  -> xsltFreeRVTs
195  -> xmlFreeDoc
196 
197  And in that case, the node would fulfill three conditions:
198 
199  1. It would be a standalone document (i.e. it wouldn't be
200  part of a document)
201  2. It would have a space-prefixed name (for reference, please
202  see xsltInternals.h: XSLT_MARK_RES_TREE_FRAG)
203  3. It would carry its own payload in the _private field.
204 
205  We do not free data in this circumstance to avoid a failed
206  assertion on the XML_*_PRIVATE_MAGIC later.
207 
208  */
209  if (node->name == NULL || node->name[0] != ' ') {
210  if (node->_private) {
211  if (node->type == XML_DOCUMENT_NODE) {
212  reset_xml_private_data(node->_private);
213  } else {
214  CRM_ASSERT(((xml_node_private_t *) node->_private)->check
216  /* nothing dynamically allocated nested */
217  }
218  free(node->_private);
219  node->_private = NULL;
220  }
221  }
222 }
223 
224 // Allocate and initialize private data for an XML node
225 static void
226 new_private_data(xmlNode *node)
227 {
228  switch (node->type) {
229  case XML_DOCUMENT_NODE: {
230  xml_doc_private_t *docpriv = NULL;
231  docpriv = calloc(1, sizeof(xml_doc_private_t));
232  CRM_ASSERT(docpriv != NULL);
233  docpriv->check = XML_DOC_PRIVATE_MAGIC;
234  /* Flags will be reset if necessary when tracking is enabled */
236  node->_private = docpriv;
237  break;
238  }
239  case XML_ELEMENT_NODE:
240  case XML_ATTRIBUTE_NODE:
241  case XML_COMMENT_NODE: {
242  xml_node_private_t *nodepriv = NULL;
243  nodepriv = calloc(1, sizeof(xml_node_private_t));
244  CRM_ASSERT(nodepriv != NULL);
245  nodepriv->check = XML_NODE_PRIVATE_MAGIC;
246  /* Flags will be reset if necessary when tracking is enabled */
248  node->_private = nodepriv;
249  if (pcmk__tracking_xml_changes(node, FALSE)) {
250  /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
251  * not hooked up at the point we are called
252  */
253  mark_xml_node_dirty(node);
254  }
255  break;
256  }
257  case XML_TEXT_NODE:
258  case XML_DTD_NODE:
259  case XML_CDATA_SECTION_NODE:
260  break;
261  default:
262  /* Ignore */
263  crm_trace("Ignoring %p %d", node, node->type);
264  CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
265  break;
266  }
267 }
268 
269 void
270 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
271 {
272  xml_accept_changes(xml);
273  crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
275  if(enforce_acls) {
276  if(acl_source == NULL) {
277  acl_source = xml;
278  }
280  pcmk__unpack_acl(acl_source, xml, user);
281  pcmk__apply_acl(xml);
282  }
283 }
284 
285 bool xml_tracking_changes(xmlNode * xml)
286 {
287  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
288  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
290 }
291 
292 bool xml_document_dirty(xmlNode *xml)
293 {
294  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
295  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
297 }
298 
308 int
309 pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
310 {
311  int position = 0;
312 
313  for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
314  xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
315 
316  if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
317  position++;
318  }
319  }
320 
321  return position;
322 }
323 
324 // This also clears attribute's flags if not marked as deleted
325 static bool
326 marked_as_deleted(xmlAttrPtr a, void *user_data)
327 {
328  xml_node_private_t *nodepriv = a->_private;
329 
330  if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
331  return true;
332  }
333  nodepriv->flags = pcmk__xf_none;
334  return false;
335 }
336 
337 // Remove all attributes marked as deleted from an XML node
338 static void
339 accept_attr_deletions(xmlNode *xml)
340 {
341  // Clear XML node's flags
342  ((xml_node_private_t *) xml->_private)->flags = pcmk__xf_none;
343 
344  // Remove this XML node's attributes that were marked as deleted
345  pcmk__xe_remove_matching_attrs(xml, marked_as_deleted, NULL);
346 
347  // Recursively do the same for this XML node's children
348  for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
349  cIter = pcmk__xml_next(cIter)) {
350  accept_attr_deletions(cIter);
351  }
352 }
353 
362 xmlNode *
363 pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
364 {
365  CRM_CHECK(needle != NULL, return NULL);
366 
367  if (needle->type == XML_COMMENT_NODE) {
368  return pcmk__xc_match(haystack, needle, exact);
369 
370  } else {
371  const char *id = ID(needle);
372  const char *attr = (id == NULL)? NULL : XML_ATTR_ID;
373 
374  return pcmk__xe_match(haystack, crm_element_name(needle), attr, id);
375  }
376 }
377 
378 void
379 xml_accept_changes(xmlNode * xml)
380 {
381  xmlNode *top = NULL;
382  xml_doc_private_t *docpriv = NULL;
383 
384  if(xml == NULL) {
385  return;
386  }
387 
388  crm_trace("Accepting changes to %p", xml);
389  docpriv = xml->doc->_private;
390  top = xmlDocGetRootElement(xml->doc);
391 
392  reset_xml_private_data(xml->doc->_private);
393 
394  if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
395  docpriv->flags = pcmk__xf_none;
396  return;
397  }
398 
399  docpriv->flags = pcmk__xf_none;
400  accept_attr_deletions(top);
401 }
402 
403 xmlNode *
404 find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
405 {
406  xmlNode *a_child = NULL;
407  const char *name = "NULL";
408 
409  if (root != NULL) {
410  name = crm_element_name(root);
411  }
412 
413  if (search_path == NULL) {
414  crm_warn("Will never find <NULL>");
415  return NULL;
416  }
417 
418  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
419  a_child = pcmk__xml_next(a_child)) {
420  if (strcmp((const char *)a_child->name, search_path) == 0) {
421 /* crm_trace("returning node (%s).", crm_element_name(a_child)); */
422  return a_child;
423  }
424  }
425 
426  if (must_find) {
427  crm_warn("Could not find %s in %s.", search_path, name);
428  } else if (root != NULL) {
429  crm_trace("Could not find %s in %s.", search_path, name);
430  } else {
431  crm_trace("Could not find %s in <NULL>.", search_path);
432  }
433 
434  return NULL;
435 }
436 
437 #define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \
438  (v), pcmk__str_none)
439 
453 xmlNode *
454 pcmk__xe_match(const xmlNode *parent, const char *node_name,
455  const char *attr_n, const char *attr_v)
456 {
457  CRM_CHECK(parent != NULL, return NULL);
458  CRM_CHECK(attr_v == NULL || attr_n != NULL, return NULL);
459 
460  for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
461  child = pcmk__xml_next(child)) {
462  if (pcmk__str_eq(node_name, (const char *) (child->name),
464  && ((attr_n == NULL) ||
465  (attr_v == NULL && xmlHasProp(child, (pcmkXmlStr) attr_n)) ||
466  (attr_v != NULL && attr_matches(child, attr_n, attr_v)))) {
467  return child;
468  }
469  }
470  crm_trace("XML child node <%s%s%s%s%s> not found in %s",
471  (node_name? node_name : "(any)"),
472  (attr_n? " " : ""),
473  (attr_n? attr_n : ""),
474  (attr_n? "=" : ""),
475  (attr_n? attr_v : ""),
476  crm_element_name(parent));
477  return NULL;
478 }
479 
480 void
481 copy_in_properties(xmlNode *target, const xmlNode *src)
482 {
483  if (src == NULL) {
484  crm_warn("No node to copy properties from");
485 
486  } else if (target == NULL) {
487  crm_err("No node to copy properties into");
488 
489  } else {
490  for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
491  const char *p_name = (const char *) a->name;
492  const char *p_value = pcmk__xml_attr_value(a);
493 
494  expand_plus_plus(target, p_name, p_value);
495  if (xml_acl_denied(target)) {
496  crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name);
497  return;
498  }
499  }
500  }
501 
502  return;
503 }
504 
513 void
515 {
516  /* TODO: Remove recursion and use xpath searches for value++ */
517  xmlNode *child = NULL;
518 
519  for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
520  const char *p_name = (const char *) a->name;
521  const char *p_value = pcmk__xml_attr_value(a);
522 
523  expand_plus_plus(target, p_name, p_value);
524  }
525  for (child = pcmk__xml_first_child(target); child != NULL;
526  child = pcmk__xml_next(child)) {
528  }
529 }
530 
547 void
548 expand_plus_plus(xmlNode * target, const char *name, const char *value)
549 {
550  int offset = 1;
551  int name_len = 0;
552  int int_value = 0;
553  int value_len = 0;
554 
555  const char *old_value = NULL;
556 
557  if (target == NULL || value == NULL || name == NULL) {
558  return;
559  }
560 
561  old_value = crm_element_value(target, name);
562 
563  if (old_value == NULL) {
564  /* if no previous value, set unexpanded */
565  goto set_unexpanded;
566 
567  } else if (strstr(value, name) != value) {
568  goto set_unexpanded;
569  }
570 
571  name_len = strlen(name);
572  value_len = strlen(value);
573  if (value_len < (name_len + 2)
574  || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
575  goto set_unexpanded;
576  }
577 
578  /* if we are expanding ourselves,
579  * then no previous value was set and leave int_value as 0
580  */
581  if (old_value != value) {
582  int_value = char2score(old_value);
583  }
584 
585  if (value[name_len + 1] != '+') {
586  const char *offset_s = value + (name_len + 2);
587 
588  offset = char2score(offset_s);
589  }
590  int_value += offset;
591 
592  if (int_value > INFINITY) {
593  int_value = (int)INFINITY;
594  }
595 
596  crm_xml_add_int(target, name, int_value);
597  return;
598 
599  set_unexpanded:
600  if (old_value == value) {
601  /* the old value is already set, nothing to do */
602  return;
603  }
604  crm_xml_add(target, name, value);
605  return;
606 }
607 
617 void
619  bool (*match)(xmlAttrPtr, void *),
620  void *user_data)
621 {
622  xmlAttrPtr next = NULL;
623 
624  for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
625  next = a->next; // Grab now because attribute might get removed
626  if ((match == NULL) || match(a, user_data)) {
627  if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
628  crm_trace("ACLs prevent removal of attributes (%s and "
629  "possibly others) from %s element",
630  (const char *) a->name, (const char *) element->name);
631  return; // ACLs apply to element, not particular attributes
632  }
633 
634  if (pcmk__tracking_xml_changes(element, false)) {
635  // Leave (marked for removal) until after diff is calculated
636  set_parent_flag(element, pcmk__xf_dirty);
637  pcmk__set_xml_flags((xml_node_private_t *) a->_private,
639  } else {
640  xmlRemoveProp(a);
641  }
642  }
643  }
644 }
645 
646 xmlDoc *
647 getDocPtr(xmlNode * node)
648 {
649  xmlDoc *doc = NULL;
650 
651  CRM_CHECK(node != NULL, return NULL);
652 
653  doc = node->doc;
654  if (doc == NULL) {
655  doc = xmlNewDoc((pcmkXmlStr) "1.0");
656  xmlDocSetRootElement(doc, node);
657  xmlSetTreeDoc(node, doc);
658  }
659  return doc;
660 }
661 
662 xmlNode *
663 add_node_copy(xmlNode * parent, xmlNode * src_node)
664 {
665  xmlNode *child = NULL;
666  xmlDoc *doc = getDocPtr(parent);
667 
668  CRM_CHECK(src_node != NULL, return NULL);
669 
670  child = xmlDocCopyNode(src_node, doc, 1);
671  xmlAddChild(parent, child);
672  pcmk__mark_xml_created(child);
673  return child;
674 }
675 
676 xmlNode *
677 create_xml_node(xmlNode * parent, const char *name)
678 {
679  xmlDoc *doc = NULL;
680  xmlNode *node = NULL;
681 
682  if (pcmk__str_empty(name)) {
683  CRM_CHECK(name != NULL && name[0] == 0, return NULL);
684  return NULL;
685  }
686 
687  if (parent == NULL) {
688  doc = xmlNewDoc((pcmkXmlStr) "1.0");
689  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
690  xmlDocSetRootElement(doc, node);
691 
692  } else {
693  doc = getDocPtr(parent);
694  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
695  xmlAddChild(parent, node);
696  }
698  return node;
699 }
700 
701 xmlNode *
702 pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content)
703 {
704  xmlNode *node = create_xml_node(parent, name);
705 
706  if (node != NULL) {
707  xmlNodeSetContent(node, (pcmkXmlStr) content);
708  }
709 
710  return node;
711 }
712 
713 xmlNode *
714 pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id,
715  const char *class_name, const char *text)
716 {
717  xmlNode *node = pcmk_create_xml_text_node(parent, element_name, text);
718 
719  if (class_name != NULL) {
720  crm_xml_add(node, "class", class_name);
721  }
722 
723  if (id != NULL) {
724  crm_xml_add(node, "id", id);
725  }
726 
727  return node;
728 }
729 
735 void
737 {
738  xmlUnlinkNode(xml); // Detaches from parent and siblings
739  xmlFreeNode(xml); // Frees
740 }
741 
742 static void
743 free_xml_with_position(xmlNode * child, int position)
744 {
745  if (child != NULL) {
746  xmlNode *top = NULL;
747  xmlDoc *doc = child->doc;
748  xml_node_private_t *nodepriv = child->_private;
749  xml_doc_private_t *docpriv = NULL;
750 
751  if (doc != NULL) {
752  top = xmlDocGetRootElement(doc);
753  }
754 
755  if (doc != NULL && top == child) {
756  /* Free everything */
757  xmlFreeDoc(doc);
758 
759  } else if (pcmk__check_acl(child, NULL, pcmk__xf_acl_write) == FALSE) {
760  GString *xpath = NULL;
761 
762  pcmk__if_tracing({}, return);
763  xpath = pcmk__element_xpath(child);
764  qb_log_from_external_source(__func__, __FILE__,
765  "Cannot remove %s %x", LOG_TRACE,
766  __LINE__, 0, (const char *) xpath->str,
767  nodepriv->flags);
768  g_string_free(xpath, TRUE);
769  return;
770 
771  } else {
772  if (doc && pcmk__tracking_xml_changes(child, FALSE)
773  && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
774 
775  GString *xpath = pcmk__element_xpath(child);
776 
777  if (xpath != NULL) {
778  pcmk__deleted_xml_t *deleted_obj = NULL;
779 
780  crm_trace("Deleting %s %p from %p",
781  (const char *) xpath->str, child, doc);
782 
783  deleted_obj = calloc(1, sizeof(pcmk__deleted_xml_t));
784  deleted_obj->path = strdup((const char *) xpath->str);
785 
786  CRM_ASSERT(deleted_obj->path != NULL);
787  g_string_free(xpath, TRUE);
788 
789  deleted_obj->position = -1;
790  /* Record the "position" only for XML comments for now */
791  if (child->type == XML_COMMENT_NODE) {
792  if (position >= 0) {
793  deleted_obj->position = position;
794 
795  } else {
796  deleted_obj->position = pcmk__xml_position(child,
797  pcmk__xf_skip);
798  }
799  }
800 
801  docpriv = doc->_private;
802  docpriv->deleted_objs = g_list_append(docpriv->deleted_objs, deleted_obj);
804  }
805  }
806  pcmk_free_xml_subtree(child);
807  }
808  }
809 }
810 
811 
812 void
813 free_xml(xmlNode * child)
814 {
815  free_xml_with_position(child, -1);
816 }
817 
818 xmlNode *
819 copy_xml(xmlNode * src)
820 {
821  xmlDoc *doc = xmlNewDoc((pcmkXmlStr) "1.0");
822  xmlNode *copy = xmlDocCopyNode(src, doc, 1);
823 
824  CRM_ASSERT(copy != NULL);
825  xmlDocSetRootElement(doc, copy);
826  xmlSetTreeDoc(copy, doc);
827  return copy;
828 }
829 
830 xmlNode *
831 string2xml(const char *input)
832 {
833  xmlNode *xml = NULL;
834  xmlDocPtr output = NULL;
835  xmlParserCtxtPtr ctxt = NULL;
836  xmlErrorPtr last_error = NULL;
837 
838  if (input == NULL) {
839  crm_err("Can't parse NULL input");
840  return NULL;
841  }
842 
843  /* create a parser context */
844  ctxt = xmlNewParserCtxt();
845  CRM_CHECK(ctxt != NULL, return NULL);
846 
847  xmlCtxtResetLastError(ctxt);
848  xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
849  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
851  if (output) {
852  xml = xmlDocGetRootElement(output);
853  }
854  last_error = xmlCtxtGetLastError(ctxt);
855  if (last_error && last_error->code != XML_ERR_OK) {
856  /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
857  /*
858  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
859  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
860  */
861  crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
862  last_error->domain, last_error->level, last_error->code, last_error->message);
863 
864  if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
865  CRM_LOG_ASSERT("Cannot parse an empty string");
866 
867  } else if (last_error->code != XML_ERR_DOCUMENT_END) {
868  crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
869  input);
870  if (xml != NULL) {
871  crm_log_xml_err(xml, "Partial");
872  }
873 
874  } else {
875  int len = strlen(input);
876  int lpc = 0;
877 
878  while(lpc < len) {
879  crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
880  lpc += 80;
881  }
882 
883  CRM_LOG_ASSERT("String parsing error");
884  }
885  }
886 
887  xmlFreeParserCtxt(ctxt);
888  return xml;
889 }
890 
891 xmlNode *
893 {
894  size_t data_length = 0;
895  size_t read_chars = 0;
896 
897  char *xml_buffer = NULL;
898  xmlNode *xml_obj = NULL;
899 
900  do {
901  xml_buffer = pcmk__realloc(xml_buffer, data_length + PCMK__BUFFER_SIZE);
902  read_chars = fread(xml_buffer + data_length, 1, PCMK__BUFFER_SIZE,
903  stdin);
904  data_length += read_chars;
905  } while (read_chars == PCMK__BUFFER_SIZE);
906 
907  if (data_length == 0) {
908  crm_warn("No XML supplied on stdin");
909  free(xml_buffer);
910  return NULL;
911  }
912 
913  xml_buffer[data_length] = '\0';
914  xml_obj = string2xml(xml_buffer);
915  free(xml_buffer);
916 
917  crm_log_xml_trace(xml_obj, "Created fragment");
918  return xml_obj;
919 }
920 
921 static char *
922 decompress_file(const char *filename)
923 {
924  char *buffer = NULL;
925  int rc = 0;
926  size_t length = 0, read_len = 0;
927  BZFILE *bz_file = NULL;
928  FILE *input = fopen(filename, "r");
929 
930  if (input == NULL) {
931  crm_perror(LOG_ERR, "Could not open %s for reading", filename);
932  return NULL;
933  }
934 
935  bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
936  if (rc != BZ_OK) {
937  crm_err("Could not prepare to read compressed %s: %s "
938  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
939  BZ2_bzReadClose(&rc, bz_file);
940  fclose(input);
941  return NULL;
942  }
943 
944  rc = BZ_OK;
945  // cppcheck seems not to understand the abort-logic in pcmk__realloc
946  // cppcheck-suppress memleak
947  while (rc == BZ_OK) {
948  buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1);
949  read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
950 
951  crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
952 
953  if (rc == BZ_OK || rc == BZ_STREAM_END) {
954  length += read_len;
955  }
956  }
957 
958  buffer[length] = '\0';
959 
960  if (rc != BZ_STREAM_END) {
961  crm_err("Could not read compressed %s: %s "
962  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
963  free(buffer);
964  buffer = NULL;
965  }
966 
967  BZ2_bzReadClose(&rc, bz_file);
968  fclose(input);
969  return buffer;
970 }
971 
978 void
979 pcmk__strip_xml_text(xmlNode *xml)
980 {
981  xmlNode *iter = xml->children;
982 
983  while (iter) {
984  xmlNode *next = iter->next;
985 
986  switch (iter->type) {
987  case XML_TEXT_NODE:
988  /* Remove it */
989  pcmk_free_xml_subtree(iter);
990  break;
991 
992  case XML_ELEMENT_NODE:
993  /* Search it */
994  pcmk__strip_xml_text(iter);
995  break;
996 
997  default:
998  /* Leave it */
999  break;
1000  }
1001 
1002  iter = next;
1003  }
1004 }
1005 
1006 xmlNode *
1007 filename2xml(const char *filename)
1008 {
1009  xmlNode *xml = NULL;
1010  xmlDocPtr output = NULL;
1011  bool uncompressed = true;
1012  xmlParserCtxtPtr ctxt = NULL;
1013  xmlErrorPtr last_error = NULL;
1014 
1015  /* create a parser context */
1016  ctxt = xmlNewParserCtxt();
1017  CRM_CHECK(ctxt != NULL, return NULL);
1018 
1019  xmlCtxtResetLastError(ctxt);
1020  xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
1021 
1022  if (filename) {
1023  uncompressed = !pcmk__ends_with_ext(filename, ".bz2");
1024  }
1025 
1026  if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
1027  /* STDIN_FILENO == fileno(stdin) */
1028  output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
1030 
1031  } else if (uncompressed) {
1032  output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
1033 
1034  } else {
1035  char *input = decompress_file(filename);
1036 
1037  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
1039  free(input);
1040  }
1041 
1042  if (output && (xml = xmlDocGetRootElement(output))) {
1043  pcmk__strip_xml_text(xml);
1044  }
1045 
1046  last_error = xmlCtxtGetLastError(ctxt);
1047  if (last_error && last_error->code != XML_ERR_OK) {
1048  /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
1049  /*
1050  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
1051  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
1052  */
1053  crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
1054  last_error->domain, last_error->level, last_error->code, last_error->message);
1055 
1056  if (last_error && last_error->code != XML_ERR_OK) {
1057  crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
1058  if (xml != NULL) {
1059  crm_log_xml_err(xml, "Partial");
1060  }
1061  }
1062  }
1063 
1064  xmlFreeParserCtxt(ctxt);
1065  return xml;
1066 }
1067 
1076 const char *
1078 {
1079  char *now_s = pcmk__epoch2str(NULL, 0);
1080  const char *result = NULL;
1081 
1083  pcmk__s(now_s, "Could not determine current time"));
1084  free(now_s);
1085  return result;
1086 }
1087 
1093 void
1095 {
1096  char *c;
1097 
1098  for (c = id; *c; ++c) {
1099  /* @TODO Sanitize more comprehensively */
1100  switch (*c) {
1101  case ':':
1102  case '#':
1103  *c = '.';
1104  }
1105  }
1106 }
1107 
1115 void
1116 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1117 {
1118  va_list ap;
1119  int len = 0;
1120  char *id = NULL;
1121 
1122  /* equivalent to crm_strdup_printf() */
1123  va_start(ap, format);
1124  len = vasprintf(&id, format, ap);
1125  va_end(ap);
1126  CRM_ASSERT(len > 0);
1127 
1128  crm_xml_sanitize_id(id);
1129  crm_xml_add(xml, XML_ATTR_ID, id);
1130  free(id);
1131 }
1132 
1145 static int
1146 write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
1147  bool compress, unsigned int *nbytes)
1148 {
1149  int rc = pcmk_rc_ok;
1150  char *buffer = NULL;
1151 
1152  *nbytes = 0;
1153  crm_log_xml_trace(xml_node, "writing");
1154 
1155  buffer = dump_xml_formatted(xml_node);
1156  CRM_CHECK(buffer && strlen(buffer),
1157  crm_log_xml_warn(xml_node, "formatting failed");
1158  rc = pcmk_rc_error;
1159  goto bail);
1160 
1161  if (compress) {
1162  unsigned int in = 0;
1163  BZFILE *bz_file = NULL;
1164 
1165  rc = BZ_OK;
1166  bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1167  if (rc != BZ_OK) {
1168  crm_warn("Not compressing %s: could not prepare file stream: %s "
1169  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1170  } else {
1171  BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1172  if (rc != BZ_OK) {
1173  crm_warn("Not compressing %s: could not compress data: %s "
1174  CRM_XS " bzerror=%d errno=%d",
1175  filename, bz2_strerror(rc), rc, errno);
1176  }
1177  }
1178 
1179  if (rc == BZ_OK) {
1180  BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1181  if (rc != BZ_OK) {
1182  crm_warn("Not compressing %s: could not write compressed data: %s "
1183  CRM_XS " bzerror=%d errno=%d",
1184  filename, bz2_strerror(rc), rc, errno);
1185  *nbytes = 0; // retry without compression
1186  } else {
1187  crm_trace("Compressed XML for %s from %u bytes to %u",
1188  filename, in, *nbytes);
1189  }
1190  }
1191  rc = pcmk_rc_ok; // Either true, or we'll retry without compression
1192  }
1193 
1194  if (*nbytes == 0) {
1195  rc = fprintf(stream, "%s", buffer);
1196  if (rc < 0) {
1197  rc = errno;
1198  crm_perror(LOG_ERR, "writing %s", filename);
1199  } else {
1200  *nbytes = (unsigned int) rc;
1201  rc = pcmk_rc_ok;
1202  }
1203  }
1204 
1205  bail:
1206 
1207  if (fflush(stream) != 0) {
1208  rc = errno;
1209  crm_perror(LOG_ERR, "flushing %s", filename);
1210  }
1211 
1212  /* Don't report error if the file does not support synchronization */
1213  if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1214  rc = errno;
1215  crm_perror(LOG_ERR, "synchronizing %s", filename);
1216  }
1217 
1218  fclose(stream);
1219 
1220  crm_trace("Saved %d bytes to %s as XML", *nbytes, filename);
1221  free(buffer);
1222 
1223  return rc;
1224 }
1225 
1236 int
1237 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
1238 {
1239  FILE *stream = NULL;
1240  unsigned int nbytes = 0;
1241  int rc = pcmk_rc_ok;
1242 
1243  CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
1244  stream = fdopen(fd, "w");
1245  if (stream == NULL) {
1246  return -errno;
1247  }
1248  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1249  if (rc != pcmk_rc_ok) {
1250  return pcmk_rc2legacy(rc);
1251  }
1252  return (int) nbytes;
1253 }
1254 
1264 int
1265 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
1266 {
1267  FILE *stream = NULL;
1268  unsigned int nbytes = 0;
1269  int rc = pcmk_rc_ok;
1270 
1271  CRM_CHECK(xml_node && filename, return -EINVAL);
1272  stream = fopen(filename, "w");
1273  if (stream == NULL) {
1274  return -errno;
1275  }
1276  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1277  if (rc != pcmk_rc_ok) {
1278  return pcmk_rc2legacy(rc);
1279  }
1280  return (int) nbytes;
1281 }
1282 
1283 // Replace a portion of a dynamically allocated string (reallocating memory)
1284 static char *
1285 replace_text(char *text, int start, size_t *length, const char *replace)
1286 {
1287  size_t offset = strlen(replace) - 1; // We have space for 1 char already
1288 
1289  *length += offset;
1290  text = pcmk__realloc(text, *length);
1291 
1292  for (size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1293  text[lpc] = text[lpc - offset];
1294  }
1295 
1296  memcpy(text + start, replace, offset + 1);
1297  return text;
1298 }
1299 
1309 char *
1310 crm_xml_escape(const char *text)
1311 {
1312  size_t length;
1313  char *copy;
1314 
1315  /*
1316  * When xmlCtxtReadDoc() parses &lt; and friends in a
1317  * value, it converts them to their human readable
1318  * form.
1319  *
1320  * If one uses xmlNodeDump() to convert it back to a
1321  * string, all is well, because special characters are
1322  * converted back to their escape sequences.
1323  *
1324  * However xmlNodeDump() is randomly dog slow, even with the same
1325  * input. So we need to replicate the escaping in our custom
1326  * version so that the result can be re-parsed by xmlCtxtReadDoc()
1327  * when necessary.
1328  */
1329 
1330  if (text == NULL) {
1331  return NULL;
1332  }
1333 
1334  length = 1 + strlen(text);
1335  copy = strdup(text);
1336  CRM_ASSERT(copy != NULL);
1337  for (size_t index = 0; index < length; index++) {
1338  if(copy[index] & 0x80 && copy[index+1] & 0x80){
1339  index++;
1340  break;
1341  }
1342  switch (copy[index]) {
1343  case 0:
1344  break;
1345  case '<':
1346  copy = replace_text(copy, index, &length, "&lt;");
1347  break;
1348  case '>':
1349  copy = replace_text(copy, index, &length, "&gt;");
1350  break;
1351  case '"':
1352  copy = replace_text(copy, index, &length, "&quot;");
1353  break;
1354  case '\'':
1355  copy = replace_text(copy, index, &length, "&apos;");
1356  break;
1357  case '&':
1358  copy = replace_text(copy, index, &length, "&amp;");
1359  break;
1360  case '\t':
1361  /* Might as well just expand to a few spaces... */
1362  copy = replace_text(copy, index, &length, " ");
1363  break;
1364  case '\n':
1365  copy = replace_text(copy, index, &length, "\\n");
1366  break;
1367  case '\r':
1368  copy = replace_text(copy, index, &length, "\\r");
1369  break;
1370  default:
1371  /* Check for and replace non-printing characters with their octal equivalent */
1372  if(copy[index] < ' ' || copy[index] > '~') {
1373  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
1374 
1375  copy = replace_text(copy, index, &length, replace);
1376  free(replace);
1377  }
1378  }
1379  }
1380  return copy;
1381 }
1382 
1390 static void
1391 dump_xml_attr(const xmlAttr *attr, GString *buffer)
1392 {
1393  char *p_value = NULL;
1394  const char *p_name = NULL;
1395  xml_node_private_t *nodepriv = NULL;
1396 
1397  if (attr == NULL || attr->children == NULL) {
1398  return;
1399  }
1400 
1401  nodepriv = attr->_private;
1402  if (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1403  return;
1404  }
1405 
1406  p_name = (const char *) attr->name;
1407  p_value = crm_xml_escape((const char *)attr->children->content);
1408  pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"",
1409  NULL);
1410 
1411  free(p_value);
1412 }
1413 
1423 static void
1424 dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
1425  int depth)
1426 {
1427  const char *name = crm_element_name(data);
1428  bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1429  bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
1430  int spaces = pretty? (2 * depth) : 0;
1431 
1432  CRM_ASSERT(name != NULL);
1433 
1434  for (int lpc = 0; lpc < spaces; lpc++) {
1435  g_string_append_c(buffer, ' ');
1436  }
1437 
1438  pcmk__g_strcat(buffer, "<", name, NULL);
1439 
1440  for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
1441  attr = attr->next) {
1442 
1443  if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
1444  dump_xml_attr(attr, buffer);
1445  }
1446  }
1447 
1448  if (data->children == NULL) {
1449  g_string_append(buffer, "/>");
1450 
1451  } else {
1452  g_string_append_c(buffer, '>');
1453  }
1454 
1455  if (pretty) {
1456  g_string_append_c(buffer, '\n');
1457  }
1458 
1459  if (data->children) {
1460  xmlNode *xChild = NULL;
1461  for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1462  pcmk__xml2text(xChild, options, buffer, depth + 1);
1463  }
1464 
1465  for (int lpc = 0; lpc < spaces; lpc++) {
1466  g_string_append_c(buffer, ' ');
1467  }
1468 
1469  pcmk__g_strcat(buffer, "</", name, ">", NULL);
1470 
1471  if (pretty) {
1472  g_string_append_c(buffer, '\n');
1473  }
1474  }
1475 }
1476 
1486 static void
1487 dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer,
1488  int depth)
1489 {
1490  /* @COMPAT: Remove when log_data_element() is removed. There are no internal
1491  * code paths to this, except through the deprecated log_data_element().
1492  */
1493  bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1494  int spaces = pretty? (2 * depth) : 0;
1495 
1496  for (int lpc = 0; lpc < spaces; lpc++) {
1497  g_string_append_c(buffer, ' ');
1498  }
1499 
1500  g_string_append(buffer, (const gchar *) data->content);
1501 
1502  if (pretty) {
1503  g_string_append_c(buffer, '\n');
1504  }
1505 }
1506 
1516 static void
1517 dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer,
1518  int depth)
1519 {
1520  bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1521  int spaces = pretty? (2 * depth) : 0;
1522 
1523  for (int lpc = 0; lpc < spaces; lpc++) {
1524  g_string_append_c(buffer, ' ');
1525  }
1526 
1527  pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
1528  NULL);
1529 
1530  if (pretty) {
1531  g_string_append_c(buffer, '\n');
1532  }
1533 }
1534 
1544 static void
1545 dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
1546  int depth)
1547 {
1548  bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1549  int spaces = pretty? (2 * depth) : 0;
1550 
1551  for (int lpc = 0; lpc < spaces; lpc++) {
1552  g_string_append_c(buffer, ' ');
1553  }
1554 
1555  pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
1556 
1557  if (pretty) {
1558  g_string_append_c(buffer, '\n');
1559  }
1560 }
1561 
1562 #define PCMK__XMLDUMP_STATS 0
1563 
1573 void
1574 pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
1575 {
1576  if (data == NULL) {
1577  crm_trace("Nothing to dump");
1578  return;
1579  }
1580 
1581  CRM_ASSERT(buffer != NULL);
1582  CRM_CHECK(depth >= 0, depth = 0);
1583 
1584  if (pcmk_is_set(options, pcmk__xml_fmt_full)) {
1585  /* libxml's serialization reuse is a good idea, sadly we cannot
1586  apply it for the filtered cases (preceding filtering pass
1587  would preclude further reuse of such in-situ modified XML
1588  in generic context and is likely not a win performance-wise),
1589  and there's also a historically unstable throughput argument
1590  (likely stemming from memory allocation overhead, eventhough
1591  that shall be minimized with defaults preset in crm_xml_init) */
1592 #if (PCMK__XMLDUMP_STATS - 0)
1593  time_t next, new = time(NULL);
1594 #endif
1595  xmlDoc *doc;
1596  xmlOutputBuffer *xml_buffer;
1597 
1598  doc = getDocPtr(data);
1599  /* doc will only be NULL if data is */
1600  CRM_CHECK(doc != NULL, return);
1601 
1602  xml_buffer = xmlAllocOutputBuffer(NULL);
1603  CRM_ASSERT(xml_buffer != NULL);
1604 
1605  /* XXX we could setup custom allocation scheme for the particular
1606  buffer, but it's subsumed with crm_xml_init that needs to
1607  be invoked prior to entering this function as such, since
1608  its other branch vitally depends on it -- what can be done
1609  about this all is to have a facade parsing functions that
1610  would 100% mark entering libxml code for us, since we don't
1611  do anything as crazy as swapping out the binary form of the
1612  parsed tree (but those would need to be strictly used as
1613  opposed to libxml's raw functions) */
1614 
1615  xmlNodeDumpOutput(xml_buffer, doc, data, 0,
1616  pcmk_is_set(options, pcmk__xml_fmt_pretty), NULL);
1617  /* attempt adding final NL - failing shouldn't be fatal here */
1618  (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
1619  if (xml_buffer->buffer != NULL) {
1620  g_string_append(buffer,
1621  (const gchar *) xmlBufContent(xml_buffer->buffer));
1622  }
1623 
1624 #if (PCMK__XMLDUMP_STATS - 0)
1625  next = time(NULL);
1626  if ((now + 1) < next) {
1627  crm_log_xml_trace(data, "Long time");
1628  crm_err("xmlNodeDumpOutput() -> %lld bytes took %ds",
1629  (long long) buffer->len, next - now);
1630  }
1631 #endif
1632 
1633  /* asserted allocation before so there should be something to remove */
1634  (void) xmlOutputBufferClose(xml_buffer);
1635  return;
1636  }
1637 
1638  switch(data->type) {
1639  case XML_ELEMENT_NODE:
1640  /* Handle below */
1641  dump_xml_element(data, options, buffer, depth);
1642  break;
1643  case XML_TEXT_NODE:
1644  if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
1645  /* @COMPAT: Remove when log_data_element() is removed. There are
1646  * no other internal code paths that set pcmk__xml_fmt_text.
1647  * Keep an empty case handler so that we don't log an unhandled
1648  * type warning.
1649  */
1650  dump_xml_text(data, options, buffer, depth);
1651  }
1652  break;
1653  case XML_COMMENT_NODE:
1654  dump_xml_comment(data, options, buffer, depth);
1655  break;
1656  case XML_CDATA_SECTION_NODE:
1657  dump_xml_cdata(data, options, buffer, depth);
1658  break;
1659  default:
1660  crm_warn("Unhandled type: %d", data->type);
1661  break;
1662 
1663  /*
1664  XML_ATTRIBUTE_NODE = 2
1665  XML_ENTITY_REF_NODE = 5
1666  XML_ENTITY_NODE = 6
1667  XML_PI_NODE = 7
1668  XML_DOCUMENT_NODE = 9
1669  XML_DOCUMENT_TYPE_NODE = 10
1670  XML_DOCUMENT_FRAG_NODE = 11
1671  XML_NOTATION_NODE = 12
1672  XML_HTML_DOCUMENT_NODE = 13
1673  XML_DTD_NODE = 14
1674  XML_ELEMENT_DECL = 15
1675  XML_ATTRIBUTE_DECL = 16
1676  XML_ENTITY_DECL = 17
1677  XML_NAMESPACE_DECL = 18
1678  XML_XINCLUDE_START = 19
1679  XML_XINCLUDE_END = 20
1680  XML_DOCB_DOCUMENT_NODE = 21
1681  */
1682  }
1683 }
1684 
1685 char *
1686 dump_xml_formatted_with_text(xmlNode * an_xml_node)
1687 {
1688  char *buffer = NULL;
1689  GString *g_buffer = g_string_sized_new(1024);
1690 
1692  g_buffer, 0);
1693 
1694  pcmk__str_update(&buffer, g_buffer->str);
1695  g_string_free(g_buffer, TRUE);
1696  return buffer;
1697 }
1698 
1699 char *
1700 dump_xml_formatted(xmlNode * an_xml_node)
1701 {
1702  char *buffer = NULL;
1703  GString *g_buffer = g_string_sized_new(1024);
1704 
1705  pcmk__xml2text(an_xml_node, pcmk__xml_fmt_pretty, g_buffer, 0);
1706 
1707  pcmk__str_update(&buffer, g_buffer->str);
1708  g_string_free(g_buffer, TRUE);
1709  return buffer;
1710 }
1711 
1712 char *
1713 dump_xml_unformatted(xmlNode * an_xml_node)
1714 {
1715  char *buffer = NULL;
1716  GString *g_buffer = g_string_sized_new(1024);
1717 
1718  pcmk__xml2text(an_xml_node, 0, g_buffer, 0);
1719 
1720  pcmk__str_update(&buffer, g_buffer->str);
1721  g_string_free(g_buffer, TRUE);
1722  return buffer;
1723 }
1724 
1725 gboolean
1726 xml_has_children(const xmlNode * xml_root)
1727 {
1728  if (xml_root != NULL && xml_root->children != NULL) {
1729  return TRUE;
1730  }
1731  return FALSE;
1732 }
1733 
1734 void
1735 xml_remove_prop(xmlNode * obj, const char *name)
1736 {
1737  if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
1738  crm_trace("Cannot remove %s from %s", name, obj->name);
1739 
1740  } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
1741  /* Leave in place (marked for removal) until after the diff is calculated */
1742  xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
1743  xml_node_private_t *nodepriv = attr->_private;
1744 
1745  set_parent_flag(obj, pcmk__xf_dirty);
1747  } else {
1748  xmlUnsetProp(obj, (pcmkXmlStr) name);
1749  }
1750 }
1751 
1752 void
1753 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
1754 {
1755  char *f = NULL;
1756 
1757  if (filename == NULL) {
1758  char *uuid = crm_generate_uuid();
1759 
1760  f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
1761  filename = f;
1762  free(uuid);
1763  }
1764 
1765  crm_info("Saving %s to %s", desc, filename);
1766  write_xml_file(xml, filename, FALSE);
1767  free(f);
1768 }
1769 
1777 static void
1778 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
1779 {
1780  for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
1781  pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
1782  }
1783 }
1784 
1799 static void
1800 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
1801  const char *old_value)
1802 {
1803  xml_doc_private_t *docpriv = new_xml->doc->_private;
1804  xmlAttr *attr = NULL;
1805  xml_node_private_t *nodepriv;
1806 
1807  // Prevent the dirty flag being set recursively upwards
1809 
1810  // Restore the old value (and the tracking flag)
1811  attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1813 
1814  // Reset flags (so the attribute doesn't appear as newly created)
1815  nodepriv = attr->_private;
1816  nodepriv->flags = 0;
1817 
1818  // Check ACLs and mark restored value for later removal
1819  xml_remove_prop(new_xml, attr_name);
1820 
1821  crm_trace("XML attribute %s=%s was removed from %s",
1822  attr_name, old_value, element);
1823 }
1824 
1825 /*
1826  * \internal
1827  * \brief Check ACLs for a changed XML attribute
1828  */
1829 static void
1830 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
1831  const char *old_value)
1832 {
1833  char *vcopy = crm_element_value_copy(new_xml, attr_name);
1834 
1835  crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
1836  attr_name, old_value, vcopy, element);
1837 
1838  // Restore the original value
1839  xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1840 
1841  // Change it back to the new value, to check ACLs
1842  crm_xml_add(new_xml, attr_name, vcopy);
1843  free(vcopy);
1844 }
1845 
1857 static void
1858 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
1859  xmlAttr *new_attr, int p_old, int p_new)
1860 {
1861  xml_node_private_t *nodepriv = new_attr->_private;
1862 
1863  crm_trace("XML attribute %s moved from position %d to %d in %s",
1864  old_attr->name, p_old, p_new, element);
1865 
1866  // Mark document, element, and all element's parents as changed
1867  mark_xml_node_dirty(new_xml);
1868 
1869  // Mark attribute as changed
1871 
1872  nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1874 }
1875 
1883 static void
1884 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1885 {
1886  xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1887 
1888  while (attr_iter != NULL) {
1889  xmlAttr *old_attr = attr_iter;
1890  xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1891  const char *name = (const char *) attr_iter->name;
1892  const char *old_value = crm_element_value(old_xml, name);
1893 
1894  attr_iter = attr_iter->next;
1895  if (new_attr == NULL) {
1896  mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
1897  old_value);
1898 
1899  } else {
1900  xml_node_private_t *nodepriv = new_attr->_private;
1901  int new_pos = pcmk__xml_position((xmlNode*) new_attr,
1902  pcmk__xf_skip);
1903  int old_pos = pcmk__xml_position((xmlNode*) old_attr,
1904  pcmk__xf_skip);
1905  const char *new_value = crm_element_value(new_xml, name);
1906 
1907  // This attribute isn't new
1909 
1910  if (strcmp(new_value, old_value) != 0) {
1911  mark_attr_changed(new_xml, (const char *) old_xml->name, name,
1912  old_value);
1913 
1914  } else if ((old_pos != new_pos)
1915  && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
1916  mark_attr_moved(new_xml, (const char *) old_xml->name,
1917  old_attr, new_attr, old_pos, new_pos);
1918  }
1919  }
1920  }
1921 }
1922 
1932 static void
1933 mark_created_attrs(xmlNode *new_xml)
1934 {
1935  xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1936 
1937  while (attr_iter != NULL) {
1938  xmlAttr *new_attr = attr_iter;
1939  xml_node_private_t *nodepriv = attr_iter->_private;
1940 
1941  attr_iter = attr_iter->next;
1942  if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1943  const char *attr_name = (const char *) new_attr->name;
1944 
1945  crm_trace("Created new attribute %s=%s in %s",
1946  attr_name, crm_element_value(new_xml, attr_name),
1947  new_xml->name);
1948 
1949  /* Check ACLs (we can't use the remove-then-create trick because it
1950  * would modify the attribute position).
1951  */
1952  if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
1953  pcmk__mark_xml_attr_dirty(new_attr);
1954  } else {
1955  // Creation was not allowed, so remove the attribute
1956  xmlUnsetProp(new_xml, new_attr->name);
1957  }
1958  }
1959  }
1960 }
1961 
1969 static void
1970 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1971 {
1972  set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
1973  xml_diff_old_attrs(old_xml, new_xml);
1974  mark_created_attrs(new_xml);
1975 }
1976 
1989 static void
1990 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1991 {
1992  // Re-create the child element so we can check ACLs
1993  xmlNode *candidate = add_node_copy(new_parent, old_child);
1994 
1995  // Clear flags on new child and its children
1996  reset_xml_node_flags(candidate);
1997 
1998  // Check whether ACLs allow the deletion
1999  pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
2000 
2001  // Remove the child again (which will track it in document's deleted_objs)
2002  free_xml_with_position(candidate,
2003  pcmk__xml_position(old_child, pcmk__xf_skip));
2004 
2005  if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
2006  pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
2007  pcmk__xf_skip);
2008  }
2009 }
2010 
2011 static void
2012 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2013  int p_old, int p_new)
2014 {
2015  xml_node_private_t *nodepriv = new_child->_private;
2016 
2017  crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
2018  new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
2019  p_old, p_new, new_parent->name);
2020  mark_xml_node_dirty(new_parent);
2022 
2023  if (p_old > p_new) {
2024  nodepriv = old_child->_private;
2025  } else {
2026  nodepriv = new_child->_private;
2027  }
2029 }
2030 
2031 // Given original and new XML, mark new XML portions that have changed
2032 static void
2033 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
2034 {
2035  xmlNode *cIter = NULL;
2036  xml_node_private_t *nodepriv = NULL;
2037 
2038  CRM_CHECK(new_xml != NULL, return);
2039  if (old_xml == NULL) {
2040  pcmk__mark_xml_created(new_xml);
2041  pcmk__apply_creation_acl(new_xml, check_top);
2042  return;
2043  }
2044 
2045  nodepriv = new_xml->_private;
2046  CRM_CHECK(nodepriv != NULL, return);
2047 
2048  if(nodepriv->flags & pcmk__xf_processed) {
2049  /* Avoid re-comparing nodes */
2050  return;
2051  }
2053 
2054  xml_diff_attrs(old_xml, new_xml);
2055 
2056  // Check for differences in the original children
2057  for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2058  xmlNode *old_child = cIter;
2059  xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true);
2060 
2061  cIter = pcmk__xml_next(cIter);
2062  if(new_child) {
2063  mark_xml_changes(old_child, new_child, TRUE);
2064 
2065  } else {
2066  mark_child_deleted(old_child, new_xml);
2067  }
2068  }
2069 
2070  // Check for moved or created children
2071  for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2072  xmlNode *new_child = cIter;
2073  xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true);
2074 
2075  cIter = pcmk__xml_next(cIter);
2076  if(old_child == NULL) {
2077  // This is a newly created child
2078  nodepriv = new_child->_private;
2080  mark_xml_changes(old_child, new_child, TRUE);
2081 
2082  } else {
2083  /* Check for movement, we already checked for differences */
2084  int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
2085  int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
2086 
2087  if(p_old != p_new) {
2088  mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2089  }
2090  }
2091  }
2092 }
2093 
2094 void
2095 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
2096 {
2098  xml_calculate_changes(old_xml, new_xml);
2099 }
2100 
2101 // Called functions may set the \p pcmk__xf_skip flag on parts of \p old_xml
2102 void
2103 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
2104 {
2105  CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
2106  return);
2107  CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
2108 
2109  if(xml_tracking_changes(new_xml) == FALSE) {
2110  xml_track_changes(new_xml, NULL, NULL, FALSE);
2111  }
2112 
2113  mark_xml_changes(old_xml, new_xml, FALSE);
2114 }
2115 
2116 gboolean
2117 can_prune_leaf(xmlNode * xml_node)
2118 {
2119  xmlNode *cIter = NULL;
2120  gboolean can_prune = TRUE;
2121  const char *name = crm_element_name(xml_node);
2122 
2125  return FALSE;
2126  }
2127 
2128  for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2129  const char *p_name = (const char *) a->name;
2130 
2131  if (strcmp(p_name, XML_ATTR_ID) == 0) {
2132  continue;
2133  }
2134  can_prune = FALSE;
2135  }
2136 
2137  cIter = pcmk__xml_first_child(xml_node);
2138  while (cIter) {
2139  xmlNode *child = cIter;
2140 
2141  cIter = pcmk__xml_next(cIter);
2142  if (can_prune_leaf(child)) {
2143  free_xml(child);
2144  } else {
2145  can_prune = FALSE;
2146  }
2147  }
2148  return can_prune;
2149 }
2150 
2159 xmlNode *
2160 pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
2161 {
2162  xmlNode *a_child = NULL;
2163  int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
2164 
2165  CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
2166 
2167  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2168  a_child = pcmk__xml_next(a_child)) {
2169  if (exact) {
2170  int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
2171  xml_node_private_t *nodepriv = a_child->_private;
2172 
2173  if (offset < search_offset) {
2174  continue;
2175 
2176  } else if (offset > search_offset) {
2177  return NULL;
2178  }
2179 
2180  if (pcmk_is_set(nodepriv->flags, pcmk__xf_skip)) {
2181  continue;
2182  }
2183  }
2184 
2185  if (a_child->type == XML_COMMENT_NODE
2186  && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
2187  return a_child;
2188 
2189  } else if (exact) {
2190  return NULL;
2191  }
2192  }
2193 
2194  return NULL;
2195 }
2196 
2208 void
2209 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
2210 {
2211  CRM_CHECK(update != NULL, return);
2212  CRM_CHECK(update->type == XML_COMMENT_NODE, return);
2213 
2214  if (target == NULL) {
2215  target = pcmk__xc_match(parent, update, false);
2216  }
2217 
2218  if (target == NULL) {
2219  add_node_copy(parent, update);
2220 
2221  } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
2222  xmlFree(target->content);
2223  target->content = xmlStrdup(update->content);
2224  }
2225 }
2226 
2239 void
2240 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
2241  bool as_diff)
2242 {
2243  xmlNode *a_child = NULL;
2244  const char *object_name = NULL,
2245  *object_href = NULL,
2246  *object_href_val = NULL;
2247 
2248 #if XML_PARSER_DEBUG
2249  crm_log_xml_trace(update, "update:");
2250  crm_log_xml_trace(target, "target:");
2251 #endif
2252 
2253  CRM_CHECK(update != NULL, return);
2254 
2255  if (update->type == XML_COMMENT_NODE) {
2256  pcmk__xc_update(parent, target, update);
2257  return;
2258  }
2259 
2260  object_name = crm_element_name(update);
2261  object_href_val = ID(update);
2262  if (object_href_val != NULL) {
2263  object_href = XML_ATTR_ID;
2264  } else {
2265  object_href_val = crm_element_value(update, XML_ATTR_IDREF);
2266  object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
2267  }
2268 
2269  CRM_CHECK(object_name != NULL, return);
2270  CRM_CHECK(target != NULL || parent != NULL, return);
2271 
2272  if (target == NULL) {
2273  target = pcmk__xe_match(parent, object_name,
2274  object_href, object_href_val);
2275  }
2276 
2277  if (target == NULL) {
2278  target = create_xml_node(parent, object_name);
2279  CRM_CHECK(target != NULL, return);
2280 #if XML_PARSER_DEBUG
2281  crm_trace("Added <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2282  object_href ? " " : "",
2283  object_href ? object_href : "",
2284  object_href ? "=" : "",
2285  object_href ? object_href_val : "");
2286 
2287  } else {
2288  crm_trace("Found node <%s%s%s%s%s/> to update",
2289  pcmk__s(object_name, "<null>"),
2290  object_href ? " " : "",
2291  object_href ? object_href : "",
2292  object_href ? "=" : "",
2293  object_href ? object_href_val : "");
2294 #endif
2295  }
2296 
2297  CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2298  pcmk__str_casei),
2299  return);
2300 
2301  if (as_diff == FALSE) {
2302  /* So that expand_plus_plus() gets called */
2303  copy_in_properties(target, update);
2304 
2305  } else {
2306  /* No need for expand_plus_plus(), just raw speed */
2307  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2308  a = a->next) {
2309  const char *p_value = pcmk__xml_attr_value(a);
2310 
2311  /* Remove it first so the ordering of the update is preserved */
2312  xmlUnsetProp(target, a->name);
2313  xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
2314  }
2315  }
2316 
2317  for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2318  a_child = pcmk__xml_next(a_child)) {
2319 #if XML_PARSER_DEBUG
2320  crm_trace("Updating child <%s%s%s%s%s/>",
2321  pcmk__s(object_name, "<null>"),
2322  object_href ? " " : "",
2323  object_href ? object_href : "",
2324  object_href ? "=" : "",
2325  object_href ? object_href_val : "");
2326 #endif
2327  pcmk__xml_update(target, NULL, a_child, as_diff);
2328  }
2329 
2330 #if XML_PARSER_DEBUG
2331  crm_trace("Finished with <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2332  object_href ? " " : "",
2333  object_href ? object_href : "",
2334  object_href ? "=" : "",
2335  object_href ? object_href_val : "");
2336 #endif
2337 }
2338 
2339 gboolean
2340 update_xml_child(xmlNode * child, xmlNode * to_update)
2341 {
2342  gboolean can_update = TRUE;
2343  xmlNode *child_of_child = NULL;
2344 
2345  CRM_CHECK(child != NULL, return FALSE);
2346  CRM_CHECK(to_update != NULL, return FALSE);
2347 
2348  if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
2349  can_update = FALSE;
2350 
2351  } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
2352  can_update = FALSE;
2353 
2354  } else if (can_update) {
2355 #if XML_PARSER_DEBUG
2356  crm_log_xml_trace(child, "Update match found...");
2357 #endif
2358  pcmk__xml_update(NULL, child, to_update, false);
2359  }
2360 
2361  for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2362  child_of_child = pcmk__xml_next(child_of_child)) {
2363  /* only update the first one */
2364  if (can_update) {
2365  break;
2366  }
2367  can_update = update_xml_child(child_of_child, to_update);
2368  }
2369 
2370  return can_update;
2371 }
2372 
2373 int
2374 find_xml_children(xmlNode ** children, xmlNode * root,
2375  const char *tag, const char *field, const char *value, gboolean search_matches)
2376 {
2377  int match_found = 0;
2378 
2379  CRM_CHECK(root != NULL, return FALSE);
2380  CRM_CHECK(children != NULL, return FALSE);
2381 
2382  if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
2383 
2384  } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
2385 
2386  } else {
2387  if (*children == NULL) {
2388  *children = create_xml_node(NULL, __func__);
2389  }
2390  add_node_copy(*children, root);
2391  match_found = 1;
2392  }
2393 
2394  if (search_matches || match_found == 0) {
2395  xmlNode *child = NULL;
2396 
2397  for (child = pcmk__xml_first_child(root); child != NULL;
2398  child = pcmk__xml_next(child)) {
2399  match_found += find_xml_children(children, child, tag, field, value, search_matches);
2400  }
2401  }
2402 
2403  return match_found;
2404 }
2405 
2406 gboolean
2407 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2408 {
2409  gboolean can_delete = FALSE;
2410  xmlNode *child_of_child = NULL;
2411 
2412  const char *up_id = NULL;
2413  const char *child_id = NULL;
2414  const char *right_val = NULL;
2415 
2416  CRM_CHECK(child != NULL, return FALSE);
2417  CRM_CHECK(update != NULL, return FALSE);
2418 
2419  up_id = ID(update);
2420  child_id = ID(child);
2421 
2422  if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2423  can_delete = TRUE;
2424  }
2425  if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
2426  can_delete = FALSE;
2427  }
2428  if (can_delete && delete_only) {
2429  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2430  a = a->next) {
2431  const char *p_name = (const char *) a->name;
2432  const char *p_value = pcmk__xml_attr_value(a);
2433 
2434  right_val = crm_element_value(child, p_name);
2435  if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) {
2436  can_delete = FALSE;
2437  }
2438  }
2439  }
2440 
2441  if (can_delete && parent != NULL) {
2442  crm_log_xml_trace(child, "Delete match found...");
2443  if (delete_only || update == NULL) {
2444  free_xml(child);
2445 
2446  } else {
2447  xmlNode *tmp = copy_xml(update);
2448  xmlDoc *doc = tmp->doc;
2449  xmlNode *old = NULL;
2450 
2451  xml_accept_changes(tmp);
2452  old = xmlReplaceNode(child, tmp);
2453 
2454  if(xml_tracking_changes(tmp)) {
2455  /* Replaced sections may have included relevant ACLs */
2456  pcmk__apply_acl(tmp);
2457  }
2458 
2459  xml_calculate_changes(old, tmp);
2460  xmlDocSetRootElement(doc, old);
2461  free_xml(old);
2462  }
2463  child = NULL;
2464  return TRUE;
2465 
2466  } else if (can_delete) {
2467  crm_log_xml_debug(child, "Cannot delete the search root");
2468  can_delete = FALSE;
2469  }
2470 
2471  child_of_child = pcmk__xml_first_child(child);
2472  while (child_of_child) {
2473  xmlNode *next = pcmk__xml_next(child_of_child);
2474 
2475  can_delete = replace_xml_child(child, child_of_child, update, delete_only);
2476 
2477  /* only delete the first one */
2478  if (can_delete) {
2479  child_of_child = NULL;
2480  } else {
2481  child_of_child = next;
2482  }
2483  }
2484 
2485  return can_delete;
2486 }
2487 
2488 xmlNode *
2489 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2490 {
2491  xmlNode *child = NULL;
2492  GSList *nvpairs = NULL;
2493  xmlNode *result = NULL;
2494  const char *name = NULL;
2495 
2496  CRM_CHECK(input != NULL, return NULL);
2497 
2498  name = crm_element_name(input);
2499  CRM_CHECK(name != NULL, return NULL);
2500 
2502  nvpairs = pcmk_xml_attrs2nvpairs(input);
2503  nvpairs = pcmk_sort_nvpairs(nvpairs);
2504  pcmk_nvpairs2xml_attrs(nvpairs, result);
2505  pcmk_free_nvpairs(nvpairs);
2506 
2507  for (child = pcmk__xml_first_child(input); child != NULL;
2508  child = pcmk__xml_next(child)) {
2509 
2510  if (recursive) {
2511  sorted_xml(child, result, recursive);
2512  } else {
2513  add_node_copy(result, child);
2514  }
2515  }
2516 
2517  return result;
2518 }
2519 
2520 xmlNode *
2521 first_named_child(const xmlNode *parent, const char *name)
2522 {
2523  xmlNode *match = NULL;
2524 
2525  for (match = pcmk__xe_first_child(parent); match != NULL;
2526  match = pcmk__xe_next(match)) {
2527  /*
2528  * name == NULL gives first child regardless of name; this is
2529  * semantically incorrect in this function, but may be necessary
2530  * due to prior use of xml_child_iter_filter
2531  */
2532  if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) {
2533  return match;
2534  }
2535  }
2536  return NULL;
2537 }
2538 
2546 xmlNode *
2547 crm_next_same_xml(const xmlNode *sibling)
2548 {
2549  xmlNode *match = pcmk__xe_next(sibling);
2550  const char *name = crm_element_name(sibling);
2551 
2552  while (match != NULL) {
2553  if (!strcmp(crm_element_name(match), name)) {
2554  return match;
2555  }
2556  match = pcmk__xe_next(match);
2557  }
2558  return NULL;
2559 }
2560 
2561 void
2563 {
2564  static bool init = true;
2565 
2566  if(init) {
2567  init = false;
2568  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
2569  * pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
2570  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
2571  * less than 1 second.
2572  */
2573  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2574 
2575  /* Populate and free the _private field when nodes are created and destroyed */
2576  xmlDeregisterNodeDefault(free_private_data);
2577  xmlRegisterNodeDefault(new_private_data);
2578 
2579  crm_schema_init();
2580  }
2581 }
2582 
2583 void
2585 {
2587  xmlCleanupParser();
2588 }
2589 
2590 #define XPATH_MAX 512
2591 
2592 xmlNode *
2593 expand_idref(xmlNode * input, xmlNode * top)
2594 {
2595  const char *tag = NULL;
2596  const char *ref = NULL;
2597  xmlNode *result = input;
2598 
2599  if (result == NULL) {
2600  return NULL;
2601 
2602  } else if (top == NULL) {
2603  top = input;
2604  }
2605 
2606  tag = crm_element_name(result);
2608 
2609  if (ref != NULL) {
2610  char *xpath_string = crm_strdup_printf("//%s[@" XML_ATTR_ID "='%s']",
2611  tag, ref);
2612 
2613  result = get_xpath_object(xpath_string, top, LOG_ERR);
2614  if (result == NULL) {
2615  char *nodePath = (char *)xmlGetNodePath(top);
2616 
2617  crm_err("No match for %s found in %s: Invalid configuration",
2618  xpath_string, pcmk__s(nodePath, "unrecognizable path"));
2619  free(nodePath);
2620  }
2621  free(xpath_string);
2622  }
2623  return result;
2624 }
2625 
2626 char *
2628 {
2629  static const char *base = NULL;
2630  char *ret = NULL;
2631 
2632  if (base == NULL) {
2633  base = getenv("PCMK_schema_directory");
2634  }
2635  if (pcmk__str_empty(base)) {
2636  base = CRM_SCHEMA_DIRECTORY;
2637  }
2638 
2639  switch (ns) {
2642  ret = strdup(base);
2643  break;
2646  ret = crm_strdup_printf("%s/base", base);
2647  break;
2648  default:
2649  crm_err("XML artefact family specified as %u not recognized", ns);
2650  }
2651  return ret;
2652 }
2653 
2654 char *
2655 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2656 {
2657  char *base = pcmk__xml_artefact_root(ns), *ret = NULL;
2658 
2659  switch (ns) {
2662  ret = crm_strdup_printf("%s/%s.rng", base, filespec);
2663  break;
2666  ret = crm_strdup_printf("%s/%s.xsl", base, filespec);
2667  break;
2668  default:
2669  crm_err("XML artefact family specified as %u not recognized", ns);
2670  }
2671  free(base);
2672 
2673  return ret;
2674 }
2675 
2676 void
2677 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2678 {
2679  while (true) {
2680  const char *name, *value;
2681 
2682  name = va_arg(pairs, const char *);
2683  if (name == NULL) {
2684  return;
2685  }
2686 
2687  value = va_arg(pairs, const char *);
2688  if (value != NULL) {
2689  crm_xml_add(node, name, value);
2690  }
2691  }
2692 }
2693 
2694 void
2695 pcmk__xe_set_props(xmlNodePtr node, ...)
2696 {
2697  va_list pairs;
2698  va_start(pairs, node);
2699  pcmk__xe_set_propv(node, pairs);
2700  va_end(pairs);
2701 }
2702 
2703 int
2704 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
2705  int (*handler)(xmlNode *xml, void *userdata),
2706  void *userdata)
2707 {
2708  xmlNode *children = (xml? xml->children : NULL);
2709 
2710  CRM_ASSERT(handler != NULL);
2711 
2712  for (xmlNode *node = children; node != NULL; node = node->next) {
2713  if (node->type == XML_ELEMENT_NODE &&
2714  pcmk__str_eq(child_element_name, (const char *) node->name, pcmk__str_null_matches)) {
2715  int rc = handler(node, userdata);
2716 
2717  if (rc != pcmk_rc_ok) {
2718  return rc;
2719  }
2720  }
2721  }
2722 
2723  return pcmk_rc_ok;
2724 }
2725 
2726 // Deprecated functions kept only for backward API compatibility
2727 // LCOV_EXCL_START
2728 
2729 #include <crm/common/xml_compat.h>
2730 
2731 xmlNode *
2732 find_entity(xmlNode *parent, const char *node_name, const char *id)
2733 {
2734  return pcmk__xe_match(parent, node_name,
2735  ((id == NULL)? id : XML_ATTR_ID), id);
2736 }
2737 
2738 void
2740 {
2741  free_xml(data);
2742 }
2743 
2744 int
2745 add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
2746 {
2747  add_node_copy(parent, child);
2748  free_xml(child);
2749  return 1;
2750 }
2751 
2752 // LCOV_EXCL_STOP
2753 // End deprecated API
#define LOG_TRACE
Definition: logging.h:37
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:235
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml.c:2704
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:48
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2095
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:45
const char * bz2_strerror(int rc)
Definition: results.c:841
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:99
char * crm_generate_uuid(void)
Definition: utils.c:509
#define pcmk__if_tracing(if_action, else_action)
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:533
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:933
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition: xml.c:270
G_GNUC_INTERNAL void pcmk__log_xmllib_err(void *ctx, const char *fmt,...) G_GNUC_PRINTF(2
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2103
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:398
Exclude certain XML attributes (for calculating digests)
Definition: xml_internal.h:133
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:1094
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:1237
void fix_plus_plus_recursive(xmlNode *target)
Parse integer assignment statements on this node and all its child nodes.
Definition: xml.c:514
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition: xml.c:2562
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2521
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:302
void pcmk_free_xml_subtree(xmlNode *xml)
Definition: xml.c:736
#define XML_ATTR_IDREF
Definition: msg_xml.h:149
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml.c:2695
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition: acl.c:566
xmlNode * stdin2xml(void)
Definition: xml.c:892
#define XML_DOC_PRIVATE_MAGIC
Definition: xml.c:142
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:219
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2547
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:214
Include indentation and newlines.
Definition: xml_internal.h:136
Deprecated Pacemaker XML API.
xmlNode * filename2xml(const char *filename)
Definition: xml.c:1007
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:2374
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:548
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
Definition: xml.c:2240
int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition: xml.c:309
xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
Definition: xml.c:363
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition: xml.c:2627
#define crm_warn(fmt, args...)
Definition: logging.h:378
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml.c:2677
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:979
xmlNode * copy_xml(xmlNode *src)
Definition: xml.c:819
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2593
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:693
#define XML_ATTR_ID
Definition: msg_xml.h:147
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:496
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:677
void free_xml(xmlNode *child)
Definition: xml.c:813
#define crm_trace(fmt, args...)
Definition: logging.h:383
#define XML_NODE_PRIVATE_MAGIC
Definition: xml.c:143
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1217
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
Include full XML subtree (with any text), using libxml serialization.
Definition: xml_internal.h:139
#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:390
xmlNode * pcmk__xe_match(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:454
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:1753
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Definition: xml.c:77
void pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
Definition: xml.c:1574
void pcmk__str_update(char **str, const char *value)
Definition: strings.c:1193
Wrappers for and extensions to libxml2.
#define crm_log_xml_warn(xml, text)
Definition: logging.h:387
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition: xml.c:1116
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:229
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition: xml.c:2655
void crm_xml_cleanup(void)
Definition: xml.c:2584
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition: xml.c:663
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:647
char * dump_xml_formatted(xmlNode *an_xml_node)
Definition: xml.c:1700
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition: acl.c:282
xmlNode * string2xml(const char *input)
Definition: xml.c:831
const xmlChar * pcmkXmlStr
Definition: xml.h:50
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
Definition: xml.c:1686
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
Definition: digest.c:234
xml_private_flags
Definition: xml_internal.h:365
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:1713
const char * pcmk__get_tmpdir(void)
Definition: io.c:541
void copy_in_properties(xmlNode *target, const xmlNode *src)
Definition: xml.c:481
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1265
#define PCMK__BUFFER_SIZE
pcmk__xml_artefact_ns
Definition: xml_internal.h:194
#define crm_log_xml_err(xml, text)
Definition: logging.h:386
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:319
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:1726
#define crm_err(fmt, args...)
Definition: logging.h:377
#define CRM_ASSERT(expr)
Definition: results.h:42
#define CRM_SCHEMA_DIRECTORY
Definition: config.h:45
char * pcmk__epoch2str(const time_t *source, uint32_t flags)
Definition: iso8601.c:1858
#define XML_CIB_ATTR_WRITTEN
Definition: msg_xml.h:144
xmlNode * input
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:650
#define XML_ACL_TAG_ROLE_REFv1
Definition: msg_xml.h:440
void pcmk__mark_xml_created(xmlNode *xml)
Definition: xml.c:114
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:224
void xml_accept_changes(xmlNode *xml)
Definition: xml.c:379
void crm_destroy_xml(gpointer data)
Definition: xml.c:2739
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition: nvpair.c:201
Include XML text nodes.
Definition: xml_internal.h:152
void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml.c:132
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition: acl.c:608
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1310
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
Definition: xml.c:714
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2340
GString * pcmk__element_xpath(const xmlNode *xml)
Definition: xpath.c:281
#define XML_CIB_TAG_OBJ_REF
Definition: msg_xml.h:457
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:702
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2489
#define crm_log_xml_trace(xml, text)
Definition: logging.h:391
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:404
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:285
#define XML_ACL_TAG_ROLE_REF
Definition: msg_xml.h:439
#define attr_matches(c, n, v)
Definition: xml.c:437
#define ID(x)
Definition: msg_xml.h:480
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:618
const char * parent
Definition: cib.c:25
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:2745
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2407
void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml.c:2209
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1735
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1077
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:2732
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:380
xmlNode * pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
Definition: xml.c:2160
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:563
gboolean can_prune_leaf(xmlNode *xml_node)
Definition: xml.c:2117
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:292