pacemaker  2.1.4-dc6eb4362
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 #define CHUNK_SIZE 1024
49 
50 bool
51 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
52 {
53  if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
54  return FALSE;
55  } else if (!pcmk_is_set(((xml_private_t *)xml->doc->_private)->flags,
57  return FALSE;
58  } else if (lazy && !pcmk_is_set(((xml_private_t *)xml->doc->_private)->flags,
59  pcmk__xf_lazy)) {
60  return FALSE;
61  }
62  return TRUE;
63 }
64 
65 #define buffer_print(buffer, max, offset, fmt, args...) do { \
66  int rc = (max); \
67  if(buffer) { \
68  rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \
69  } \
70  if(buffer && rc < 0) { \
71  crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \
72  (buffer)[(offset)] = 0; \
73  break; \
74  } else if(rc >= ((max) - (offset))) { \
75  char *tmp = NULL; \
76  (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \
77  tmp = pcmk__realloc((buffer), (max)); \
78  CRM_ASSERT(tmp); \
79  (buffer) = tmp; \
80  } else { \
81  offset += rc; \
82  break; \
83  } \
84  } while(1);
85 
86 static void
87 insert_prefix(int options, char **buffer, int *offset, int *max, int depth)
88 {
89  if (options & xml_log_option_formatted) {
90  size_t spaces = 2 * depth;
91 
92  if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
93  (*max) = QB_MAX(CHUNK_SIZE, (*max) * 2);
94  (*buffer) = pcmk__realloc((*buffer), (*max));
95  }
96  memset((*buffer) + (*offset), ' ', spaces);
97  (*offset) += spaces;
98  }
99 }
100 
101 static void
102 set_parent_flag(xmlNode *xml, long flag)
103 {
104 
105  for(; xml; xml = xml->parent) {
106  xml_private_t *p = xml->_private;
107 
108  if(p == NULL) {
109  /* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
110  } else {
111  pcmk__set_xml_flags(p, flag);
112  }
113  }
114 }
115 
116 void
118 {
119 
120  if(xml && xml->doc && xml->doc->_private){
121  /* During calls to xmlDocCopyNode(), xml->doc may be unset */
122  xml_private_t *p = xml->doc->_private;
123 
124  pcmk__set_xml_flags(p, flag);
125  }
126 }
127 
128 // Mark document, element, and all element's parents as changed
129 static void
130 mark_xml_node_dirty(xmlNode *xml)
131 {
133  set_parent_flag(xml, pcmk__xf_dirty);
134 }
135 
136 // Clear flags on XML node and its children
137 static void
138 reset_xml_node_flags(xmlNode *xml)
139 {
140  xmlNode *cIter = NULL;
141  xml_private_t *p = xml->_private;
142 
143  if (p) {
144  p->flags = 0;
145  }
146 
147  for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
148  cIter = pcmk__xml_next(cIter)) {
149  reset_xml_node_flags(cIter);
150  }
151 }
152 
153 // Set xpf_created flag on XML node and any children
154 void
156 {
157  xmlNode *cIter = NULL;
158  xml_private_t *p = xml->_private;
159 
160  if (p && pcmk__tracking_xml_changes(xml, FALSE)) {
161  if (!pcmk_is_set(p->flags, pcmk__xf_created)) {
163  mark_xml_node_dirty(xml);
164  }
165  for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
166  cIter = pcmk__xml_next(cIter)) {
167  pcmk__mark_xml_created(cIter);
168  }
169  }
170 }
171 
172 void
174 {
175  xmlNode *parent = a->parent;
176  xml_private_t *p = NULL;
177 
178  p = a->_private;
181  mark_xml_node_dirty(parent);
182 }
183 
184 #define XML_PRIVATE_MAGIC (long) 0x81726354
185 
186 // Free an XML object previously marked as deleted
187 static void
188 free_deleted_object(void *data)
189 {
190  if(data) {
191  pcmk__deleted_xml_t *deleted_obj = data;
192 
193  free(deleted_obj->path);
194  free(deleted_obj);
195  }
196 }
197 
198 // Free and NULL user, ACLs, and deleted objects in an XML node's private data
199 static void
200 reset_xml_private_data(xml_private_t *p)
201 {
202  if(p) {
204 
205  free(p->user);
206  p->user = NULL;
207 
208  if(p->acls) {
209  pcmk__free_acls(p->acls);
210  p->acls = NULL;
211  }
212 
213  if(p->deleted_objs) {
214  g_list_free_full(p->deleted_objs, free_deleted_object);
215  p->deleted_objs = NULL;
216  }
217  }
218 }
219 
220 // Free all private data associated with an XML node
221 static void
222 free_private_data(xmlNode *node)
223 {
224  /* need to explicitly avoid our custom _private field cleanup when
225  called from internal XSLT cleanup (xsltApplyStylesheetInternal
226  -> xsltFreeTransformContext -> xsltFreeRVTs -> xmlFreeDoc)
227  onto result tree fragments, represented as standalone documents
228  with otherwise infeasible space-prefixed name (xsltInternals.h:
229  XSLT_MARK_RES_TREE_FRAG) and carrying it's own load at _private
230  field -- later assert on the XML_PRIVATE_MAGIC would explode */
231  if (node->type != XML_DOCUMENT_NODE || node->name == NULL
232  || node->name[0] != ' ') {
233  reset_xml_private_data(node->_private);
234  free(node->_private);
235  }
236 }
237 
238 // Allocate and initialize private data for an XML node
239 static void
240 new_private_data(xmlNode *node)
241 {
242  xml_private_t *p = NULL;
243 
244  switch(node->type) {
245  case XML_ELEMENT_NODE:
246  case XML_DOCUMENT_NODE:
247  case XML_ATTRIBUTE_NODE:
248  case XML_COMMENT_NODE:
249  p = calloc(1, sizeof(xml_private_t));
251  /* Flags will be reset if necessary when tracking is enabled */
253  node->_private = p;
254  break;
255  case XML_TEXT_NODE:
256  case XML_DTD_NODE:
257  case XML_CDATA_SECTION_NODE:
258  break;
259  default:
260  /* Ignore */
261  crm_trace("Ignoring %p %d", node, node->type);
262  CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
263  break;
264  }
265 
266  if(p && pcmk__tracking_xml_changes(node, FALSE)) {
267  /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
268  * not hooked up at the point we are called
269  */
270  mark_xml_node_dirty(node);
271  }
272 }
273 
274 void
275 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
276 {
277  xml_accept_changes(xml);
278  crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
280  if(enforce_acls) {
281  if(acl_source == NULL) {
282  acl_source = xml;
283  }
285  pcmk__unpack_acl(acl_source, xml, user);
286  pcmk__apply_acl(xml);
287  }
288 }
289 
290 bool xml_tracking_changes(xmlNode * xml)
291 {
292  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
293  && pcmk_is_set(((xml_private_t *)(xml->doc->_private))->flags,
295 }
296 
297 bool xml_document_dirty(xmlNode *xml)
298 {
299  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
300  && pcmk_is_set(((xml_private_t *)(xml->doc->_private))->flags,
302 }
303 
313 int
314 pcmk__xml_position(xmlNode *xml, enum xml_private_flags ignore_if_set)
315 {
316  int position = 0;
317  xmlNode *cIter = NULL;
318 
319  for(cIter = xml; cIter->prev; cIter = cIter->prev) {
320  xml_private_t *p = ((xmlNode*)cIter->prev)->_private;
321 
322  if (!pcmk_is_set(p->flags, ignore_if_set)) {
323  position++;
324  }
325  }
326 
327  return position;
328 }
329 
330 // This also clears attribute's flags if not marked as deleted
331 static bool
332 marked_as_deleted(xmlAttrPtr a, void *user_data)
333 {
334  xml_private_t *p = a->_private;
335 
337  return true;
338  }
339  p->flags = pcmk__xf_none;
340  return false;
341 }
342 
343 // Remove all attributes marked as deleted from an XML node
344 static void
345 accept_attr_deletions(xmlNode *xml)
346 {
347  // Clear XML node's flags
348  ((xml_private_t *) xml->_private)->flags = pcmk__xf_none;
349 
350  // Remove this XML node's attributes that were marked as deleted
351  pcmk__xe_remove_matching_attrs(xml, marked_as_deleted, NULL);
352 
353  // Recursively do the same for this XML node's children
354  for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
355  cIter = pcmk__xml_next(cIter)) {
356  accept_attr_deletions(cIter);
357  }
358 }
359 
368 xmlNode *
369 pcmk__xml_match(xmlNode *haystack, xmlNode *needle, bool exact)
370 {
371  CRM_CHECK(needle != NULL, return NULL);
372 
373  if (needle->type == XML_COMMENT_NODE) {
374  return pcmk__xc_match(haystack, needle, exact);
375 
376  } else {
377  const char *id = ID(needle);
378  const char *attr = (id == NULL)? NULL : XML_ATTR_ID;
379 
380  return pcmk__xe_match(haystack, crm_element_name(needle), attr, id);
381  }
382 }
383 
384 void
385 xml_log_changes(uint8_t log_level, const char *function, xmlNode * xml)
386 {
387  GList *gIter = NULL;
388  xml_private_t *doc = NULL;
389 
390  if (log_level == LOG_NEVER) {
391  return;
392  }
393 
394  CRM_ASSERT(xml);
395  CRM_ASSERT(xml->doc);
396 
397  doc = xml->doc->_private;
398  if (!pcmk_is_set(doc->flags, pcmk__xf_dirty)) {
399  return;
400  }
401 
402  for(gIter = doc->deleted_objs; gIter; gIter = gIter->next) {
403  pcmk__deleted_xml_t *deleted_obj = gIter->data;
404 
405  if (deleted_obj->position >= 0) {
406  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)",
407  deleted_obj->path, deleted_obj->position);
408 
409  } else {
410  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s",
411  deleted_obj->path);
412  }
413  }
414 
415  log_data_element(log_level, __FILE__, function, __LINE__, "+ ", xml, 0,
417 }
418 
419 void
420 xml_accept_changes(xmlNode * xml)
421 {
422  xmlNode *top = NULL;
423  xml_private_t *doc = NULL;
424 
425  if(xml == NULL) {
426  return;
427  }
428 
429  crm_trace("Accepting changes to %p", xml);
430  doc = xml->doc->_private;
431  top = xmlDocGetRootElement(xml->doc);
432 
433  reset_xml_private_data(xml->doc->_private);
434 
435  if (!pcmk_is_set(doc->flags, pcmk__xf_dirty)) {
436  doc->flags = pcmk__xf_none;
437  return;
438  }
439 
440  doc->flags = pcmk__xf_none;
441  accept_attr_deletions(top);
442 }
443 
444 xmlNode *
445 find_xml_node(xmlNode * root, const char *search_path, gboolean must_find)
446 {
447  xmlNode *a_child = NULL;
448  const char *name = "NULL";
449 
450  if (root != NULL) {
451  name = crm_element_name(root);
452  }
453 
454  if (search_path == NULL) {
455  crm_warn("Will never find <NULL>");
456  return NULL;
457  }
458 
459  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
460  a_child = pcmk__xml_next(a_child)) {
461  if (strcmp((const char *)a_child->name, search_path) == 0) {
462 /* crm_trace("returning node (%s).", crm_element_name(a_child)); */
463  return a_child;
464  }
465  }
466 
467  if (must_find) {
468  crm_warn("Could not find %s in %s.", search_path, name);
469  } else if (root != NULL) {
470  crm_trace("Could not find %s in %s.", search_path, name);
471  } else {
472  crm_trace("Could not find %s in <NULL>.", search_path);
473  }
474 
475  return NULL;
476 }
477 
478 #define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \
479  (v), pcmk__str_none)
480 
494 xmlNode *
495 pcmk__xe_match(xmlNode *parent, const char *node_name,
496  const char *attr_n, const char *attr_v)
497 {
498  /* ensure attr_v specified when attr_n is */
499  CRM_CHECK(attr_n == NULL || attr_v != NULL, return NULL);
500 
501  for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
502  child = pcmk__xml_next(child)) {
503  if (pcmk__str_eq(node_name, (const char *) (child->name),
505  && ((attr_n == NULL) || attr_matches(child, attr_n, attr_v))) {
506  return child;
507  }
508  }
509  crm_trace("XML child node <%s%s%s%s%s> not found in %s",
510  (node_name? node_name : "(any)"),
511  (attr_n? " " : ""),
512  (attr_n? attr_n : ""),
513  (attr_n? "=" : ""),
514  (attr_n? attr_v : ""),
515  crm_element_name(parent));
516  return NULL;
517 }
518 
519 void
520 copy_in_properties(xmlNode * target, xmlNode * src)
521 {
522  if (src == NULL) {
523  crm_warn("No node to copy properties from");
524 
525  } else if (target == NULL) {
526  crm_err("No node to copy properties into");
527 
528  } else {
529  for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
530  const char *p_name = (const char *) a->name;
531  const char *p_value = pcmk__xml_attr_value(a);
532 
533  expand_plus_plus(target, p_name, p_value);
534  }
535  }
536 
537  return;
538 }
539 
540 void
542 {
543  /* TODO: Remove recursion and use xpath searches for value++ */
544  xmlNode *child = NULL;
545 
546  for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
547  const char *p_name = (const char *) a->name;
548  const char *p_value = pcmk__xml_attr_value(a);
549 
550  expand_plus_plus(target, p_name, p_value);
551  }
552  for (child = pcmk__xml_first_child(target); child != NULL;
553  child = pcmk__xml_next(child)) {
555  }
556 }
557 
558 void
559 expand_plus_plus(xmlNode * target, const char *name, const char *value)
560 {
561  int offset = 1;
562  int name_len = 0;
563  int int_value = 0;
564  int value_len = 0;
565 
566  const char *old_value = NULL;
567 
568  if (value == NULL || name == NULL) {
569  return;
570  }
571 
572  old_value = crm_element_value(target, name);
573 
574  if (old_value == NULL) {
575  /* if no previous value, set unexpanded */
576  goto set_unexpanded;
577 
578  } else if (strstr(value, name) != value) {
579  goto set_unexpanded;
580  }
581 
582  name_len = strlen(name);
583  value_len = strlen(value);
584  if (value_len < (name_len + 2)
585  || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
586  goto set_unexpanded;
587  }
588 
589  /* if we are expanding ourselves,
590  * then no previous value was set and leave int_value as 0
591  */
592  if (old_value != value) {
593  int_value = char2score(old_value);
594  }
595 
596  if (value[name_len + 1] != '+') {
597  const char *offset_s = value + (name_len + 2);
598 
599  offset = char2score(offset_s);
600  }
601  int_value += offset;
602 
603  if (int_value > INFINITY) {
604  int_value = (int)INFINITY;
605  }
606 
607  crm_xml_add_int(target, name, int_value);
608  return;
609 
610  set_unexpanded:
611  if (old_value == value) {
612  /* the old value is already set, nothing to do */
613  return;
614  }
615  crm_xml_add(target, name, value);
616  return;
617 }
618 
628 void
630  bool (*match)(xmlAttrPtr, void *),
631  void *user_data)
632 {
633  xmlAttrPtr next = NULL;
634 
635  for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
636  next = a->next; // Grab now because attribute might get removed
637  if ((match == NULL) || match(a, user_data)) {
638  if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
639  crm_trace("ACLs prevent removal of attributes (%s and "
640  "possibly others) from %s element",
641  (const char *) a->name, (const char *) element->name);
642  return; // ACLs apply to element, not particular attributes
643  }
644 
645  if (pcmk__tracking_xml_changes(element, false)) {
646  // Leave (marked for removal) until after diff is calculated
647  set_parent_flag(element, pcmk__xf_dirty);
648  pcmk__set_xml_flags((xml_private_t *) a->_private,
650  } else {
651  xmlRemoveProp(a);
652  }
653  }
654  }
655 }
656 
657 xmlDoc *
658 getDocPtr(xmlNode * node)
659 {
660  xmlDoc *doc = NULL;
661 
662  CRM_CHECK(node != NULL, return NULL);
663 
664  doc = node->doc;
665  if (doc == NULL) {
666  doc = xmlNewDoc((pcmkXmlStr) "1.0");
667  xmlDocSetRootElement(doc, node);
668  xmlSetTreeDoc(node, doc);
669  }
670  return doc;
671 }
672 
673 xmlNode *
674 add_node_copy(xmlNode * parent, xmlNode * src_node)
675 {
676  xmlNode *child = NULL;
677  xmlDoc *doc = getDocPtr(parent);
678 
679  CRM_CHECK(src_node != NULL, return NULL);
680 
681  child = xmlDocCopyNode(src_node, doc, 1);
682  xmlAddChild(parent, child);
683  pcmk__mark_xml_created(child);
684  return child;
685 }
686 
687 int
688 add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child)
689 {
690  add_node_copy(parent, child);
691  free_xml(child);
692  return 1;
693 }
694 
695 xmlNode *
696 create_xml_node(xmlNode * parent, const char *name)
697 {
698  xmlDoc *doc = NULL;
699  xmlNode *node = NULL;
700 
701  if (pcmk__str_empty(name)) {
702  CRM_CHECK(name != NULL && name[0] == 0, return NULL);
703  return NULL;
704  }
705 
706  if (parent == NULL) {
707  doc = xmlNewDoc((pcmkXmlStr) "1.0");
708  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
709  xmlDocSetRootElement(doc, node);
710 
711  } else {
712  doc = getDocPtr(parent);
713  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
714  xmlAddChild(parent, node);
715  }
717  return node;
718 }
719 
720 xmlNode *
721 pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content)
722 {
723  xmlNode *node = create_xml_node(parent, name);
724 
725  if (node != NULL) {
726  xmlNodeSetContent(node, (pcmkXmlStr) content);
727  }
728 
729  return node;
730 }
731 
732 xmlNode *
733 pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id,
734  const char *class_name, const char *text)
735 {
736  xmlNode *node = pcmk_create_xml_text_node(parent, element_name, text);
737 
738  if (class_name != NULL) {
739  crm_xml_add(node, "class", class_name);
740  }
741 
742  if (id != NULL) {
743  crm_xml_add(node, "id", id);
744  }
745 
746  return node;
747 }
748 
754 void
756 {
757  xmlUnlinkNode(xml); // Detaches from parent and siblings
758  xmlFreeNode(xml); // Frees
759 }
760 
761 static void
762 free_xml_with_position(xmlNode * child, int position)
763 {
764  if (child != NULL) {
765  xmlNode *top = NULL;
766  xmlDoc *doc = child->doc;
767  xml_private_t *p = child->_private;
768 
769  if (doc != NULL) {
770  top = xmlDocGetRootElement(doc);
771  }
772 
773  if (doc != NULL && top == child) {
774  /* Free everything */
775  xmlFreeDoc(doc);
776 
777  } else if (pcmk__check_acl(child, NULL, pcmk__xf_acl_write) == FALSE) {
778  int offset = 0;
779  char buffer[PCMK__BUFFER_SIZE];
780 
781  pcmk__element_xpath(NULL, child, buffer, offset, sizeof(buffer));
782  crm_trace("Cannot remove %s %x", buffer, p->flags);
783  return;
784 
785  } else {
786  if (doc && pcmk__tracking_xml_changes(child, FALSE)
788  int offset = 0;
789  char buffer[PCMK__BUFFER_SIZE];
790 
791  if (pcmk__element_xpath(NULL, child, buffer, offset,
792  sizeof(buffer)) > 0) {
793  pcmk__deleted_xml_t *deleted_obj = NULL;
794 
795  crm_trace("Deleting %s %p from %p", buffer, child, doc);
796 
797  deleted_obj = calloc(1, sizeof(pcmk__deleted_xml_t));
798  deleted_obj->path = strdup(buffer);
799 
800  deleted_obj->position = -1;
801  /* Record the "position" only for XML comments for now */
802  if (child->type == XML_COMMENT_NODE) {
803  if (position >= 0) {
804  deleted_obj->position = position;
805 
806  } else {
807  deleted_obj->position = pcmk__xml_position(child,
808  pcmk__xf_skip);
809  }
810  }
811 
812  p = doc->_private;
813  p->deleted_objs = g_list_append(p->deleted_objs, deleted_obj);
815  }
816  }
817  pcmk_free_xml_subtree(child);
818  }
819  }
820 }
821 
822 
823 void
824 free_xml(xmlNode * child)
825 {
826  free_xml_with_position(child, -1);
827 }
828 
829 xmlNode *
830 copy_xml(xmlNode * src)
831 {
832  xmlDoc *doc = xmlNewDoc((pcmkXmlStr) "1.0");
833  xmlNode *copy = xmlDocCopyNode(src, doc, 1);
834 
835  xmlDocSetRootElement(doc, copy);
836  xmlSetTreeDoc(copy, doc);
837  return copy;
838 }
839 
840 static void
841 log_xmllib_err(void *ctx, const char *fmt, ...)
842 G_GNUC_PRINTF(2, 3);
843 
844 // Log an XML library error
845 static void
846 log_xmllib_err(void *ctx, const char *fmt, ...)
847 {
848  va_list ap;
849  static struct qb_log_callsite *xml_error_cs = NULL;
850 
851  if (xml_error_cs == NULL) {
852  xml_error_cs = qb_log_callsite_get(
853  __func__, __FILE__, "xml library error", LOG_TRACE, __LINE__, crm_trace_nonlog);
854  }
855 
856  va_start(ap, fmt);
857  if (xml_error_cs && xml_error_cs->targets) {
858  PCMK__XML_LOG_BASE(LOG_ERR, TRUE,
859  crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, "xml library error",
860  TRUE, TRUE),
861  "XML Error: ", fmt, ap);
862  } else {
863  PCMK__XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
864  }
865  va_end(ap);
866 }
867 
868 xmlNode *
869 string2xml(const char *input)
870 {
871  xmlNode *xml = NULL;
872  xmlDocPtr output = NULL;
873  xmlParserCtxtPtr ctxt = NULL;
874  xmlErrorPtr last_error = NULL;
875 
876  if (input == NULL) {
877  crm_err("Can't parse NULL input");
878  return NULL;
879  }
880 
881  /* create a parser context */
882  ctxt = xmlNewParserCtxt();
883  CRM_CHECK(ctxt != NULL, return NULL);
884 
885  xmlCtxtResetLastError(ctxt);
886  xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
887  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
889  if (output) {
890  xml = xmlDocGetRootElement(output);
891  }
892  last_error = xmlCtxtGetLastError(ctxt);
893  if (last_error && last_error->code != XML_ERR_OK) {
894  /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
895  /*
896  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
897  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
898  */
899  crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
900  last_error->domain, last_error->level, last_error->code, last_error->message);
901 
902  if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
903  CRM_LOG_ASSERT("Cannot parse an empty string");
904 
905  } else if (last_error->code != XML_ERR_DOCUMENT_END) {
906  crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
907  input);
908  if (xml != NULL) {
909  crm_log_xml_err(xml, "Partial");
910  }
911 
912  } else {
913  int len = strlen(input);
914  int lpc = 0;
915 
916  while(lpc < len) {
917  crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
918  lpc += 80;
919  }
920 
921  CRM_LOG_ASSERT("String parsing error");
922  }
923  }
924 
925  xmlFreeParserCtxt(ctxt);
926  return xml;
927 }
928 
929 xmlNode *
931 {
932  size_t data_length = 0;
933  size_t read_chars = 0;
934 
935  char *xml_buffer = NULL;
936  xmlNode *xml_obj = NULL;
937 
938  do {
939  xml_buffer = pcmk__realloc(xml_buffer, data_length + PCMK__BUFFER_SIZE);
940  read_chars = fread(xml_buffer + data_length, 1, PCMK__BUFFER_SIZE,
941  stdin);
942  data_length += read_chars;
943  } while (read_chars == PCMK__BUFFER_SIZE);
944 
945  if (data_length == 0) {
946  crm_warn("No XML supplied on stdin");
947  free(xml_buffer);
948  return NULL;
949  }
950 
951  xml_buffer[data_length] = '\0';
952  xml_obj = string2xml(xml_buffer);
953  free(xml_buffer);
954 
955  crm_log_xml_trace(xml_obj, "Created fragment");
956  return xml_obj;
957 }
958 
959 static char *
960 decompress_file(const char *filename)
961 {
962  char *buffer = NULL;
963  int rc = 0;
964  size_t length = 0, read_len = 0;
965  BZFILE *bz_file = NULL;
966  FILE *input = fopen(filename, "r");
967 
968  if (input == NULL) {
969  crm_perror(LOG_ERR, "Could not open %s for reading", filename);
970  return NULL;
971  }
972 
973  bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
974  if (rc != BZ_OK) {
975  crm_err("Could not prepare to read compressed %s: %s "
976  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
977  BZ2_bzReadClose(&rc, bz_file);
978  fclose(input);
979  return NULL;
980  }
981 
982  rc = BZ_OK;
983  // cppcheck seems not to understand the abort-logic in pcmk__realloc
984  // cppcheck-suppress memleak
985  while (rc == BZ_OK) {
986  buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1);
987  read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
988 
989  crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
990 
991  if (rc == BZ_OK || rc == BZ_STREAM_END) {
992  length += read_len;
993  }
994  }
995 
996  buffer[length] = '\0';
997 
998  if (rc != BZ_STREAM_END) {
999  crm_err("Could not read compressed %s: %s "
1000  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1001  free(buffer);
1002  buffer = NULL;
1003  }
1004 
1005  BZ2_bzReadClose(&rc, bz_file);
1006  fclose(input);
1007  return buffer;
1008 }
1009 
1016 void
1018 {
1019  xmlNode *iter = xml->children;
1020 
1021  while (iter) {
1022  xmlNode *next = iter->next;
1023 
1024  switch (iter->type) {
1025  case XML_TEXT_NODE:
1026  /* Remove it */
1027  pcmk_free_xml_subtree(iter);
1028  break;
1029 
1030  case XML_ELEMENT_NODE:
1031  /* Search it */
1032  pcmk__strip_xml_text(iter);
1033  break;
1034 
1035  default:
1036  /* Leave it */
1037  break;
1038  }
1039 
1040  iter = next;
1041  }
1042 }
1043 
1044 xmlNode *
1045 filename2xml(const char *filename)
1046 {
1047  xmlNode *xml = NULL;
1048  xmlDocPtr output = NULL;
1049  bool uncompressed = true;
1050  xmlParserCtxtPtr ctxt = NULL;
1051  xmlErrorPtr last_error = NULL;
1052 
1053  /* create a parser context */
1054  ctxt = xmlNewParserCtxt();
1055  CRM_CHECK(ctxt != NULL, return NULL);
1056 
1057  xmlCtxtResetLastError(ctxt);
1058  xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1059 
1060  if (filename) {
1061  uncompressed = !pcmk__ends_with_ext(filename, ".bz2");
1062  }
1063 
1064  if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
1065  /* STDIN_FILENO == fileno(stdin) */
1066  output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
1068 
1069  } else if (uncompressed) {
1070  output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
1071 
1072  } else {
1073  char *input = decompress_file(filename);
1074 
1075  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
1077  free(input);
1078  }
1079 
1080  if (output && (xml = xmlDocGetRootElement(output))) {
1081  pcmk__strip_xml_text(xml);
1082  }
1083 
1084  last_error = xmlCtxtGetLastError(ctxt);
1085  if (last_error && last_error->code != XML_ERR_OK) {
1086  /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
1087  /*
1088  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
1089  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
1090  */
1091  crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
1092  last_error->domain, last_error->level, last_error->code, last_error->message);
1093 
1094  if (last_error && last_error->code != XML_ERR_OK) {
1095  crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
1096  if (xml != NULL) {
1097  crm_log_xml_err(xml, "Partial");
1098  }
1099  }
1100  }
1101 
1102  xmlFreeParserCtxt(ctxt);
1103  return xml;
1104 }
1105 
1114 const char *
1116 {
1117  const char *now_str = pcmk__epoch2str(NULL);
1118 
1119  return crm_xml_add(xe, XML_CIB_ATTR_WRITTEN,
1120  now_str ? now_str : "Could not determine current time");
1121 }
1122 
1128 void
1130 {
1131  char *c;
1132 
1133  for (c = id; *c; ++c) {
1134  /* @TODO Sanitize more comprehensively */
1135  switch (*c) {
1136  case ':':
1137  case '#':
1138  *c = '.';
1139  }
1140  }
1141 }
1142 
1150 void
1151 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1152 {
1153  va_list ap;
1154  int len = 0;
1155  char *id = NULL;
1156 
1157  /* equivalent to crm_strdup_printf() */
1158  va_start(ap, format);
1159  len = vasprintf(&id, format, ap);
1160  va_end(ap);
1161  CRM_ASSERT(len > 0);
1162 
1163  crm_xml_sanitize_id(id);
1164  crm_xml_add(xml, XML_ATTR_ID, id);
1165  free(id);
1166 }
1167 
1180 static int
1181 write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
1182  bool compress, unsigned int *nbytes)
1183 {
1184  int rc = pcmk_rc_ok;
1185  char *buffer = NULL;
1186 
1187  *nbytes = 0;
1188  crm_log_xml_trace(xml_node, "writing");
1189 
1190  buffer = dump_xml_formatted(xml_node);
1191  CRM_CHECK(buffer && strlen(buffer),
1192  crm_log_xml_warn(xml_node, "formatting failed");
1193  rc = pcmk_rc_error;
1194  goto bail);
1195 
1196  if (compress) {
1197  unsigned int in = 0;
1198  BZFILE *bz_file = NULL;
1199 
1200  rc = BZ_OK;
1201  bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1202  if (rc != BZ_OK) {
1203  crm_warn("Not compressing %s: could not prepare file stream: %s "
1204  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1205  } else {
1206  BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1207  if (rc != BZ_OK) {
1208  crm_warn("Not compressing %s: could not compress data: %s "
1209  CRM_XS " bzerror=%d errno=%d",
1210  filename, bz2_strerror(rc), rc, errno);
1211  }
1212  }
1213 
1214  if (rc == BZ_OK) {
1215  BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1216  if (rc != BZ_OK) {
1217  crm_warn("Not compressing %s: could not write compressed data: %s "
1218  CRM_XS " bzerror=%d errno=%d",
1219  filename, bz2_strerror(rc), rc, errno);
1220  *nbytes = 0; // retry without compression
1221  } else {
1222  crm_trace("Compressed XML for %s from %u bytes to %u",
1223  filename, in, *nbytes);
1224  }
1225  }
1226  rc = pcmk_rc_ok; // Either true, or we'll retry without compression
1227  }
1228 
1229  if (*nbytes == 0) {
1230  rc = fprintf(stream, "%s", buffer);
1231  if (rc < 0) {
1232  rc = errno;
1233  crm_perror(LOG_ERR, "writing %s", filename);
1234  } else {
1235  *nbytes = (unsigned int) rc;
1236  rc = pcmk_rc_ok;
1237  }
1238  }
1239 
1240  bail:
1241 
1242  if (fflush(stream) != 0) {
1243  rc = errno;
1244  crm_perror(LOG_ERR, "flushing %s", filename);
1245  }
1246 
1247  /* Don't report error if the file does not support synchronization */
1248  if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1249  rc = errno;
1250  crm_perror(LOG_ERR, "synchronizing %s", filename);
1251  }
1252 
1253  fclose(stream);
1254 
1255  crm_trace("Saved %d bytes to %s as XML", *nbytes, filename);
1256  free(buffer);
1257 
1258  return rc;
1259 }
1260 
1271 int
1272 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
1273 {
1274  FILE *stream = NULL;
1275  unsigned int nbytes = 0;
1276  int rc = pcmk_rc_ok;
1277 
1278  CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
1279  stream = fdopen(fd, "w");
1280  if (stream == NULL) {
1281  return -errno;
1282  }
1283  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1284  if (rc != pcmk_rc_ok) {
1285  return pcmk_rc2legacy(rc);
1286  }
1287  return (int) nbytes;
1288 }
1289 
1299 int
1300 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
1301 {
1302  FILE *stream = NULL;
1303  unsigned int nbytes = 0;
1304  int rc = pcmk_rc_ok;
1305 
1306  CRM_CHECK(xml_node && filename, return -EINVAL);
1307  stream = fopen(filename, "w");
1308  if (stream == NULL) {
1309  return -errno;
1310  }
1311  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1312  if (rc != pcmk_rc_ok) {
1313  return pcmk_rc2legacy(rc);
1314  }
1315  return (int) nbytes;
1316 }
1317 
1318 // Replace a portion of a dynamically allocated string (reallocating memory)
1319 static char *
1320 replace_text(char *text, int start, size_t *length, const char *replace)
1321 {
1322  size_t offset = strlen(replace) - 1; // We have space for 1 char already
1323 
1324  *length += offset;
1325  text = pcmk__realloc(text, *length);
1326 
1327  for (size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1328  text[lpc] = text[lpc - offset];
1329  }
1330 
1331  memcpy(text + start, replace, offset + 1);
1332  return text;
1333 }
1334 
1344 char *
1345 crm_xml_escape(const char *text)
1346 {
1347  size_t length;
1348  char *copy;
1349 
1350  /*
1351  * When xmlCtxtReadDoc() parses &lt; and friends in a
1352  * value, it converts them to their human readable
1353  * form.
1354  *
1355  * If one uses xmlNodeDump() to convert it back to a
1356  * string, all is well, because special characters are
1357  * converted back to their escape sequences.
1358  *
1359  * However xmlNodeDump() is randomly dog slow, even with the same
1360  * input. So we need to replicate the escaping in our custom
1361  * version so that the result can be re-parsed by xmlCtxtReadDoc()
1362  * when necessary.
1363  */
1364 
1365  if (text == NULL) {
1366  return NULL;
1367  }
1368 
1369  length = 1 + strlen(text);
1370  copy = strdup(text);
1371  CRM_ASSERT(copy != NULL);
1372  for (size_t index = 0; index < length; index++) {
1373  if(copy[index] & 0x80 && copy[index+1] & 0x80){
1374  index++;
1375  break;
1376  }
1377  switch (copy[index]) {
1378  case 0:
1379  break;
1380  case '<':
1381  copy = replace_text(copy, index, &length, "&lt;");
1382  break;
1383  case '>':
1384  copy = replace_text(copy, index, &length, "&gt;");
1385  break;
1386  case '"':
1387  copy = replace_text(copy, index, &length, "&quot;");
1388  break;
1389  case '\'':
1390  copy = replace_text(copy, index, &length, "&apos;");
1391  break;
1392  case '&':
1393  copy = replace_text(copy, index, &length, "&amp;");
1394  break;
1395  case '\t':
1396  /* Might as well just expand to a few spaces... */
1397  copy = replace_text(copy, index, &length, " ");
1398  break;
1399  case '\n':
1400  copy = replace_text(copy, index, &length, "\\n");
1401  break;
1402  case '\r':
1403  copy = replace_text(copy, index, &length, "\\r");
1404  break;
1405  default:
1406  /* Check for and replace non-printing characters with their octal equivalent */
1407  if(copy[index] < ' ' || copy[index] > '~') {
1408  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
1409 
1410  copy = replace_text(copy, index, &length, replace);
1411  free(replace);
1412  }
1413  }
1414  }
1415  return copy;
1416 }
1417 
1418 static inline void
1419 dump_xml_attr(xmlAttrPtr attr, int options, char **buffer, int *offset, int *max)
1420 {
1421  char *p_value = NULL;
1422  const char *p_name = NULL;
1423  xml_private_t *p = NULL;
1424 
1425  CRM_ASSERT(buffer != NULL);
1426  if (attr == NULL || attr->children == NULL) {
1427  return;
1428  }
1429 
1430  p = attr->_private;
1431  if (p && pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1432  return;
1433  }
1434 
1435  p_name = (const char *)attr->name;
1436  p_value = crm_xml_escape((const char *)attr->children->content);
1437  buffer_print(*buffer, *max, *offset, " %s=\"%s\"",
1438  p_name, crm_str(p_value));
1439  free(p_value);
1440 }
1441 
1442 // Log an XML element (and any children) in a formatted way
1443 void
1444 pcmk__xe_log(int log_level, const char *file, const char *function, int line,
1445  const char *prefix, xmlNode *data, int depth, int options)
1446 {
1447  int max = 0;
1448  int offset = 0;
1449  const char *name = NULL;
1450  const char *hidden = NULL;
1451 
1452  xmlNode *child = NULL;
1453 
1454  if ((data == NULL) || (log_level == LOG_NEVER)) {
1455  return;
1456  }
1457 
1458  name = crm_element_name(data);
1459 
1460  if (pcmk_is_set(options, xml_log_option_open)) {
1461  char *buffer = NULL;
1462 
1463  insert_prefix(options, &buffer, &offset, &max, depth);
1464 
1465  if (data->type == XML_COMMENT_NODE) {
1466  buffer_print(buffer, max, offset, "<!--%s-->", data->content);
1467 
1468  } else {
1469  buffer_print(buffer, max, offset, "<%s", name);
1470 
1471  hidden = crm_element_value(data, "hidden");
1472  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL;
1473  a = a->next) {
1474 
1475  xml_private_t *p = a->_private;
1476  const char *p_name = (const char *) a->name;
1477  const char *p_value = pcmk__xml_attr_value(a);
1478  char *p_copy = NULL;
1479 
1480  if (pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1481  continue;
1482  } else if (pcmk_any_flags_set(options,
1485  && (strcmp(XML_DIFF_MARKER, p_name) == 0)) {
1486  continue;
1487 
1488  } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1489  p_copy = strdup("*****");
1490 
1491  } else {
1492  p_copy = crm_xml_escape(p_value);
1493  }
1494 
1495  buffer_print(buffer, max, offset, " %s=\"%s\"",
1496  p_name, crm_str(p_copy));
1497  free(p_copy);
1498  }
1499 
1500  if(xml_has_children(data) == FALSE) {
1501  buffer_print(buffer, max, offset, "/>");
1502 
1503  } else if (pcmk_is_set(options, xml_log_option_children)) {
1504  buffer_print(buffer, max, offset, ">");
1505 
1506  } else {
1507  buffer_print(buffer, max, offset, "/>");
1508  }
1509  }
1510 
1511  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
1512  free(buffer);
1513  }
1514 
1515  if(data->type == XML_COMMENT_NODE) {
1516  return;
1517 
1518  } else if(xml_has_children(data) == FALSE) {
1519  return;
1520 
1521  } else if (pcmk_is_set(options, xml_log_option_children)) {
1522  offset = 0;
1523  max = 0;
1524 
1525  for (child = pcmk__xml_first_child(data); child != NULL;
1526  child = pcmk__xml_next(child)) {
1527  pcmk__xe_log(log_level, file, function, line, prefix, child,
1528  depth + 1,
1530  }
1531  }
1532 
1533  if (pcmk_is_set(options, xml_log_option_close)) {
1534  char *buffer = NULL;
1535 
1536  insert_prefix(options, &buffer, &offset, &max, depth);
1537  buffer_print(buffer, max, offset, "</%s>", name);
1538 
1539  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
1540  free(buffer);
1541  }
1542 }
1543 
1544 // Log XML portions that have been marked as changed
1545 static void
1546 log_xml_changes(int log_level, const char *file, const char *function, int line,
1547  const char *prefix, xmlNode *data, int depth, int options)
1548 {
1549  xml_private_t *p;
1550  char *prefix_m = NULL;
1551  xmlNode *child = NULL;
1552 
1553  if ((data == NULL) || (log_level == LOG_NEVER)) {
1554  return;
1555  }
1556 
1557  p = data->_private;
1558 
1559  prefix_m = strdup(prefix);
1560  prefix_m[1] = '+';
1561 
1562  if (pcmk_all_flags_set(p->flags, pcmk__xf_dirty|pcmk__xf_created)) {
1563  /* Continue and log full subtree */
1564  pcmk__xe_log(log_level, file, function, line, prefix_m, data, depth,
1567 
1568  } else if (pcmk_is_set(p->flags, pcmk__xf_dirty)) {
1569  char *spaces = calloc(80, 1);
1570  int s_count = 0, s_max = 80;
1571  char *prefix_del = NULL;
1572  char *prefix_moved = NULL;
1573  const char *flags = prefix;
1574 
1575  insert_prefix(options, &spaces, &s_count, &s_max, depth);
1576  prefix_del = strdup(prefix);
1577  prefix_del[0] = '-';
1578  prefix_del[1] = '-';
1579  prefix_moved = strdup(prefix);
1580  prefix_moved[1] = '~';
1581 
1582  if (pcmk_is_set(p->flags, pcmk__xf_moved)) {
1583  flags = prefix_moved;
1584  } else {
1585  flags = prefix;
1586  }
1587 
1588  pcmk__xe_log(log_level, file, function, line, flags, data, depth,
1589  options|xml_log_option_open);
1590 
1591  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1592  const char *aname = (const char*) a->name;
1593 
1594  p = a->_private;
1595  if (pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1596  const char *value = crm_element_value(data, aname);
1597  flags = prefix_del;
1598  do_crm_log_alias(log_level, file, function, line,
1599  "%s %s @%s=%s", flags, spaces, aname, value);
1600 
1601  } else if (pcmk_is_set(p->flags, pcmk__xf_dirty)) {
1602  const char *value = crm_element_value(data, aname);
1603 
1604  if (pcmk_is_set(p->flags, pcmk__xf_created)) {
1605  flags = prefix_m;
1606 
1607  } else if (pcmk_is_set(p->flags, pcmk__xf_modified)) {
1608  flags = prefix;
1609 
1610  } else if (pcmk_is_set(p->flags, pcmk__xf_moved)) {
1611  flags = prefix_moved;
1612 
1613  } else {
1614  flags = prefix;
1615  }
1616  do_crm_log_alias(log_level, file, function, line,
1617  "%s %s @%s=%s", flags, spaces, aname, value);
1618  }
1619  }
1620  free(prefix_moved);
1621  free(prefix_del);
1622  free(spaces);
1623 
1624  for (child = pcmk__xml_first_child(data); child != NULL;
1625  child = pcmk__xml_next(child)) {
1626  log_xml_changes(log_level, file, function, line, prefix, child,
1627  depth + 1, options);
1628  }
1629 
1630  pcmk__xe_log(log_level, file, function, line, prefix, data, depth,
1631  options|xml_log_option_close);
1632 
1633  } else {
1634  for (child = pcmk__xml_first_child(data); child != NULL;
1635  child = pcmk__xml_next(child)) {
1636  log_xml_changes(log_level, file, function, line, prefix, child,
1637  depth + 1, options);
1638  }
1639  }
1640 
1641  free(prefix_m);
1642 
1643 }
1644 
1645 void
1646 log_data_element(int log_level, const char *file, const char *function, int line,
1647  const char *prefix, xmlNode * data, int depth, int options)
1648 {
1649  xmlNode *a_child = NULL;
1650 
1651  char *prefix_m = NULL;
1652 
1653  if (log_level == LOG_NEVER) {
1654  return;
1655  }
1656 
1657  if (prefix == NULL) {
1658  prefix = "";
1659  }
1660 
1661  /* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
1662  if (data == NULL) {
1663  do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
1664  "No data to dump as XML");
1665  return;
1666  }
1667 
1668  if (pcmk_is_set(options, xml_log_option_dirty_add)) {
1669  log_xml_changes(log_level, file, function, line, prefix, data, depth,
1670  options);
1671  return;
1672  }
1673 
1674  if (pcmk_is_set(options, xml_log_option_formatted)) {
1676  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1677  options |= xml_log_option_diff_all;
1678  prefix_m = strdup(prefix);
1679  prefix_m[1] = '+';
1680  prefix = prefix_m;
1681 
1682  } else if (pcmk_is_set(options, xml_log_option_diff_minus)
1683  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1684  options |= xml_log_option_diff_all;
1685  prefix_m = strdup(prefix);
1686  prefix_m[1] = '-';
1687  prefix = prefix_m;
1688  }
1689  }
1690 
1692  && !pcmk_is_set(options, xml_log_option_diff_all)) {
1693  /* Still searching for the actual change */
1694  for (a_child = pcmk__xml_first_child(data); a_child != NULL;
1695  a_child = pcmk__xml_next(a_child)) {
1696  log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
1697  }
1698  } else {
1699  pcmk__xe_log(log_level, file, function, line, prefix, data, depth,
1702  }
1703  free(prefix_m);
1704 }
1705 
1706 static void
1707 dump_filtered_xml(xmlNode * data, int options, char **buffer, int *offset, int *max)
1708 {
1709  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1710  if (!pcmk__xa_filterable((const char *) (a->name))) {
1711  dump_xml_attr(a, options, buffer, offset, max);
1712  }
1713  }
1714 }
1715 
1716 static void
1717 dump_xml_element(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1718 {
1719  const char *name = NULL;
1720 
1721  CRM_ASSERT(max != NULL);
1722  CRM_ASSERT(offset != NULL);
1723  CRM_ASSERT(buffer != NULL);
1724 
1725  if (data == NULL) {
1726  crm_trace("Nothing to dump");
1727  return;
1728  }
1729 
1730  if (*buffer == NULL) {
1731  *offset = 0;
1732  *max = 0;
1733  }
1734 
1735  name = crm_element_name(data);
1736  CRM_ASSERT(name != NULL);
1737 
1738  insert_prefix(options, buffer, offset, max, depth);
1739  buffer_print(*buffer, *max, *offset, "<%s", name);
1740 
1741  if (options & xml_log_option_filtered) {
1742  dump_filtered_xml(data, options, buffer, offset, max);
1743 
1744  } else {
1745  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1746  dump_xml_attr(a, options, buffer, offset, max);
1747  }
1748  }
1749 
1750  if (data->children == NULL) {
1751  buffer_print(*buffer, *max, *offset, "/>");
1752 
1753  } else {
1754  buffer_print(*buffer, *max, *offset, ">");
1755  }
1756 
1757  if (options & xml_log_option_formatted) {
1758  buffer_print(*buffer, *max, *offset, "\n");
1759  }
1760 
1761  if (data->children) {
1762  xmlNode *xChild = NULL;
1763  for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1764  pcmk__xml2text(xChild, options, buffer, offset, max, depth + 1);
1765  }
1766 
1767  insert_prefix(options, buffer, offset, max, depth);
1768  buffer_print(*buffer, *max, *offset, "</%s>", name);
1769 
1770  if (options & xml_log_option_formatted) {
1771  buffer_print(*buffer, *max, *offset, "\n");
1772  }
1773  }
1774 }
1775 
1776 static void
1777 dump_xml_text(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1778 {
1779  CRM_ASSERT(max != NULL);
1780  CRM_ASSERT(offset != NULL);
1781  CRM_ASSERT(buffer != NULL);
1782 
1783  if (data == NULL) {
1784  crm_trace("Nothing to dump");
1785  return;
1786  }
1787 
1788  if (*buffer == NULL) {
1789  *offset = 0;
1790  *max = 0;
1791  }
1792 
1793  insert_prefix(options, buffer, offset, max, depth);
1794 
1795  buffer_print(*buffer, *max, *offset, "%s", data->content);
1796 
1797  if (options & xml_log_option_formatted) {
1798  buffer_print(*buffer, *max, *offset, "\n");
1799  }
1800 }
1801 
1802 static void
1803 dump_xml_cdata(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1804 {
1805  CRM_ASSERT(max != NULL);
1806  CRM_ASSERT(offset != NULL);
1807  CRM_ASSERT(buffer != NULL);
1808 
1809  if (data == NULL) {
1810  crm_trace("Nothing to dump");
1811  return;
1812  }
1813 
1814  if (*buffer == NULL) {
1815  *offset = 0;
1816  *max = 0;
1817  }
1818 
1819  insert_prefix(options, buffer, offset, max, depth);
1820 
1821  buffer_print(*buffer, *max, *offset, "<![CDATA[");
1822  buffer_print(*buffer, *max, *offset, "%s", data->content);
1823  buffer_print(*buffer, *max, *offset, "]]>");
1824 
1825  if (options & xml_log_option_formatted) {
1826  buffer_print(*buffer, *max, *offset, "\n");
1827  }
1828 }
1829 
1830 static void
1831 dump_xml_comment(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1832 {
1833  CRM_ASSERT(max != NULL);
1834  CRM_ASSERT(offset != NULL);
1835  CRM_ASSERT(buffer != NULL);
1836 
1837  if (data == NULL) {
1838  crm_trace("Nothing to dump");
1839  return;
1840  }
1841 
1842  if (*buffer == NULL) {
1843  *offset = 0;
1844  *max = 0;
1845  }
1846 
1847  insert_prefix(options, buffer, offset, max, depth);
1848 
1849  buffer_print(*buffer, *max, *offset, "<!--");
1850  buffer_print(*buffer, *max, *offset, "%s", data->content);
1851  buffer_print(*buffer, *max, *offset, "-->");
1852 
1853  if (options & xml_log_option_formatted) {
1854  buffer_print(*buffer, *max, *offset, "\n");
1855  }
1856 }
1857 
1858 #define PCMK__XMLDUMP_STATS 0
1859 
1871 void
1872 pcmk__xml2text(xmlNode *data, int options, char **buffer, int *offset,
1873  int *max, int depth)
1874 {
1875  if(data == NULL) {
1876  *offset = 0;
1877  *max = 0;
1878  return;
1879  }
1880 
1881  if (!pcmk_is_set(options, xml_log_option_filtered)
1883  /* libxml's serialization reuse is a good idea, sadly we cannot
1884  apply it for the filtered cases (preceding filtering pass
1885  would preclude further reuse of such in-situ modified XML
1886  in generic context and is likely not a win performance-wise),
1887  and there's also a historically unstable throughput argument
1888  (likely stemming from memory allocation overhead, eventhough
1889  that shall be minimized with defaults preset in crm_xml_init) */
1890 #if (PCMK__XMLDUMP_STATS - 0)
1891  time_t next, new = time(NULL);
1892 #endif
1893  xmlDoc *doc;
1894  xmlOutputBuffer *xml_buffer;
1895 
1896  doc = getDocPtr(data);
1897  /* doc will only be NULL if data is */
1898  CRM_CHECK(doc != NULL, return);
1899 
1900  xml_buffer = xmlAllocOutputBuffer(NULL);
1901  CRM_ASSERT(xml_buffer != NULL);
1902 
1903  /* XXX we could setup custom allocation scheme for the particular
1904  buffer, but it's subsumed with crm_xml_init that needs to
1905  be invoked prior to entering this function as such, since
1906  its other branch vitally depends on it -- what can be done
1907  about this all is to have a facade parsing functions that
1908  would 100% mark entering libxml code for us, since we don't
1909  do anything as crazy as swapping out the binary form of the
1910  parsed tree (but those would need to be strictly used as
1911  opposed to libxml's raw functions) */
1912 
1913  xmlNodeDumpOutput(xml_buffer, doc, data, 0,
1914  (options & xml_log_option_formatted), NULL);
1915  /* attempt adding final NL - failing shouldn't be fatal here */
1916  (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
1917  if (xml_buffer->buffer != NULL) {
1918  buffer_print(*buffer, *max, *offset, "%s",
1919  (char *) xmlBufContent(xml_buffer->buffer));
1920  }
1921 
1922 #if (PCMK__XMLDUMP_STATS - 0)
1923  next = time(NULL);
1924  if ((now + 1) < next) {
1925  crm_log_xml_trace(data, "Long time");
1926  crm_err("xmlNodeDump() -> %dbytes took %ds", *max, next - now);
1927  }
1928 #endif
1929 
1930  /* asserted allocation before so there should be something to remove */
1931  (void) xmlOutputBufferClose(xml_buffer);
1932  return;
1933  }
1934 
1935  switch(data->type) {
1936  case XML_ELEMENT_NODE:
1937  /* Handle below */
1938  dump_xml_element(data, options, buffer, offset, max, depth);
1939  break;
1940  case XML_TEXT_NODE:
1941  /* if option xml_log_option_text is enabled, then dump XML_TEXT_NODE */
1942  if (options & xml_log_option_text) {
1943  dump_xml_text(data, options, buffer, offset, max, depth);
1944  }
1945  return;
1946  case XML_COMMENT_NODE:
1947  dump_xml_comment(data, options, buffer, offset, max, depth);
1948  break;
1949  case XML_CDATA_SECTION_NODE:
1950  dump_xml_cdata(data, options, buffer, offset, max, depth);
1951  break;
1952  default:
1953  crm_warn("Unhandled type: %d", data->type);
1954  return;
1955 
1956  /*
1957  XML_ATTRIBUTE_NODE = 2
1958  XML_ENTITY_REF_NODE = 5
1959  XML_ENTITY_NODE = 6
1960  XML_PI_NODE = 7
1961  XML_DOCUMENT_NODE = 9
1962  XML_DOCUMENT_TYPE_NODE = 10
1963  XML_DOCUMENT_FRAG_NODE = 11
1964  XML_NOTATION_NODE = 12
1965  XML_HTML_DOCUMENT_NODE = 13
1966  XML_DTD_NODE = 14
1967  XML_ELEMENT_DECL = 15
1968  XML_ATTRIBUTE_DECL = 16
1969  XML_ENTITY_DECL = 17
1970  XML_NAMESPACE_DECL = 18
1971  XML_XINCLUDE_START = 19
1972  XML_XINCLUDE_END = 20
1973  XML_DOCB_DOCUMENT_NODE = 21
1974  */
1975  }
1976 
1977 }
1978 
1988 void
1989 pcmk__buffer_add_char(char **buffer, int *offset, int *max, char c)
1990 {
1991  buffer_print(*buffer, *max, *offset, "%c", c);
1992 }
1993 
1994 char *
1995 dump_xml_formatted_with_text(xmlNode * an_xml_node)
1996 {
1997  char *buffer = NULL;
1998  int offset = 0, max = 0;
1999 
2000  pcmk__xml2text(an_xml_node,
2002  &buffer, &offset, &max, 0);
2003  return buffer;
2004 }
2005 
2006 char *
2007 dump_xml_formatted(xmlNode * an_xml_node)
2008 {
2009  char *buffer = NULL;
2010  int offset = 0, max = 0;
2011 
2012  pcmk__xml2text(an_xml_node, xml_log_option_formatted, &buffer, &offset,
2013  &max, 0);
2014  return buffer;
2015 }
2016 
2017 char *
2018 dump_xml_unformatted(xmlNode * an_xml_node)
2019 {
2020  char *buffer = NULL;
2021  int offset = 0, max = 0;
2022 
2023  pcmk__xml2text(an_xml_node, 0, &buffer, &offset, &max, 0);
2024  return buffer;
2025 }
2026 
2027 gboolean
2028 xml_has_children(const xmlNode * xml_root)
2029 {
2030  if (xml_root != NULL && xml_root->children != NULL) {
2031  return TRUE;
2032  }
2033  return FALSE;
2034 }
2035 
2036 void
2037 xml_remove_prop(xmlNode * obj, const char *name)
2038 {
2039  if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
2040  crm_trace("Cannot remove %s from %s", name, obj->name);
2041 
2042  } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
2043  /* Leave in place (marked for removal) until after the diff is calculated */
2044  xml_private_t *p = NULL;
2045  xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
2046 
2047  p = attr->_private;
2048  set_parent_flag(obj, pcmk__xf_dirty);
2050  } else {
2051  xmlUnsetProp(obj, (pcmkXmlStr) name);
2052  }
2053 }
2054 
2055 void
2056 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
2057 {
2058  char *f = NULL;
2059 
2060  if (filename == NULL) {
2061  char *uuid = crm_generate_uuid();
2062 
2063  f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
2064  filename = f;
2065  free(uuid);
2066  }
2067 
2068  crm_info("Saving %s to %s", desc, filename);
2069  write_xml_file(xml, filename, FALSE);
2070  free(f);
2071 }
2072 
2080 static void
2081 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
2082 {
2083  for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
2084  pcmk__set_xml_flags((xml_private_t *) (attr->_private), flag);
2085  }
2086 }
2087 
2097 static void
2098 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
2099  const char *old_value)
2100 {
2101  xml_private_t *p = new_xml->doc->_private;
2102  xmlAttr *attr = NULL;
2103 
2104  // Prevent the dirty flag being set recursively upwards
2106 
2107  // Restore the old value (and the tracking flag)
2108  attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2110 
2111  // Reset flags (so the attribute doesn't appear as newly created)
2112  p = attr->_private;
2113  p->flags = 0;
2114 
2115  // Check ACLs and mark restored value for later removal
2116  xml_remove_prop(new_xml, attr_name);
2117 
2118  crm_trace("XML attribute %s=%s was removed from %s",
2119  attr_name, old_value, element);
2120 }
2121 
2122 /*
2123  * \internal
2124  * \brief Check ACLs for a changed XML attribute
2125  */
2126 static void
2127 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
2128  const char *old_value)
2129 {
2130  char *vcopy = crm_element_value_copy(new_xml, attr_name);
2131 
2132  crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
2133  attr_name, old_value, vcopy, element);
2134 
2135  // Restore the original value
2136  xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2137 
2138  // Change it back to the new value, to check ACLs
2139  crm_xml_add(new_xml, attr_name, vcopy);
2140  free(vcopy);
2141 }
2142 
2147 static void
2148 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
2149  xmlAttr *new_attr, int p_old, int p_new)
2150 {
2151  xml_private_t *p = new_attr->_private;
2152 
2153  crm_trace("XML attribute %s moved from position %d to %d in %s",
2154  old_attr->name, p_old, p_new, element);
2155 
2156  // Mark document, element, and all element's parents as changed
2157  mark_xml_node_dirty(new_xml);
2158 
2159  // Mark attribute as changed
2161 
2162  p = (p_old > p_new)? old_attr->_private : new_attr->_private;
2164 }
2165 
2170 static void
2171 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2172 {
2173  xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
2174 
2175  while (attr_iter != NULL) {
2176  xmlAttr *old_attr = attr_iter;
2177  xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2178  const char *name = (const char *) attr_iter->name;
2179  const char *old_value = crm_element_value(old_xml, name);
2180 
2181  attr_iter = attr_iter->next;
2182  if (new_attr == NULL) {
2183  mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
2184  old_value);
2185 
2186  } else {
2187  xml_private_t *p = new_attr->_private;
2188  int new_pos = pcmk__xml_position((xmlNode*) new_attr,
2189  pcmk__xf_skip);
2190  int old_pos = pcmk__xml_position((xmlNode*) old_attr,
2191  pcmk__xf_skip);
2192  const char *new_value = crm_element_value(new_xml, name);
2193 
2194  // This attribute isn't new
2196 
2197  if (strcmp(new_value, old_value) != 0) {
2198  mark_attr_changed(new_xml, (const char *) old_xml->name, name,
2199  old_value);
2200 
2201  } else if ((old_pos != new_pos)
2202  && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
2203  mark_attr_moved(new_xml, (const char *) old_xml->name,
2204  old_attr, new_attr, old_pos, new_pos);
2205  }
2206  }
2207  }
2208 }
2209 
2214 static void
2215 mark_created_attrs(xmlNode *new_xml)
2216 {
2217  xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
2218 
2219  while (attr_iter != NULL) {
2220  xmlAttr *new_attr = attr_iter;
2221  xml_private_t *p = attr_iter->_private;
2222 
2223  attr_iter = attr_iter->next;
2224  if (pcmk_is_set(p->flags, pcmk__xf_created)) {
2225  const char *attr_name = (const char *) new_attr->name;
2226 
2227  crm_trace("Created new attribute %s=%s in %s",
2228  attr_name, crm_element_value(new_xml, attr_name),
2229  new_xml->name);
2230 
2231  /* Check ACLs (we can't use the remove-then-create trick because it
2232  * would modify the attribute position).
2233  */
2234  if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
2235  pcmk__mark_xml_attr_dirty(new_attr);
2236  } else {
2237  // Creation was not allowed, so remove the attribute
2238  xmlUnsetProp(new_xml, new_attr->name);
2239  }
2240  }
2241  }
2242 }
2243 
2248 static void
2249 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2250 {
2251  set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
2252  xml_diff_old_attrs(old_xml, new_xml);
2253  mark_created_attrs(new_xml);
2254 }
2255 
2265 static void
2266 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2267 {
2268  // Re-create the child element so we can check ACLs
2269  xmlNode *candidate = add_node_copy(new_parent, old_child);
2270 
2271  // Clear flags on new child and its children
2272  reset_xml_node_flags(candidate);
2273 
2274  // Check whether ACLs allow the deletion
2275  pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
2276 
2277  // Remove the child again (which will track it in document's deleted_objs)
2278  free_xml_with_position(candidate,
2279  pcmk__xml_position(old_child, pcmk__xf_skip));
2280 
2281  if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
2282  pcmk__set_xml_flags((xml_private_t *) (old_child->_private),
2283  pcmk__xf_skip);
2284  }
2285 }
2286 
2287 static void
2288 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2289  int p_old, int p_new)
2290 {
2291  xml_private_t *p = new_child->_private;
2292 
2293  crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
2294  new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
2295  p_old, p_new, new_parent->name);
2296  mark_xml_node_dirty(new_parent);
2298 
2299  if (p_old > p_new) {
2300  p = old_child->_private;
2301  } else {
2302  p = new_child->_private;
2303  }
2305 }
2306 
2307 // Given original and new XML, mark new XML portions that have changed
2308 static void
2309 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
2310 {
2311  xmlNode *cIter = NULL;
2312  xml_private_t *p = NULL;
2313 
2314  CRM_CHECK(new_xml != NULL, return);
2315  if (old_xml == NULL) {
2316  pcmk__mark_xml_created(new_xml);
2317  pcmk__apply_creation_acl(new_xml, check_top);
2318  return;
2319  }
2320 
2321  p = new_xml->_private;
2322  CRM_CHECK(p != NULL, return);
2323 
2324  if(p->flags & pcmk__xf_processed) {
2325  /* Avoid re-comparing nodes */
2326  return;
2327  }
2329 
2330  xml_diff_attrs(old_xml, new_xml);
2331 
2332  // Check for differences in the original children
2333  for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2334  xmlNode *old_child = cIter;
2335  xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true);
2336 
2337  cIter = pcmk__xml_next(cIter);
2338  if(new_child) {
2339  mark_xml_changes(old_child, new_child, TRUE);
2340 
2341  } else {
2342  mark_child_deleted(old_child, new_xml);
2343  }
2344  }
2345 
2346  // Check for moved or created children
2347  for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2348  xmlNode *new_child = cIter;
2349  xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true);
2350 
2351  cIter = pcmk__xml_next(cIter);
2352  if(old_child == NULL) {
2353  // This is a newly created child
2354  p = new_child->_private;
2356  mark_xml_changes(old_child, new_child, TRUE);
2357 
2358  } else {
2359  /* Check for movement, we already checked for differences */
2360  int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
2361  int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
2362 
2363  if(p_old != p_new) {
2364  mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2365  }
2366  }
2367  }
2368 }
2369 
2370 void
2371 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
2372 {
2374  xml_calculate_changes(old_xml, new_xml);
2375 }
2376 
2377 void
2378 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
2379 {
2380  CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
2381  return);
2382  CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
2383 
2384  if(xml_tracking_changes(new_xml) == FALSE) {
2385  xml_track_changes(new_xml, NULL, NULL, FALSE);
2386  }
2387 
2388  mark_xml_changes(old_xml, new_xml, FALSE);
2389 }
2390 
2391 gboolean
2392 can_prune_leaf(xmlNode * xml_node)
2393 {
2394  xmlNode *cIter = NULL;
2395  gboolean can_prune = TRUE;
2396  const char *name = crm_element_name(xml_node);
2397 
2400  return FALSE;
2401  }
2402 
2403  for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2404  const char *p_name = (const char *) a->name;
2405 
2406  if (strcmp(p_name, XML_ATTR_ID) == 0) {
2407  continue;
2408  }
2409  can_prune = FALSE;
2410  }
2411 
2412  cIter = pcmk__xml_first_child(xml_node);
2413  while (cIter) {
2414  xmlNode *child = cIter;
2415 
2416  cIter = pcmk__xml_next(cIter);
2417  if (can_prune_leaf(child)) {
2418  free_xml(child);
2419  } else {
2420  can_prune = FALSE;
2421  }
2422  }
2423  return can_prune;
2424 }
2425 
2434 xmlNode *
2435 pcmk__xc_match(xmlNode *root, xmlNode *search_comment, bool exact)
2436 {
2437  xmlNode *a_child = NULL;
2438  int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
2439 
2440  CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
2441 
2442  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2443  a_child = pcmk__xml_next(a_child)) {
2444  if (exact) {
2445  int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
2446  xml_private_t *p = a_child->_private;
2447 
2448  if (offset < search_offset) {
2449  continue;
2450 
2451  } else if (offset > search_offset) {
2452  return NULL;
2453  }
2454 
2455  if (pcmk_is_set(p->flags, pcmk__xf_skip)) {
2456  continue;
2457  }
2458  }
2459 
2460  if (a_child->type == XML_COMMENT_NODE
2461  && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
2462  return a_child;
2463 
2464  } else if (exact) {
2465  return NULL;
2466  }
2467  }
2468 
2469  return NULL;
2470 }
2471 
2483 void
2484 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
2485 {
2486  CRM_CHECK(update != NULL, return);
2487  CRM_CHECK(update->type == XML_COMMENT_NODE, return);
2488 
2489  if (target == NULL) {
2490  target = pcmk__xc_match(parent, update, false);
2491  }
2492 
2493  if (target == NULL) {
2494  add_node_copy(parent, update);
2495 
2496  } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
2497  xmlFree(target->content);
2498  target->content = xmlStrdup(update->content);
2499  }
2500 }
2501 
2514 void
2515 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
2516  bool as_diff)
2517 {
2518  xmlNode *a_child = NULL;
2519  const char *object_name = NULL,
2520  *object_href = NULL,
2521  *object_href_val = NULL;
2522 
2523 #if XML_PARSER_DEBUG
2524  crm_log_xml_trace(update, "update:");
2525  crm_log_xml_trace(target, "target:");
2526 #endif
2527 
2528  CRM_CHECK(update != NULL, return);
2529 
2530  if (update->type == XML_COMMENT_NODE) {
2531  pcmk__xc_update(parent, target, update);
2532  return;
2533  }
2534 
2535  object_name = crm_element_name(update);
2536  object_href_val = ID(update);
2537  if (object_href_val != NULL) {
2538  object_href = XML_ATTR_ID;
2539  } else {
2540  object_href_val = crm_element_value(update, XML_ATTR_IDREF);
2541  object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
2542  }
2543 
2544  CRM_CHECK(object_name != NULL, return);
2545  CRM_CHECK(target != NULL || parent != NULL, return);
2546 
2547  if (target == NULL) {
2548  target = pcmk__xe_match(parent, object_name,
2549  object_href, object_href_val);
2550  }
2551 
2552  if (target == NULL) {
2553  target = create_xml_node(parent, object_name);
2554  CRM_CHECK(target != NULL, return);
2555 #if XML_PARSER_DEBUG
2556  crm_trace("Added <%s%s%s%s%s/>", crm_str(object_name),
2557  object_href ? " " : "",
2558  object_href ? object_href : "",
2559  object_href ? "=" : "",
2560  object_href ? object_href_val : "");
2561 
2562  } else {
2563  crm_trace("Found node <%s%s%s%s%s/> to update", crm_str(object_name),
2564  object_href ? " " : "",
2565  object_href ? object_href : "",
2566  object_href ? "=" : "",
2567  object_href ? object_href_val : "");
2568 #endif
2569  }
2570 
2571  CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2572  pcmk__str_casei),
2573  return);
2574 
2575  if (as_diff == FALSE) {
2576  /* So that expand_plus_plus() gets called */
2577  copy_in_properties(target, update);
2578 
2579  } else {
2580  /* No need for expand_plus_plus(), just raw speed */
2581  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2582  a = a->next) {
2583  const char *p_value = pcmk__xml_attr_value(a);
2584 
2585  /* Remove it first so the ordering of the update is preserved */
2586  xmlUnsetProp(target, a->name);
2587  xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
2588  }
2589  }
2590 
2591  for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2592  a_child = pcmk__xml_next(a_child)) {
2593 #if XML_PARSER_DEBUG
2594  crm_trace("Updating child <%s%s%s%s%s/>", crm_str(object_name),
2595  object_href ? " " : "",
2596  object_href ? object_href : "",
2597  object_href ? "=" : "",
2598  object_href ? object_href_val : "");
2599 #endif
2600  pcmk__xml_update(target, NULL, a_child, as_diff);
2601  }
2602 
2603 #if XML_PARSER_DEBUG
2604  crm_trace("Finished with <%s%s%s%s%s/>", crm_str(object_name),
2605  object_href ? " " : "",
2606  object_href ? object_href : "",
2607  object_href ? "=" : "",
2608  object_href ? object_href_val : "");
2609 #endif
2610 }
2611 
2612 gboolean
2613 update_xml_child(xmlNode * child, xmlNode * to_update)
2614 {
2615  gboolean can_update = TRUE;
2616  xmlNode *child_of_child = NULL;
2617 
2618  CRM_CHECK(child != NULL, return FALSE);
2619  CRM_CHECK(to_update != NULL, return FALSE);
2620 
2621  if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
2622  can_update = FALSE;
2623 
2624  } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
2625  can_update = FALSE;
2626 
2627  } else if (can_update) {
2628 #if XML_PARSER_DEBUG
2629  crm_log_xml_trace(child, "Update match found...");
2630 #endif
2631  pcmk__xml_update(NULL, child, to_update, false);
2632  }
2633 
2634  for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2635  child_of_child = pcmk__xml_next(child_of_child)) {
2636  /* only update the first one */
2637  if (can_update) {
2638  break;
2639  }
2640  can_update = update_xml_child(child_of_child, to_update);
2641  }
2642 
2643  return can_update;
2644 }
2645 
2646 int
2647 find_xml_children(xmlNode ** children, xmlNode * root,
2648  const char *tag, const char *field, const char *value, gboolean search_matches)
2649 {
2650  int match_found = 0;
2651 
2652  CRM_CHECK(root != NULL, return FALSE);
2653  CRM_CHECK(children != NULL, return FALSE);
2654 
2655  if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
2656 
2657  } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
2658 
2659  } else {
2660  if (*children == NULL) {
2661  *children = create_xml_node(NULL, __func__);
2662  }
2663  add_node_copy(*children, root);
2664  match_found = 1;
2665  }
2666 
2667  if (search_matches || match_found == 0) {
2668  xmlNode *child = NULL;
2669 
2670  for (child = pcmk__xml_first_child(root); child != NULL;
2671  child = pcmk__xml_next(child)) {
2672  match_found += find_xml_children(children, child, tag, field, value, search_matches);
2673  }
2674  }
2675 
2676  return match_found;
2677 }
2678 
2679 gboolean
2680 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2681 {
2682  gboolean can_delete = FALSE;
2683  xmlNode *child_of_child = NULL;
2684 
2685  const char *up_id = NULL;
2686  const char *child_id = NULL;
2687  const char *right_val = NULL;
2688 
2689  CRM_CHECK(child != NULL, return FALSE);
2690  CRM_CHECK(update != NULL, return FALSE);
2691 
2692  up_id = ID(update);
2693  child_id = ID(child);
2694 
2695  if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2696  can_delete = TRUE;
2697  }
2698  if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
2699  can_delete = FALSE;
2700  }
2701  if (can_delete && delete_only) {
2702  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2703  a = a->next) {
2704  const char *p_name = (const char *) a->name;
2705  const char *p_value = pcmk__xml_attr_value(a);
2706 
2707  right_val = crm_element_value(child, p_name);
2708  if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) {
2709  can_delete = FALSE;
2710  }
2711  }
2712  }
2713 
2714  if (can_delete && parent != NULL) {
2715  crm_log_xml_trace(child, "Delete match found...");
2716  if (delete_only || update == NULL) {
2717  free_xml(child);
2718 
2719  } else {
2720  xmlNode *tmp = copy_xml(update);
2721  xmlDoc *doc = tmp->doc;
2722  xmlNode *old = NULL;
2723 
2724  xml_accept_changes(tmp);
2725  old = xmlReplaceNode(child, tmp);
2726 
2727  if(xml_tracking_changes(tmp)) {
2728  /* Replaced sections may have included relevant ACLs */
2729  pcmk__apply_acl(tmp);
2730  }
2731 
2732  xml_calculate_changes(old, tmp);
2733  xmlDocSetRootElement(doc, old);
2734  free_xml(old);
2735  }
2736  child = NULL;
2737  return TRUE;
2738 
2739  } else if (can_delete) {
2740  crm_log_xml_debug(child, "Cannot delete the search root");
2741  can_delete = FALSE;
2742  }
2743 
2744  child_of_child = pcmk__xml_first_child(child);
2745  while (child_of_child) {
2746  xmlNode *next = pcmk__xml_next(child_of_child);
2747 
2748  can_delete = replace_xml_child(child, child_of_child, update, delete_only);
2749 
2750  /* only delete the first one */
2751  if (can_delete) {
2752  child_of_child = NULL;
2753  } else {
2754  child_of_child = next;
2755  }
2756  }
2757 
2758  return can_delete;
2759 }
2760 
2761 xmlNode *
2762 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2763 {
2764  xmlNode *child = NULL;
2765  GSList *nvpairs = NULL;
2766  xmlNode *result = NULL;
2767  const char *name = NULL;
2768 
2769  CRM_CHECK(input != NULL, return NULL);
2770 
2771  name = crm_element_name(input);
2772  CRM_CHECK(name != NULL, return NULL);
2773 
2775  nvpairs = pcmk_xml_attrs2nvpairs(input);
2776  nvpairs = pcmk_sort_nvpairs(nvpairs);
2777  pcmk_nvpairs2xml_attrs(nvpairs, result);
2778  pcmk_free_nvpairs(nvpairs);
2779 
2780  for (child = pcmk__xml_first_child(input); child != NULL;
2781  child = pcmk__xml_next(child)) {
2782 
2783  if (recursive) {
2784  sorted_xml(child, result, recursive);
2785  } else {
2786  add_node_copy(result, child);
2787  }
2788  }
2789 
2790  return result;
2791 }
2792 
2793 xmlNode *
2794 first_named_child(const xmlNode *parent, const char *name)
2795 {
2796  xmlNode *match = NULL;
2797 
2798  for (match = pcmk__xe_first_child(parent); match != NULL;
2799  match = pcmk__xe_next(match)) {
2800  /*
2801  * name == NULL gives first child regardless of name; this is
2802  * semantically incorrect in this function, but may be necessary
2803  * due to prior use of xml_child_iter_filter
2804  */
2805  if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) {
2806  return match;
2807  }
2808  }
2809  return NULL;
2810 }
2811 
2819 xmlNode *
2820 crm_next_same_xml(const xmlNode *sibling)
2821 {
2822  xmlNode *match = pcmk__xe_next(sibling);
2823  const char *name = crm_element_name(sibling);
2824 
2825  while (match != NULL) {
2826  if (!strcmp(crm_element_name(match), name)) {
2827  return match;
2828  }
2829  match = pcmk__xe_next(match);
2830  }
2831  return NULL;
2832 }
2833 
2834 void
2836 {
2837  static bool init = true;
2838 
2839  if(init) {
2840  init = false;
2841  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
2842  * pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
2843  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
2844  * less than 1 second.
2845  */
2846  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2847 
2848  /* Populate and free the _private field when nodes are created and destroyed */
2849  xmlDeregisterNodeDefault(free_private_data);
2850  xmlRegisterNodeDefault(new_private_data);
2851 
2852  crm_schema_init();
2853  }
2854 }
2855 
2856 void
2858 {
2859  crm_info("Cleaning up memory from libxml2");
2861  xmlCleanupParser();
2862 }
2863 
2864 #define XPATH_MAX 512
2865 
2866 xmlNode *
2867 expand_idref(xmlNode * input, xmlNode * top)
2868 {
2869  const char *tag = NULL;
2870  const char *ref = NULL;
2871  xmlNode *result = input;
2872 
2873  if (result == NULL) {
2874  return NULL;
2875 
2876  } else if (top == NULL) {
2877  top = input;
2878  }
2879 
2880  tag = crm_element_name(result);
2882 
2883  if (ref != NULL) {
2884  char *xpath_string = crm_strdup_printf("//%s[@id='%s']", tag, ref);
2885 
2886  result = get_xpath_object(xpath_string, top, LOG_ERR);
2887  if (result == NULL) {
2888  char *nodePath = (char *)xmlGetNodePath(top);
2889 
2890  crm_err("No match for %s found in %s: Invalid configuration", xpath_string,
2891  crm_str(nodePath));
2892  free(nodePath);
2893  }
2894  free(xpath_string);
2895  }
2896  return result;
2897 }
2898 
2899 void
2901 {
2902  free_xml(data);
2903 }
2904 
2905 char *
2907 {
2908  static const char *base = NULL;
2909  char *ret = NULL;
2910 
2911  if (base == NULL) {
2912  base = getenv("PCMK_schema_directory");
2913  }
2914  if (pcmk__str_empty(base)) {
2915  base = CRM_SCHEMA_DIRECTORY;
2916  }
2917 
2918  switch (ns) {
2921  ret = strdup(base);
2922  break;
2925  ret = crm_strdup_printf("%s/base", base);
2926  break;
2927  default:
2928  crm_err("XML artefact family specified as %u not recognized", ns);
2929  }
2930  return ret;
2931 }
2932 
2933 char *
2934 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2935 {
2936  char *base = pcmk__xml_artefact_root(ns), *ret = NULL;
2937 
2938  switch (ns) {
2941  ret = crm_strdup_printf("%s/%s.rng", base, filespec);
2942  break;
2945  ret = crm_strdup_printf("%s/%s.xsl", base, filespec);
2946  break;
2947  default:
2948  crm_err("XML artefact family specified as %u not recognized", ns);
2949  }
2950  free(base);
2951 
2952  return ret;
2953 }
2954 
2955 void
2956 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2957 {
2958  while (true) {
2959  const char *name, *value;
2960 
2961  name = va_arg(pairs, const char *);
2962  if (name == NULL) {
2963  return;
2964  }
2965 
2966  value = va_arg(pairs, const char *);
2967  if (value != NULL) {
2968  crm_xml_add(node, name, value);
2969  }
2970  }
2971 }
2972 
2973 void
2974 pcmk__xe_set_props(xmlNodePtr node, ...)
2975 {
2976  va_list pairs;
2977  va_start(pairs, node);
2978  pcmk__xe_set_propv(node, pairs);
2979  va_end(pairs);
2980 }
2981 
2982 // Deprecated functions kept only for backward API compatibility
2983 // LCOV_EXCL_START
2984 
2985 #include <crm/common/xml_compat.h>
2986 
2987 xmlNode *
2988 find_entity(xmlNode *parent, const char *node_name, const char *id)
2989 {
2990  return pcmk__xe_match(parent, node_name,
2991  ((id == NULL)? id : XML_ATTR_ID), id);
2992 }
2993 
2994 // LCOV_EXCL_STOP
2995 // End deprecated API
#define LOG_TRACE
Definition: logging.h:37
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:51
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2371
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:376
#define PCMK__XML_PARSE_OPTS
Definition: xml.c:46
void pcmk__xml2text(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
Definition: xml.c:1872
const char * bz2_strerror(int rc)
Definition: results.c:742
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:99
char * crm_generate_uuid(void)
Definition: utils.c:480
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:102
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, int options)
Definition: xml.c:1646
void pcmk__free_acls(GList *acls)
Definition: acl.c:46
int pcmk_rc2legacy(int rc)
Definition: results.c:415
xmlNode * pcmk__xml_match(xmlNode *haystack, xmlNode *needle, bool exact)
Definition: xml.c:369
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:931
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition: xml.c:275
G_GNUC_INTERNAL int pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer, int offset, size_t buffer_size)
Definition: xpath.c:269
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2378
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:431
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:1129
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:1272
void fix_plus_plus_recursive(xmlNode *target)
Definition: xml.c:541
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition: xml.c:2835
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2794
#define buffer_print(buffer, max, offset, fmt, args...)
Definition: xml.c:65
void crm_schema_cleanup(void)
Definition: schemas.c:552
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__xe_log(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, int options)
Definition: xml.c:1444
void pcmk_free_xml_subtree(xmlNode *xml)
Definition: xml.c:755
#define XML_ATTR_IDREF
Definition: msg_xml.h:136
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml.c:2974
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition: acl.c:541
xmlNode * stdin2xml(void)
Definition: xml.c:930
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:210
#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:281
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:2820
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node&#39;s attributes.
Definition: nvpair.c:161
#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:1045
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:2647
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Definition: xml.c:559
#define CHUNK_SIZE
Definition: xml.c:48
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
Definition: xml.c:2515
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition: xml.c:2906
#define crm_warn(fmt, args...)
Definition: logging.h:359
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml.c:2956
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:1017
xmlNode * copy_xml(xmlNode *src)
Definition: xml.c:830
int pcmk__xml_position(xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition: xml.c:314
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2867
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:726
#define XML_ATTR_ID
Definition: msg_xml.h:135
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:696
#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 free_xml(xmlNode *child)
Definition: xml.c:824
#define crm_trace(fmt, args...)
Definition: logging.h:364
#define XML_PRIVATE_MAGIC
Definition: xml.c:184
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:122
#define crm_log_xml_debug(xml, text)
Definition: logging.h:371
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:2056
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Definition: xml.c:117
Wrappers for and extensions to libxml2.
#define crm_log_xml_warn(xml, text)
Definition: logging.h:368
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition: xml.c:1151
void pcmk__buffer_add_char(char **buffer, int *offset, int *max, char c)
Definition: xml.c:1989
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:219
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition: xml.c:2934
void crm_xml_cleanup(void)
Definition: xml.c:2857
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition: xml.c:674
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:658
char * dump_xml_formatted(xmlNode *an_xml_node)
Definition: xml.c:2007
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition: acl.c:288
xmlNode * string2xml(const char *input)
Definition: xml.c:869
void xml_log_changes(uint8_t log_level, const char *function, xmlNode *xml)
Definition: xml.c:385
const char * pcmk__epoch2str(time_t *when)
Definition: iso8601.c:1715
const xmlChar * pcmkXmlStr
Definition: xml.h:52
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
Definition: xml.c:1995
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
Definition: digest.c:252
xml_private_flags
Definition: xml_internal.h:315
const char * target
Definition: pcmk_fence.c:28
#define CRM_XS
Definition: logging.h:55
char * dump_xml_unformatted(xmlNode *an_xml_node)
Definition: xml.c:2018
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1300
#define PCMK__BUFFER_SIZE
pcmk__xml_artefact_ns
Definition: xml_internal.h:149
void copy_in_properties(xmlNode *target, xmlNode *src)
Definition: xml.c:520
#define crm_log_xml_err(xml, text)
Definition: logging.h:367
xmlNode * pcmk__xe_match(xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:495
pcmk__action_result_t result
Definition: pcmk_fence.c:34
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:309
#define XML_DIFF_MARKER
Definition: msg_xml.h:114
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:2028
#define crm_err(fmt, args...)
Definition: logging.h:358
#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:132
xmlNode * input
const char * pcmk__get_tmpdir(void)
Definition: io.c:540
#define XML_ACL_TAG_ROLE_REFv1
Definition: msg_xml.h:420
void pcmk__mark_xml_created(xmlNode *xml)
Definition: xml.c:155
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:243
xmlNode * pcmk__xc_match(xmlNode *root, xmlNode *search_comment, bool exact)
Definition: xml.c:2435
xmlNode * find_xml_node(xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:445
void xml_accept_changes(xmlNode *xml)
Definition: xml.c:420
#define crm_str(x)
Definition: logging.h:384
void crm_destroy_xml(gpointer data)
xmlNode destructor which can be used in glib collections
Definition: xml.c:2900
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:173
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1345
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
Definition: xml.c:733
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2613
#define XML_CIB_TAG_OBJ_REF
Definition: msg_xml.h:437
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:721
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2762
#define crm_log_xml_trace(xml, text)
Definition: logging.h:372
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:290
#define XML_ACL_TAG_ROLE_REF
Definition: msg_xml.h:419
#define attr_matches(c, n, v)
Definition: xml.c:478
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:611
#define ID(x)
Definition: msg_xml.h:460
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:629
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:688
#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:368
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2680
void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml.c:2484
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2037
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1115
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:2988
#define crm_info(fmt, args...)
Definition: logging.h:361
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:563
uint64_t flags
Definition: remote.c:149
gboolean can_prune_leaf(xmlNode *xml_node)
Definition: xml.c:2392
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:297