pacemaker  2.1.2-ada5c3b36
Scalable High-Availability cluster resource manager
xml.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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  return NULL;
979  }
980 
981  rc = BZ_OK;
982  // cppcheck seems not to understand the abort-logic in pcmk__realloc
983  // cppcheck-suppress memleak
984  while (rc == BZ_OK) {
985  buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1);
986  read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
987 
988  crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
989 
990  if (rc == BZ_OK || rc == BZ_STREAM_END) {
991  length += read_len;
992  }
993  }
994 
995  buffer[length] = '\0';
996 
997  if (rc != BZ_STREAM_END) {
998  crm_err("Could not read compressed %s: %s "
999  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1000  free(buffer);
1001  buffer = NULL;
1002  }
1003 
1004  BZ2_bzReadClose(&rc, bz_file);
1005  fclose(input);
1006  return buffer;
1007 }
1008 
1015 void
1017 {
1018  xmlNode *iter = xml->children;
1019 
1020  while (iter) {
1021  xmlNode *next = iter->next;
1022 
1023  switch (iter->type) {
1024  case XML_TEXT_NODE:
1025  /* Remove it */
1026  pcmk_free_xml_subtree(iter);
1027  break;
1028 
1029  case XML_ELEMENT_NODE:
1030  /* Search it */
1031  pcmk__strip_xml_text(iter);
1032  break;
1033 
1034  default:
1035  /* Leave it */
1036  break;
1037  }
1038 
1039  iter = next;
1040  }
1041 }
1042 
1043 xmlNode *
1044 filename2xml(const char *filename)
1045 {
1046  xmlNode *xml = NULL;
1047  xmlDocPtr output = NULL;
1048  bool uncompressed = true;
1049  xmlParserCtxtPtr ctxt = NULL;
1050  xmlErrorPtr last_error = NULL;
1051 
1052  /* create a parser context */
1053  ctxt = xmlNewParserCtxt();
1054  CRM_CHECK(ctxt != NULL, return NULL);
1055 
1056  xmlCtxtResetLastError(ctxt);
1057  xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1058 
1059  if (filename) {
1060  uncompressed = !pcmk__ends_with_ext(filename, ".bz2");
1061  }
1062 
1063  if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
1064  /* STDIN_FILENO == fileno(stdin) */
1065  output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
1067 
1068  } else if (uncompressed) {
1069  output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
1070 
1071  } else {
1072  char *input = decompress_file(filename);
1073 
1074  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
1076  free(input);
1077  }
1078 
1079  if (output && (xml = xmlDocGetRootElement(output))) {
1080  pcmk__strip_xml_text(xml);
1081  }
1082 
1083  last_error = xmlCtxtGetLastError(ctxt);
1084  if (last_error && last_error->code != XML_ERR_OK) {
1085  /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
1086  /*
1087  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
1088  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
1089  */
1090  crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
1091  last_error->domain, last_error->level, last_error->code, last_error->message);
1092 
1093  if (last_error && last_error->code != XML_ERR_OK) {
1094  crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
1095  if (xml != NULL) {
1096  crm_log_xml_err(xml, "Partial");
1097  }
1098  }
1099  }
1100 
1101  xmlFreeParserCtxt(ctxt);
1102  return xml;
1103 }
1104 
1113 const char *
1115 {
1116  const char *now_str = pcmk__epoch2str(NULL);
1117 
1118  return crm_xml_add(xe, XML_CIB_ATTR_WRITTEN,
1119  now_str ? now_str : "Could not determine current time");
1120 }
1121 
1127 void
1129 {
1130  char *c;
1131 
1132  for (c = id; *c; ++c) {
1133  /* @TODO Sanitize more comprehensively */
1134  switch (*c) {
1135  case ':':
1136  case '#':
1137  *c = '.';
1138  }
1139  }
1140 }
1141 
1149 void
1150 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1151 {
1152  va_list ap;
1153  int len = 0;
1154  char *id = NULL;
1155 
1156  /* equivalent to crm_strdup_printf() */
1157  va_start(ap, format);
1158  len = vasprintf(&id, format, ap);
1159  va_end(ap);
1160  CRM_ASSERT(len > 0);
1161 
1162  crm_xml_sanitize_id(id);
1163  crm_xml_add(xml, XML_ATTR_ID, id);
1164  free(id);
1165 }
1166 
1179 static int
1180 write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
1181  bool compress, unsigned int *nbytes)
1182 {
1183  int rc = pcmk_rc_ok;
1184  char *buffer = NULL;
1185 
1186  *nbytes = 0;
1187  crm_log_xml_trace(xml_node, "writing");
1188 
1189  buffer = dump_xml_formatted(xml_node);
1190  CRM_CHECK(buffer && strlen(buffer),
1191  crm_log_xml_warn(xml_node, "formatting failed");
1192  rc = pcmk_rc_error;
1193  goto bail);
1194 
1195  if (compress) {
1196  unsigned int in = 0;
1197  BZFILE *bz_file = NULL;
1198 
1199  rc = BZ_OK;
1200  bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1201  if (rc != BZ_OK) {
1202  crm_warn("Not compressing %s: could not prepare file stream: %s "
1203  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1204  } else {
1205  BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1206  if (rc != BZ_OK) {
1207  crm_warn("Not compressing %s: could not compress data: %s "
1208  CRM_XS " bzerror=%d errno=%d",
1209  filename, bz2_strerror(rc), rc, errno);
1210  }
1211  }
1212 
1213  if (rc == BZ_OK) {
1214  BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1215  if (rc != BZ_OK) {
1216  crm_warn("Not compressing %s: could not write compressed data: %s "
1217  CRM_XS " bzerror=%d errno=%d",
1218  filename, bz2_strerror(rc), rc, errno);
1219  *nbytes = 0; // retry without compression
1220  } else {
1221  crm_trace("Compressed XML for %s from %u bytes to %u",
1222  filename, in, *nbytes);
1223  }
1224  }
1225  rc = pcmk_rc_ok; // Either true, or we'll retry without compression
1226  }
1227 
1228  if (*nbytes == 0) {
1229  rc = fprintf(stream, "%s", buffer);
1230  if (rc < 0) {
1231  rc = errno;
1232  crm_perror(LOG_ERR, "writing %s", filename);
1233  } else {
1234  *nbytes = (unsigned int) rc;
1235  rc = pcmk_rc_ok;
1236  }
1237  }
1238 
1239  bail:
1240 
1241  if (fflush(stream) != 0) {
1242  rc = errno;
1243  crm_perror(LOG_ERR, "flushing %s", filename);
1244  }
1245 
1246  /* Don't report error if the file does not support synchronization */
1247  if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1248  rc = errno;
1249  crm_perror(LOG_ERR, "synchronizing %s", filename);
1250  }
1251 
1252  fclose(stream);
1253 
1254  crm_trace("Saved %d bytes to %s as XML", *nbytes, filename);
1255  free(buffer);
1256 
1257  return rc;
1258 }
1259 
1270 int
1271 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
1272 {
1273  FILE *stream = NULL;
1274  unsigned int nbytes = 0;
1275  int rc = pcmk_rc_ok;
1276 
1277  CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
1278  stream = fdopen(fd, "w");
1279  if (stream == NULL) {
1280  return -errno;
1281  }
1282  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1283  if (rc != pcmk_rc_ok) {
1284  return pcmk_rc2legacy(rc);
1285  }
1286  return (int) nbytes;
1287 }
1288 
1298 int
1299 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
1300 {
1301  FILE *stream = NULL;
1302  unsigned int nbytes = 0;
1303  int rc = pcmk_rc_ok;
1304 
1305  CRM_CHECK(xml_node && filename, return -EINVAL);
1306  stream = fopen(filename, "w");
1307  if (stream == NULL) {
1308  return -errno;
1309  }
1310  rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1311  if (rc != pcmk_rc_ok) {
1312  return pcmk_rc2legacy(rc);
1313  }
1314  return (int) nbytes;
1315 }
1316 
1317 // Replace a portion of a dynamically allocated string (reallocating memory)
1318 static char *
1319 replace_text(char *text, int start, size_t *length, const char *replace)
1320 {
1321  size_t offset = strlen(replace) - 1; // We have space for 1 char already
1322 
1323  *length += offset;
1324  text = pcmk__realloc(text, *length);
1325 
1326  for (size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1327  text[lpc] = text[lpc - offset];
1328  }
1329 
1330  memcpy(text + start, replace, offset + 1);
1331  return text;
1332 }
1333 
1343 char *
1344 crm_xml_escape(const char *text)
1345 {
1346  size_t length;
1347  char *copy;
1348 
1349  /*
1350  * When xmlCtxtReadDoc() parses &lt; and friends in a
1351  * value, it converts them to their human readable
1352  * form.
1353  *
1354  * If one uses xmlNodeDump() to convert it back to a
1355  * string, all is well, because special characters are
1356  * converted back to their escape sequences.
1357  *
1358  * However xmlNodeDump() is randomly dog slow, even with the same
1359  * input. So we need to replicate the escaping in our custom
1360  * version so that the result can be re-parsed by xmlCtxtReadDoc()
1361  * when necessary.
1362  */
1363 
1364  if (text == NULL) {
1365  return NULL;
1366  }
1367 
1368  length = 1 + strlen(text);
1369  copy = strdup(text);
1370  CRM_ASSERT(copy != NULL);
1371  for (size_t index = 0; index < length; index++) {
1372  switch (copy[index]) {
1373  case 0:
1374  break;
1375  case '<':
1376  copy = replace_text(copy, index, &length, "&lt;");
1377  break;
1378  case '>':
1379  copy = replace_text(copy, index, &length, "&gt;");
1380  break;
1381  case '"':
1382  copy = replace_text(copy, index, &length, "&quot;");
1383  break;
1384  case '\'':
1385  copy = replace_text(copy, index, &length, "&apos;");
1386  break;
1387  case '&':
1388  copy = replace_text(copy, index, &length, "&amp;");
1389  break;
1390  case '\t':
1391  /* Might as well just expand to a few spaces... */
1392  copy = replace_text(copy, index, &length, " ");
1393  break;
1394  case '\n':
1395  copy = replace_text(copy, index, &length, "\\n");
1396  break;
1397  case '\r':
1398  copy = replace_text(copy, index, &length, "\\r");
1399  break;
1400  default:
1401  /* Check for and replace non-printing characters with their octal equivalent */
1402  if(copy[index] < ' ' || copy[index] > '~') {
1403  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
1404 
1405  copy = replace_text(copy, index, &length, replace);
1406  free(replace);
1407  }
1408  }
1409  }
1410  return copy;
1411 }
1412 
1413 static inline void
1414 dump_xml_attr(xmlAttrPtr attr, int options, char **buffer, int *offset, int *max)
1415 {
1416  char *p_value = NULL;
1417  const char *p_name = NULL;
1418  xml_private_t *p = NULL;
1419 
1420  CRM_ASSERT(buffer != NULL);
1421  if (attr == NULL || attr->children == NULL) {
1422  return;
1423  }
1424 
1425  p = attr->_private;
1426  if (p && pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1427  return;
1428  }
1429 
1430  p_name = (const char *)attr->name;
1431  p_value = crm_xml_escape((const char *)attr->children->content);
1432  buffer_print(*buffer, *max, *offset, " %s=\"%s\"",
1433  p_name, crm_str(p_value));
1434  free(p_value);
1435 }
1436 
1437 // Log an XML element (and any children) in a formatted way
1438 void
1439 pcmk__xe_log(int log_level, const char *file, const char *function, int line,
1440  const char *prefix, xmlNode *data, int depth, int options)
1441 {
1442  int max = 0;
1443  int offset = 0;
1444  const char *name = NULL;
1445  const char *hidden = NULL;
1446 
1447  xmlNode *child = NULL;
1448 
1449  if ((data == NULL) || (log_level == LOG_NEVER)) {
1450  return;
1451  }
1452 
1453  name = crm_element_name(data);
1454 
1455  if (pcmk_is_set(options, xml_log_option_open)) {
1456  char *buffer = NULL;
1457 
1458  insert_prefix(options, &buffer, &offset, &max, depth);
1459 
1460  if (data->type == XML_COMMENT_NODE) {
1461  buffer_print(buffer, max, offset, "<!--%s-->", data->content);
1462 
1463  } else {
1464  buffer_print(buffer, max, offset, "<%s", name);
1465 
1466  hidden = crm_element_value(data, "hidden");
1467  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL;
1468  a = a->next) {
1469 
1470  xml_private_t *p = a->_private;
1471  const char *p_name = (const char *) a->name;
1472  const char *p_value = pcmk__xml_attr_value(a);
1473  char *p_copy = NULL;
1474 
1475  if (pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1476  continue;
1477  } else if (pcmk_any_flags_set(options,
1480  && (strcmp(XML_DIFF_MARKER, p_name) == 0)) {
1481  continue;
1482 
1483  } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1484  p_copy = strdup("*****");
1485 
1486  } else {
1487  p_copy = crm_xml_escape(p_value);
1488  }
1489 
1490  buffer_print(buffer, max, offset, " %s=\"%s\"",
1491  p_name, crm_str(p_copy));
1492  free(p_copy);
1493  }
1494 
1495  if(xml_has_children(data) == FALSE) {
1496  buffer_print(buffer, max, offset, "/>");
1497 
1498  } else if (pcmk_is_set(options, xml_log_option_children)) {
1499  buffer_print(buffer, max, offset, ">");
1500 
1501  } else {
1502  buffer_print(buffer, max, offset, "/>");
1503  }
1504  }
1505 
1506  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
1507  free(buffer);
1508  }
1509 
1510  if(data->type == XML_COMMENT_NODE) {
1511  return;
1512 
1513  } else if(xml_has_children(data) == FALSE) {
1514  return;
1515 
1516  } else if (pcmk_is_set(options, xml_log_option_children)) {
1517  offset = 0;
1518  max = 0;
1519 
1520  for (child = pcmk__xml_first_child(data); child != NULL;
1521  child = pcmk__xml_next(child)) {
1522  pcmk__xe_log(log_level, file, function, line, prefix, child,
1523  depth + 1,
1525  }
1526  }
1527 
1528  if (pcmk_is_set(options, xml_log_option_close)) {
1529  char *buffer = NULL;
1530 
1531  insert_prefix(options, &buffer, &offset, &max, depth);
1532  buffer_print(buffer, max, offset, "</%s>", name);
1533 
1534  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
1535  free(buffer);
1536  }
1537 }
1538 
1539 // Log XML portions that have been marked as changed
1540 static void
1541 log_xml_changes(int log_level, const char *file, const char *function, int line,
1542  const char *prefix, xmlNode *data, int depth, int options)
1543 {
1544  xml_private_t *p;
1545  char *prefix_m = NULL;
1546  xmlNode *child = NULL;
1547 
1548  if ((data == NULL) || (log_level == LOG_NEVER)) {
1549  return;
1550  }
1551 
1552  p = data->_private;
1553 
1554  prefix_m = strdup(prefix);
1555  prefix_m[1] = '+';
1556 
1557  if (pcmk_all_flags_set(p->flags, pcmk__xf_dirty|pcmk__xf_created)) {
1558  /* Continue and log full subtree */
1559  pcmk__xe_log(log_level, file, function, line, prefix_m, data, depth,
1562 
1563  } else if (pcmk_is_set(p->flags, pcmk__xf_dirty)) {
1564  char *spaces = calloc(80, 1);
1565  int s_count = 0, s_max = 80;
1566  char *prefix_del = NULL;
1567  char *prefix_moved = NULL;
1568  const char *flags = prefix;
1569 
1570  insert_prefix(options, &spaces, &s_count, &s_max, depth);
1571  prefix_del = strdup(prefix);
1572  prefix_del[0] = '-';
1573  prefix_del[1] = '-';
1574  prefix_moved = strdup(prefix);
1575  prefix_moved[1] = '~';
1576 
1577  if (pcmk_is_set(p->flags, pcmk__xf_moved)) {
1578  flags = prefix_moved;
1579  } else {
1580  flags = prefix;
1581  }
1582 
1583  pcmk__xe_log(log_level, file, function, line, flags, data, depth,
1584  options|xml_log_option_open);
1585 
1586  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1587  const char *aname = (const char*) a->name;
1588 
1589  p = a->_private;
1590  if (pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1591  const char *value = crm_element_value(data, aname);
1592  flags = prefix_del;
1593  do_crm_log_alias(log_level, file, function, line,
1594  "%s %s @%s=%s", flags, spaces, aname, value);
1595 
1596  } else if (pcmk_is_set(p->flags, pcmk__xf_dirty)) {
1597  const char *value = crm_element_value(data, aname);
1598 
1599  if (pcmk_is_set(p->flags, pcmk__xf_created)) {
1600  flags = prefix_m;
1601 
1602  } else if (pcmk_is_set(p->flags, pcmk__xf_modified)) {
1603  flags = prefix;
1604 
1605  } else if (pcmk_is_set(p->flags, pcmk__xf_moved)) {
1606  flags = prefix_moved;
1607 
1608  } else {
1609  flags = prefix;
1610  }
1611  do_crm_log_alias(log_level, file, function, line,
1612  "%s %s @%s=%s", flags, spaces, aname, value);
1613  }
1614  }
1615  free(prefix_moved);
1616  free(prefix_del);
1617  free(spaces);
1618 
1619  for (child = pcmk__xml_first_child(data); child != NULL;
1620  child = pcmk__xml_next(child)) {
1621  log_xml_changes(log_level, file, function, line, prefix, child,
1622  depth + 1, options);
1623  }
1624 
1625  pcmk__xe_log(log_level, file, function, line, prefix, data, depth,
1626  options|xml_log_option_close);
1627 
1628  } else {
1629  for (child = pcmk__xml_first_child(data); child != NULL;
1630  child = pcmk__xml_next(child)) {
1631  log_xml_changes(log_level, file, function, line, prefix, child,
1632  depth + 1, options);
1633  }
1634  }
1635 
1636  free(prefix_m);
1637 
1638 }
1639 
1640 void
1641 log_data_element(int log_level, const char *file, const char *function, int line,
1642  const char *prefix, xmlNode * data, int depth, int options)
1643 {
1644  xmlNode *a_child = NULL;
1645 
1646  char *prefix_m = NULL;
1647 
1648  if (log_level == LOG_NEVER) {
1649  return;
1650  }
1651 
1652  if (prefix == NULL) {
1653  prefix = "";
1654  }
1655 
1656  /* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
1657  if (data == NULL) {
1658  do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
1659  "No data to dump as XML");
1660  return;
1661  }
1662 
1663  if (pcmk_is_set(options, xml_log_option_dirty_add)) {
1664  log_xml_changes(log_level, file, function, line, prefix, data, depth,
1665  options);
1666  return;
1667  }
1668 
1669  if (pcmk_is_set(options, xml_log_option_formatted)) {
1671  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1672  options |= xml_log_option_diff_all;
1673  prefix_m = strdup(prefix);
1674  prefix_m[1] = '+';
1675  prefix = prefix_m;
1676 
1677  } else if (pcmk_is_set(options, xml_log_option_diff_minus)
1678  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1679  options |= xml_log_option_diff_all;
1680  prefix_m = strdup(prefix);
1681  prefix_m[1] = '-';
1682  prefix = prefix_m;
1683  }
1684  }
1685 
1687  && !pcmk_is_set(options, xml_log_option_diff_all)) {
1688  /* Still searching for the actual change */
1689  for (a_child = pcmk__xml_first_child(data); a_child != NULL;
1690  a_child = pcmk__xml_next(a_child)) {
1691  log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
1692  }
1693  } else {
1694  pcmk__xe_log(log_level, file, function, line, prefix, data, depth,
1697  }
1698  free(prefix_m);
1699 }
1700 
1701 static void
1702 dump_filtered_xml(xmlNode * data, int options, char **buffer, int *offset, int *max)
1703 {
1704  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1705  if (!pcmk__xa_filterable((const char *) (a->name))) {
1706  dump_xml_attr(a, options, buffer, offset, max);
1707  }
1708  }
1709 }
1710 
1711 static void
1712 dump_xml_element(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1713 {
1714  const char *name = NULL;
1715 
1716  CRM_ASSERT(max != NULL);
1717  CRM_ASSERT(offset != NULL);
1718  CRM_ASSERT(buffer != NULL);
1719 
1720  if (data == NULL) {
1721  crm_trace("Nothing to dump");
1722  return;
1723  }
1724 
1725  if (*buffer == NULL) {
1726  *offset = 0;
1727  *max = 0;
1728  }
1729 
1730  name = crm_element_name(data);
1731  CRM_ASSERT(name != NULL);
1732 
1733  insert_prefix(options, buffer, offset, max, depth);
1734  buffer_print(*buffer, *max, *offset, "<%s", name);
1735 
1736  if (options & xml_log_option_filtered) {
1737  dump_filtered_xml(data, options, buffer, offset, max);
1738 
1739  } else {
1740  for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1741  dump_xml_attr(a, options, buffer, offset, max);
1742  }
1743  }
1744 
1745  if (data->children == NULL) {
1746  buffer_print(*buffer, *max, *offset, "/>");
1747 
1748  } else {
1749  buffer_print(*buffer, *max, *offset, ">");
1750  }
1751 
1752  if (options & xml_log_option_formatted) {
1753  buffer_print(*buffer, *max, *offset, "\n");
1754  }
1755 
1756  if (data->children) {
1757  xmlNode *xChild = NULL;
1758  for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1759  pcmk__xml2text(xChild, options, buffer, offset, max, depth + 1);
1760  }
1761 
1762  insert_prefix(options, buffer, offset, max, depth);
1763  buffer_print(*buffer, *max, *offset, "</%s>", name);
1764 
1765  if (options & xml_log_option_formatted) {
1766  buffer_print(*buffer, *max, *offset, "\n");
1767  }
1768  }
1769 }
1770 
1771 static void
1772 dump_xml_text(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1773 {
1774  CRM_ASSERT(max != NULL);
1775  CRM_ASSERT(offset != NULL);
1776  CRM_ASSERT(buffer != NULL);
1777 
1778  if (data == NULL) {
1779  crm_trace("Nothing to dump");
1780  return;
1781  }
1782 
1783  if (*buffer == NULL) {
1784  *offset = 0;
1785  *max = 0;
1786  }
1787 
1788  insert_prefix(options, buffer, offset, max, depth);
1789 
1790  buffer_print(*buffer, *max, *offset, "%s", data->content);
1791 
1792  if (options & xml_log_option_formatted) {
1793  buffer_print(*buffer, *max, *offset, "\n");
1794  }
1795 }
1796 
1797 static void
1798 dump_xml_cdata(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1799 {
1800  CRM_ASSERT(max != NULL);
1801  CRM_ASSERT(offset != NULL);
1802  CRM_ASSERT(buffer != NULL);
1803 
1804  if (data == NULL) {
1805  crm_trace("Nothing to dump");
1806  return;
1807  }
1808 
1809  if (*buffer == NULL) {
1810  *offset = 0;
1811  *max = 0;
1812  }
1813 
1814  insert_prefix(options, buffer, offset, max, depth);
1815 
1816  buffer_print(*buffer, *max, *offset, "<![CDATA[");
1817  buffer_print(*buffer, *max, *offset, "%s", data->content);
1818  buffer_print(*buffer, *max, *offset, "]]>");
1819 
1820  if (options & xml_log_option_formatted) {
1821  buffer_print(*buffer, *max, *offset, "\n");
1822  }
1823 }
1824 
1825 static void
1826 dump_xml_comment(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1827 {
1828  CRM_ASSERT(max != NULL);
1829  CRM_ASSERT(offset != NULL);
1830  CRM_ASSERT(buffer != NULL);
1831 
1832  if (data == NULL) {
1833  crm_trace("Nothing to dump");
1834  return;
1835  }
1836 
1837  if (*buffer == NULL) {
1838  *offset = 0;
1839  *max = 0;
1840  }
1841 
1842  insert_prefix(options, buffer, offset, max, depth);
1843 
1844  buffer_print(*buffer, *max, *offset, "<!--");
1845  buffer_print(*buffer, *max, *offset, "%s", data->content);
1846  buffer_print(*buffer, *max, *offset, "-->");
1847 
1848  if (options & xml_log_option_formatted) {
1849  buffer_print(*buffer, *max, *offset, "\n");
1850  }
1851 }
1852 
1853 #define PCMK__XMLDUMP_STATS 0
1854 
1866 void
1867 pcmk__xml2text(xmlNode *data, int options, char **buffer, int *offset,
1868  int *max, int depth)
1869 {
1870  if(data == NULL) {
1871  *offset = 0;
1872  *max = 0;
1873  return;
1874  }
1875 
1876  if (!pcmk_is_set(options, xml_log_option_filtered)
1878  /* libxml's serialization reuse is a good idea, sadly we cannot
1879  apply it for the filtered cases (preceding filtering pass
1880  would preclude further reuse of such in-situ modified XML
1881  in generic context and is likely not a win performance-wise),
1882  and there's also a historically unstable throughput argument
1883  (likely stemming from memory allocation overhead, eventhough
1884  that shall be minimized with defaults preset in crm_xml_init) */
1885 #if (PCMK__XMLDUMP_STATS - 0)
1886  time_t next, new = time(NULL);
1887 #endif
1888  xmlDoc *doc;
1889  xmlOutputBuffer *xml_buffer;
1890 
1891  doc = getDocPtr(data);
1892  /* doc will only be NULL if data is */
1893  CRM_CHECK(doc != NULL, return);
1894 
1895  xml_buffer = xmlAllocOutputBuffer(NULL);
1896  CRM_ASSERT(xml_buffer != NULL);
1897 
1898  /* XXX we could setup custom allocation scheme for the particular
1899  buffer, but it's subsumed with crm_xml_init that needs to
1900  be invoked prior to entering this function as such, since
1901  its other branch vitally depends on it -- what can be done
1902  about this all is to have a facade parsing functions that
1903  would 100% mark entering libxml code for us, since we don't
1904  do anything as crazy as swapping out the binary form of the
1905  parsed tree (but those would need to be strictly used as
1906  opposed to libxml's raw functions) */
1907 
1908  xmlNodeDumpOutput(xml_buffer, doc, data, 0,
1909  (options & xml_log_option_formatted), NULL);
1910  /* attempt adding final NL - failing shouldn't be fatal here */
1911  (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
1912  if (xml_buffer->buffer != NULL) {
1913  buffer_print(*buffer, *max, *offset, "%s",
1914  (char *) xmlBufContent(xml_buffer->buffer));
1915  }
1916 
1917 #if (PCMK__XMLDUMP_STATS - 0)
1918  next = time(NULL);
1919  if ((now + 1) < next) {
1920  crm_log_xml_trace(data, "Long time");
1921  crm_err("xmlNodeDump() -> %dbytes took %ds", *max, next - now);
1922  }
1923 #endif
1924 
1925  /* asserted allocation before so there should be something to remove */
1926  (void) xmlOutputBufferClose(xml_buffer);
1927  return;
1928  }
1929 
1930  switch(data->type) {
1931  case XML_ELEMENT_NODE:
1932  /* Handle below */
1933  dump_xml_element(data, options, buffer, offset, max, depth);
1934  break;
1935  case XML_TEXT_NODE:
1936  /* if option xml_log_option_text is enabled, then dump XML_TEXT_NODE */
1937  if (options & xml_log_option_text) {
1938  dump_xml_text(data, options, buffer, offset, max, depth);
1939  }
1940  return;
1941  case XML_COMMENT_NODE:
1942  dump_xml_comment(data, options, buffer, offset, max, depth);
1943  break;
1944  case XML_CDATA_SECTION_NODE:
1945  dump_xml_cdata(data, options, buffer, offset, max, depth);
1946  break;
1947  default:
1948  crm_warn("Unhandled type: %d", data->type);
1949  return;
1950 
1951  /*
1952  XML_ATTRIBUTE_NODE = 2
1953  XML_ENTITY_REF_NODE = 5
1954  XML_ENTITY_NODE = 6
1955  XML_PI_NODE = 7
1956  XML_DOCUMENT_NODE = 9
1957  XML_DOCUMENT_TYPE_NODE = 10
1958  XML_DOCUMENT_FRAG_NODE = 11
1959  XML_NOTATION_NODE = 12
1960  XML_HTML_DOCUMENT_NODE = 13
1961  XML_DTD_NODE = 14
1962  XML_ELEMENT_DECL = 15
1963  XML_ATTRIBUTE_DECL = 16
1964  XML_ENTITY_DECL = 17
1965  XML_NAMESPACE_DECL = 18
1966  XML_XINCLUDE_START = 19
1967  XML_XINCLUDE_END = 20
1968  XML_DOCB_DOCUMENT_NODE = 21
1969  */
1970  }
1971 
1972 }
1973 
1983 void
1984 pcmk__buffer_add_char(char **buffer, int *offset, int *max, char c)
1985 {
1986  buffer_print(*buffer, *max, *offset, "%c", c);
1987 }
1988 
1989 char *
1990 dump_xml_formatted_with_text(xmlNode * an_xml_node)
1991 {
1992  char *buffer = NULL;
1993  int offset = 0, max = 0;
1994 
1995  pcmk__xml2text(an_xml_node,
1997  &buffer, &offset, &max, 0);
1998  return buffer;
1999 }
2000 
2001 char *
2002 dump_xml_formatted(xmlNode * an_xml_node)
2003 {
2004  char *buffer = NULL;
2005  int offset = 0, max = 0;
2006 
2007  pcmk__xml2text(an_xml_node, xml_log_option_formatted, &buffer, &offset,
2008  &max, 0);
2009  return buffer;
2010 }
2011 
2012 char *
2013 dump_xml_unformatted(xmlNode * an_xml_node)
2014 {
2015  char *buffer = NULL;
2016  int offset = 0, max = 0;
2017 
2018  pcmk__xml2text(an_xml_node, 0, &buffer, &offset, &max, 0);
2019  return buffer;
2020 }
2021 
2022 gboolean
2023 xml_has_children(const xmlNode * xml_root)
2024 {
2025  if (xml_root != NULL && xml_root->children != NULL) {
2026  return TRUE;
2027  }
2028  return FALSE;
2029 }
2030 
2031 void
2032 xml_remove_prop(xmlNode * obj, const char *name)
2033 {
2034  if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
2035  crm_trace("Cannot remove %s from %s", name, obj->name);
2036 
2037  } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
2038  /* Leave in place (marked for removal) until after the diff is calculated */
2039  xml_private_t *p = NULL;
2040  xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
2041 
2042  p = attr->_private;
2043  set_parent_flag(obj, pcmk__xf_dirty);
2045  } else {
2046  xmlUnsetProp(obj, (pcmkXmlStr) name);
2047  }
2048 }
2049 
2050 void
2051 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
2052 {
2053  char *f = NULL;
2054 
2055  if (filename == NULL) {
2056  char *uuid = crm_generate_uuid();
2057 
2058  f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
2059  filename = f;
2060  free(uuid);
2061  }
2062 
2063  crm_info("Saving %s to %s", desc, filename);
2064  write_xml_file(xml, filename, FALSE);
2065  free(f);
2066 }
2067 
2075 static void
2076 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
2077 {
2078  for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
2079  pcmk__set_xml_flags((xml_private_t *) (attr->_private), flag);
2080  }
2081 }
2082 
2092 static void
2093 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
2094  const char *old_value)
2095 {
2096  xml_private_t *p = new_xml->doc->_private;
2097  xmlAttr *attr = NULL;
2098 
2099  // Prevent the dirty flag being set recursively upwards
2101 
2102  // Restore the old value (and the tracking flag)
2103  attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2105 
2106  // Reset flags (so the attribute doesn't appear as newly created)
2107  p = attr->_private;
2108  p->flags = 0;
2109 
2110  // Check ACLs and mark restored value for later removal
2111  xml_remove_prop(new_xml, attr_name);
2112 
2113  crm_trace("XML attribute %s=%s was removed from %s",
2114  attr_name, old_value, element);
2115 }
2116 
2117 /*
2118  * \internal
2119  * \brief Check ACLs for a changed XML attribute
2120  */
2121 static void
2122 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
2123  const char *old_value)
2124 {
2125  char *vcopy = crm_element_value_copy(new_xml, attr_name);
2126 
2127  crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
2128  attr_name, old_value, vcopy, element);
2129 
2130  // Restore the original value
2131  xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2132 
2133  // Change it back to the new value, to check ACLs
2134  crm_xml_add(new_xml, attr_name, vcopy);
2135  free(vcopy);
2136 }
2137 
2142 static void
2143 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
2144  xmlAttr *new_attr, int p_old, int p_new)
2145 {
2146  xml_private_t *p = new_attr->_private;
2147 
2148  crm_trace("XML attribute %s moved from position %d to %d in %s",
2149  old_attr->name, p_old, p_new, element);
2150 
2151  // Mark document, element, and all element's parents as changed
2152  mark_xml_node_dirty(new_xml);
2153 
2154  // Mark attribute as changed
2156 
2157  p = (p_old > p_new)? old_attr->_private : new_attr->_private;
2159 }
2160 
2165 static void
2166 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2167 {
2168  xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
2169 
2170  while (attr_iter != NULL) {
2171  xmlAttr *old_attr = attr_iter;
2172  xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2173  const char *name = (const char *) attr_iter->name;
2174  const char *old_value = crm_element_value(old_xml, name);
2175 
2176  attr_iter = attr_iter->next;
2177  if (new_attr == NULL) {
2178  mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
2179  old_value);
2180 
2181  } else {
2182  xml_private_t *p = new_attr->_private;
2183  int new_pos = pcmk__xml_position((xmlNode*) new_attr,
2184  pcmk__xf_skip);
2185  int old_pos = pcmk__xml_position((xmlNode*) old_attr,
2186  pcmk__xf_skip);
2187  const char *new_value = crm_element_value(new_xml, name);
2188 
2189  // This attribute isn't new
2191 
2192  if (strcmp(new_value, old_value) != 0) {
2193  mark_attr_changed(new_xml, (const char *) old_xml->name, name,
2194  old_value);
2195 
2196  } else if ((old_pos != new_pos)
2197  && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
2198  mark_attr_moved(new_xml, (const char *) old_xml->name,
2199  old_attr, new_attr, old_pos, new_pos);
2200  }
2201  }
2202  }
2203 }
2204 
2209 static void
2210 mark_created_attrs(xmlNode *new_xml)
2211 {
2212  xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
2213 
2214  while (attr_iter != NULL) {
2215  xmlAttr *new_attr = attr_iter;
2216  xml_private_t *p = attr_iter->_private;
2217 
2218  attr_iter = attr_iter->next;
2219  if (pcmk_is_set(p->flags, pcmk__xf_created)) {
2220  const char *attr_name = (const char *) new_attr->name;
2221 
2222  crm_trace("Created new attribute %s=%s in %s",
2223  attr_name, crm_element_value(new_xml, attr_name),
2224  new_xml->name);
2225 
2226  /* Check ACLs (we can't use the remove-then-create trick because it
2227  * would modify the attribute position).
2228  */
2229  if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
2230  pcmk__mark_xml_attr_dirty(new_attr);
2231  } else {
2232  // Creation was not allowed, so remove the attribute
2233  xmlUnsetProp(new_xml, new_attr->name);
2234  }
2235  }
2236  }
2237 }
2238 
2243 static void
2244 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2245 {
2246  set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
2247  xml_diff_old_attrs(old_xml, new_xml);
2248  mark_created_attrs(new_xml);
2249 }
2250 
2260 static void
2261 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2262 {
2263  // Re-create the child element so we can check ACLs
2264  xmlNode *candidate = add_node_copy(new_parent, old_child);
2265 
2266  // Clear flags on new child and its children
2267  reset_xml_node_flags(candidate);
2268 
2269  // Check whether ACLs allow the deletion
2270  pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
2271 
2272  // Remove the child again (which will track it in document's deleted_objs)
2273  free_xml_with_position(candidate,
2274  pcmk__xml_position(old_child, pcmk__xf_skip));
2275 
2276  if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
2277  pcmk__set_xml_flags((xml_private_t *) (old_child->_private),
2278  pcmk__xf_skip);
2279  }
2280 }
2281 
2282 static void
2283 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2284  int p_old, int p_new)
2285 {
2286  xml_private_t *p = new_child->_private;
2287 
2288  crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
2289  new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
2290  p_old, p_new, new_parent->name);
2291  mark_xml_node_dirty(new_parent);
2293 
2294  if (p_old > p_new) {
2295  p = old_child->_private;
2296  } else {
2297  p = new_child->_private;
2298  }
2300 }
2301 
2302 // Given original and new XML, mark new XML portions that have changed
2303 static void
2304 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
2305 {
2306  xmlNode *cIter = NULL;
2307  xml_private_t *p = NULL;
2308 
2309  CRM_CHECK(new_xml != NULL, return);
2310  if (old_xml == NULL) {
2311  pcmk__mark_xml_created(new_xml);
2312  pcmk__apply_creation_acl(new_xml, check_top);
2313  return;
2314  }
2315 
2316  p = new_xml->_private;
2317  CRM_CHECK(p != NULL, return);
2318 
2319  if(p->flags & pcmk__xf_processed) {
2320  /* Avoid re-comparing nodes */
2321  return;
2322  }
2324 
2325  xml_diff_attrs(old_xml, new_xml);
2326 
2327  // Check for differences in the original children
2328  for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2329  xmlNode *old_child = cIter;
2330  xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true);
2331 
2332  cIter = pcmk__xml_next(cIter);
2333  if(new_child) {
2334  mark_xml_changes(old_child, new_child, TRUE);
2335 
2336  } else {
2337  mark_child_deleted(old_child, new_xml);
2338  }
2339  }
2340 
2341  // Check for moved or created children
2342  for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2343  xmlNode *new_child = cIter;
2344  xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true);
2345 
2346  cIter = pcmk__xml_next(cIter);
2347  if(old_child == NULL) {
2348  // This is a newly created child
2349  p = new_child->_private;
2351  mark_xml_changes(old_child, new_child, TRUE);
2352 
2353  } else {
2354  /* Check for movement, we already checked for differences */
2355  int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
2356  int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
2357 
2358  if(p_old != p_new) {
2359  mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2360  }
2361  }
2362  }
2363 }
2364 
2365 void
2366 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
2367 {
2369  xml_calculate_changes(old_xml, new_xml);
2370 }
2371 
2372 void
2373 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
2374 {
2375  CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
2376  return);
2377  CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
2378 
2379  if(xml_tracking_changes(new_xml) == FALSE) {
2380  xml_track_changes(new_xml, NULL, NULL, FALSE);
2381  }
2382 
2383  mark_xml_changes(old_xml, new_xml, FALSE);
2384 }
2385 
2386 gboolean
2387 can_prune_leaf(xmlNode * xml_node)
2388 {
2389  xmlNode *cIter = NULL;
2390  gboolean can_prune = TRUE;
2391  const char *name = crm_element_name(xml_node);
2392 
2395  return FALSE;
2396  }
2397 
2398  for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2399  const char *p_name = (const char *) a->name;
2400 
2401  if (strcmp(p_name, XML_ATTR_ID) == 0) {
2402  continue;
2403  }
2404  can_prune = FALSE;
2405  }
2406 
2407  cIter = pcmk__xml_first_child(xml_node);
2408  while (cIter) {
2409  xmlNode *child = cIter;
2410 
2411  cIter = pcmk__xml_next(cIter);
2412  if (can_prune_leaf(child)) {
2413  free_xml(child);
2414  } else {
2415  can_prune = FALSE;
2416  }
2417  }
2418  return can_prune;
2419 }
2420 
2429 xmlNode *
2430 pcmk__xc_match(xmlNode *root, xmlNode *search_comment, bool exact)
2431 {
2432  xmlNode *a_child = NULL;
2433  int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
2434 
2435  CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
2436 
2437  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2438  a_child = pcmk__xml_next(a_child)) {
2439  if (exact) {
2440  int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
2441  xml_private_t *p = a_child->_private;
2442 
2443  if (offset < search_offset) {
2444  continue;
2445 
2446  } else if (offset > search_offset) {
2447  return NULL;
2448  }
2449 
2450  if (pcmk_is_set(p->flags, pcmk__xf_skip)) {
2451  continue;
2452  }
2453  }
2454 
2455  if (a_child->type == XML_COMMENT_NODE
2456  && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
2457  return a_child;
2458 
2459  } else if (exact) {
2460  return NULL;
2461  }
2462  }
2463 
2464  return NULL;
2465 }
2466 
2478 void
2479 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
2480 {
2481  CRM_CHECK(update != NULL, return);
2482  CRM_CHECK(update->type == XML_COMMENT_NODE, return);
2483 
2484  if (target == NULL) {
2485  target = pcmk__xc_match(parent, update, false);
2486  }
2487 
2488  if (target == NULL) {
2489  add_node_copy(parent, update);
2490 
2491  } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
2492  xmlFree(target->content);
2493  target->content = xmlStrdup(update->content);
2494  }
2495 }
2496 
2509 void
2510 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
2511  bool as_diff)
2512 {
2513  xmlNode *a_child = NULL;
2514  const char *object_name = NULL,
2515  *object_href = NULL,
2516  *object_href_val = NULL;
2517 
2518 #if XML_PARSER_DEBUG
2519  crm_log_xml_trace("update:", update);
2520  crm_log_xml_trace("target:", target);
2521 #endif
2522 
2523  CRM_CHECK(update != NULL, return);
2524 
2525  if (update->type == XML_COMMENT_NODE) {
2526  pcmk__xc_update(parent, target, update);
2527  return;
2528  }
2529 
2530  object_name = crm_element_name(update);
2531  object_href_val = ID(update);
2532  if (object_href_val != NULL) {
2533  object_href = XML_ATTR_ID;
2534  } else {
2535  object_href_val = crm_element_value(update, XML_ATTR_IDREF);
2536  object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
2537  }
2538 
2539  CRM_CHECK(object_name != NULL, return);
2540  CRM_CHECK(target != NULL || parent != NULL, return);
2541 
2542  if (target == NULL) {
2543  target = pcmk__xe_match(parent, object_name,
2544  object_href, object_href_val);
2545  }
2546 
2547  if (target == NULL) {
2548  target = create_xml_node(parent, object_name);
2549  CRM_CHECK(target != NULL, return);
2550 #if XML_PARSER_DEBUG
2551  crm_trace("Added <%s%s%s%s%s/>", crm_str(object_name),
2552  object_href ? " " : "",
2553  object_href ? object_href : "",
2554  object_href ? "=" : "",
2555  object_href ? object_href_val : "");
2556 
2557  } else {
2558  crm_trace("Found node <%s%s%s%s%s/> to update", crm_str(object_name),
2559  object_href ? " " : "",
2560  object_href ? object_href : "",
2561  object_href ? "=" : "",
2562  object_href ? object_href_val : "");
2563 #endif
2564  }
2565 
2566  CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2567  pcmk__str_casei),
2568  return);
2569 
2570  if (as_diff == FALSE) {
2571  /* So that expand_plus_plus() gets called */
2572  copy_in_properties(target, update);
2573 
2574  } else {
2575  /* No need for expand_plus_plus(), just raw speed */
2576  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2577  a = a->next) {
2578  const char *p_value = pcmk__xml_attr_value(a);
2579 
2580  /* Remove it first so the ordering of the update is preserved */
2581  xmlUnsetProp(target, a->name);
2582  xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
2583  }
2584  }
2585 
2586  for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2587  a_child = pcmk__xml_next(a_child)) {
2588 #if XML_PARSER_DEBUG
2589  crm_trace("Updating child <%s%s%s%s%s/>", crm_str(object_name),
2590  object_href ? " " : "",
2591  object_href ? object_href : "",
2592  object_href ? "=" : "",
2593  object_href ? object_href_val : "");
2594 #endif
2595  pcmk__xml_update(target, NULL, a_child, as_diff);
2596  }
2597 
2598 #if XML_PARSER_DEBUG
2599  crm_trace("Finished with <%s%s%s%s%s/>", crm_str(object_name),
2600  object_href ? " " : "",
2601  object_href ? object_href : "",
2602  object_href ? "=" : "",
2603  object_href ? object_href_val : "");
2604 #endif
2605 }
2606 
2607 gboolean
2608 update_xml_child(xmlNode * child, xmlNode * to_update)
2609 {
2610  gboolean can_update = TRUE;
2611  xmlNode *child_of_child = NULL;
2612 
2613  CRM_CHECK(child != NULL, return FALSE);
2614  CRM_CHECK(to_update != NULL, return FALSE);
2615 
2616  if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
2617  can_update = FALSE;
2618 
2619  } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
2620  can_update = FALSE;
2621 
2622  } else if (can_update) {
2623 #if XML_PARSER_DEBUG
2624  crm_log_xml_trace(child, "Update match found...");
2625 #endif
2626  pcmk__xml_update(NULL, child, to_update, false);
2627  }
2628 
2629  for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2630  child_of_child = pcmk__xml_next(child_of_child)) {
2631  /* only update the first one */
2632  if (can_update) {
2633  break;
2634  }
2635  can_update = update_xml_child(child_of_child, to_update);
2636  }
2637 
2638  return can_update;
2639 }
2640 
2641 int
2642 find_xml_children(xmlNode ** children, xmlNode * root,
2643  const char *tag, const char *field, const char *value, gboolean search_matches)
2644 {
2645  int match_found = 0;
2646 
2647  CRM_CHECK(root != NULL, return FALSE);
2648  CRM_CHECK(children != NULL, return FALSE);
2649 
2650  if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
2651 
2652  } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
2653 
2654  } else {
2655  if (*children == NULL) {
2656  *children = create_xml_node(NULL, __func__);
2657  }
2658  add_node_copy(*children, root);
2659  match_found = 1;
2660  }
2661 
2662  if (search_matches || match_found == 0) {
2663  xmlNode *child = NULL;
2664 
2665  for (child = pcmk__xml_first_child(root); child != NULL;
2666  child = pcmk__xml_next(child)) {
2667  match_found += find_xml_children(children, child, tag, field, value, search_matches);
2668  }
2669  }
2670 
2671  return match_found;
2672 }
2673 
2674 gboolean
2675 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2676 {
2677  gboolean can_delete = FALSE;
2678  xmlNode *child_of_child = NULL;
2679 
2680  const char *up_id = NULL;
2681  const char *child_id = NULL;
2682  const char *right_val = NULL;
2683 
2684  CRM_CHECK(child != NULL, return FALSE);
2685  CRM_CHECK(update != NULL, return FALSE);
2686 
2687  up_id = ID(update);
2688  child_id = ID(child);
2689 
2690  if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2691  can_delete = TRUE;
2692  }
2693  if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
2694  can_delete = FALSE;
2695  }
2696  if (can_delete && delete_only) {
2697  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2698  a = a->next) {
2699  const char *p_name = (const char *) a->name;
2700  const char *p_value = pcmk__xml_attr_value(a);
2701 
2702  right_val = crm_element_value(child, p_name);
2703  if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) {
2704  can_delete = FALSE;
2705  }
2706  }
2707  }
2708 
2709  if (can_delete && parent != NULL) {
2710  crm_log_xml_trace(child, "Delete match found...");
2711  if (delete_only || update == NULL) {
2712  free_xml(child);
2713 
2714  } else {
2715  xmlNode *tmp = copy_xml(update);
2716  xmlDoc *doc = tmp->doc;
2717  xmlNode *old = NULL;
2718 
2719  xml_accept_changes(tmp);
2720  old = xmlReplaceNode(child, tmp);
2721 
2722  if(xml_tracking_changes(tmp)) {
2723  /* Replaced sections may have included relevant ACLs */
2724  pcmk__apply_acl(tmp);
2725  }
2726 
2727  xml_calculate_changes(old, tmp);
2728  xmlDocSetRootElement(doc, old);
2729  free_xml(old);
2730  }
2731  child = NULL;
2732  return TRUE;
2733 
2734  } else if (can_delete) {
2735  crm_log_xml_debug(child, "Cannot delete the search root");
2736  can_delete = FALSE;
2737  }
2738 
2739  child_of_child = pcmk__xml_first_child(child);
2740  while (child_of_child) {
2741  xmlNode *next = pcmk__xml_next(child_of_child);
2742 
2743  can_delete = replace_xml_child(child, child_of_child, update, delete_only);
2744 
2745  /* only delete the first one */
2746  if (can_delete) {
2747  child_of_child = NULL;
2748  } else {
2749  child_of_child = next;
2750  }
2751  }
2752 
2753  return can_delete;
2754 }
2755 
2756 xmlNode *
2757 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2758 {
2759  xmlNode *child = NULL;
2760  GSList *nvpairs = NULL;
2761  xmlNode *result = NULL;
2762  const char *name = NULL;
2763 
2764  CRM_CHECK(input != NULL, return NULL);
2765 
2766  name = crm_element_name(input);
2767  CRM_CHECK(name != NULL, return NULL);
2768 
2769  result = create_xml_node(parent, name);
2770  nvpairs = pcmk_xml_attrs2nvpairs(input);
2771  nvpairs = pcmk_sort_nvpairs(nvpairs);
2772  pcmk_nvpairs2xml_attrs(nvpairs, result);
2773  pcmk_free_nvpairs(nvpairs);
2774 
2775  for (child = pcmk__xml_first_child(input); child != NULL;
2776  child = pcmk__xml_next(child)) {
2777 
2778  if (recursive) {
2779  sorted_xml(child, result, recursive);
2780  } else {
2781  add_node_copy(result, child);
2782  }
2783  }
2784 
2785  return result;
2786 }
2787 
2788 xmlNode *
2789 first_named_child(const xmlNode *parent, const char *name)
2790 {
2791  xmlNode *match = NULL;
2792 
2793  for (match = pcmk__xe_first_child(parent); match != NULL;
2794  match = pcmk__xe_next(match)) {
2795  /*
2796  * name == NULL gives first child regardless of name; this is
2797  * semantically incorrect in this function, but may be necessary
2798  * due to prior use of xml_child_iter_filter
2799  */
2800  if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) {
2801  return match;
2802  }
2803  }
2804  return NULL;
2805 }
2806 
2814 xmlNode *
2815 crm_next_same_xml(const xmlNode *sibling)
2816 {
2817  xmlNode *match = pcmk__xe_next(sibling);
2818  const char *name = crm_element_name(sibling);
2819 
2820  while (match != NULL) {
2821  if (!strcmp(crm_element_name(match), name)) {
2822  return match;
2823  }
2824  match = pcmk__xe_next(match);
2825  }
2826  return NULL;
2827 }
2828 
2829 void
2831 {
2832  static bool init = true;
2833 
2834  if(init) {
2835  init = false;
2836  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
2837  * pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
2838  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
2839  * less than 1 second.
2840  */
2841  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2842 
2843  /* Populate and free the _private field when nodes are created and destroyed */
2844  xmlDeregisterNodeDefault(free_private_data);
2845  xmlRegisterNodeDefault(new_private_data);
2846 
2847  crm_schema_init();
2848  }
2849 }
2850 
2851 void
2853 {
2854  crm_info("Cleaning up memory from libxml2");
2856  xmlCleanupParser();
2857 }
2858 
2859 #define XPATH_MAX 512
2860 
2861 xmlNode *
2862 expand_idref(xmlNode * input, xmlNode * top)
2863 {
2864  const char *tag = NULL;
2865  const char *ref = NULL;
2866  xmlNode *result = input;
2867 
2868  if (result == NULL) {
2869  return NULL;
2870 
2871  } else if (top == NULL) {
2872  top = input;
2873  }
2874 
2875  tag = crm_element_name(result);
2876  ref = crm_element_value(result, XML_ATTR_IDREF);
2877 
2878  if (ref != NULL) {
2879  char *xpath_string = crm_strdup_printf("//%s[@id='%s']", tag, ref);
2880 
2881  result = get_xpath_object(xpath_string, top, LOG_ERR);
2882  if (result == NULL) {
2883  char *nodePath = (char *)xmlGetNodePath(top);
2884 
2885  crm_err("No match for %s found in %s: Invalid configuration", xpath_string,
2886  crm_str(nodePath));
2887  free(nodePath);
2888  }
2889  free(xpath_string);
2890  }
2891  return result;
2892 }
2893 
2894 void
2896 {
2897  free_xml(data);
2898 }
2899 
2900 char *
2902 {
2903  static const char *base = NULL;
2904  char *ret = NULL;
2905 
2906  if (base == NULL) {
2907  base = getenv("PCMK_schema_directory");
2908  }
2909  if (pcmk__str_empty(base)) {
2910  base = CRM_SCHEMA_DIRECTORY;
2911  }
2912 
2913  switch (ns) {
2916  ret = strdup(base);
2917  break;
2920  ret = crm_strdup_printf("%s/base", base);
2921  break;
2922  default:
2923  crm_err("XML artefact family specified as %u not recognized", ns);
2924  }
2925  return ret;
2926 }
2927 
2928 char *
2929 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2930 {
2931  char *base = pcmk__xml_artefact_root(ns), *ret = NULL;
2932 
2933  switch (ns) {
2936  ret = crm_strdup_printf("%s/%s.rng", base, filespec);
2937  break;
2940  ret = crm_strdup_printf("%s/%s.xsl", base, filespec);
2941  break;
2942  default:
2943  crm_err("XML artefact family specified as %u not recognized", ns);
2944  }
2945  free(base);
2946 
2947  return ret;
2948 }
2949 
2950 void
2951 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2952 {
2953  while (true) {
2954  const char *name, *value;
2955 
2956  name = va_arg(pairs, const char *);
2957  if (name == NULL) {
2958  return;
2959  }
2960 
2961  value = va_arg(pairs, const char *);
2962  if (value != NULL) {
2963  crm_xml_add(node, name, value);
2964  }
2965  }
2966 }
2967 
2968 void
2969 pcmk__xe_set_props(xmlNodePtr node, ...)
2970 {
2971  va_list pairs;
2972  va_start(pairs, node);
2973  pcmk__xe_set_propv(node, pairs);
2974  va_end(pairs);
2975 }
2976 
2977 // Deprecated functions kept only for backward API compatibility
2978 // LCOV_EXCL_START
2979 
2980 #include <crm/common/xml_compat.h>
2981 
2982 xmlNode *
2983 find_entity(xmlNode *parent, const char *node_name, const char *id)
2984 {
2985  return pcmk__xe_match(parent, node_name,
2986  ((id == NULL)? id : XML_ATTR_ID), id);
2987 }
2988 
2989 // LCOV_EXCL_STOP
2990 // End deprecated API
#define LOG_TRACE
Definition: logging.h:36
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:225
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:2366
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:379
#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:1867
const char * bz2_strerror(int rc)
Definition: results.c:776
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:99
char * crm_generate_uuid(void)
Definition: utils.c:552
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:1641
void pcmk__free_acls(GList *acls)
Definition: acl.c:46
int pcmk_rc2legacy(int rc)
Definition: results.c:449
xmlNode * pcmk__xml_match(xmlNode *haystack, xmlNode *needle, bool exact)
Definition: xml.c:369
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:2373
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:1128
int char2score(const char *score)
Definition: utils.c:61
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
Definition: xml.c:1271
void fix_plus_plus_recursive(xmlNode *target)
Definition: xml.c:541
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition: xml.c:2830
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2789
#define buffer_print(buffer, max, offset, fmt, args...)
Definition: xml.c:65
void crm_schema_cleanup(void)
Definition: schemas.c:554
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
void pcmk__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:1439
void pcmk_free_xml_subtree(xmlNode *xml)
Definition: xml.c:755
#define XML_ATTR_IDREF
Definition: msg_xml.h:130
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml.c:2969
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition: acl.c:529
xmlNode * stdin2xml(void)
Definition: xml.c:930
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:209
#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:280
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:2815
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:46
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:1044
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:2642
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:2510
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition: xml.c:2901
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:599
#define crm_warn(fmt, args...)
Definition: logging.h:358
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml.c:2951
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:1016
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
int rc
Definition: pcmk_fence.c:35
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2862
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:129
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:363
#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:114
#define crm_log_xml_debug(xml, text)
Definition: logging.h:370
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:2051
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:367
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition: xml.c:1150
void pcmk__buffer_add_char(char **buffer, int *offset, int *max, char c)
Definition: xml.c:1984
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:213
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition: xml.c:2929
void crm_xml_cleanup(void)
Definition: xml.c:2852
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:2002
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition: acl.c:288
xmlNode * string2xml(const char *input)
Definition: xml.c:869
xml_private_flags
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:1714
const xmlChar * pcmkXmlStr
Definition: xml.h:51
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
Definition: xml.c:1990
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
Definition: digest.c:252
const char * target
Definition: pcmk_fence.c:29
#define CRM_XS
Definition: logging.h:54
char * dump_xml_unformatted(xmlNode *an_xml_node)
Definition: xml.c:2013
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1299
#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:366
xmlNode * pcmk__xe_match(xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:495
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:308
#define XML_DIFF_MARKER
Definition: msg_xml.h:108
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:2023
#define crm_err(fmt, args...)
Definition: logging.h:357
#define CRM_ASSERT(expr)
Definition: results.h:42
#define CRM_SCHEMA_DIRECTORY
Definition: config.h:47
#define XML_CIB_ATTR_WRITTEN
Definition: msg_xml.h:126
const char * pcmk__get_tmpdir(void)
Definition: io.c:540
#define XML_ACL_TAG_ROLE_REFv1
Definition: msg_xml.h:414
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:2430
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:383
void crm_destroy_xml(gpointer data)
xmlNode destructor which can be used in glib collections
Definition: xml.c:2895
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:1344
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:2608
#define XML_CIB_TAG_OBJ_REF
Definition: msg_xml.h:431
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:2757
#define crm_log_xml_trace(xml, text)
Definition: logging.h:371
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:290
#define XML_ACL_TAG_ROLE_REF
Definition: msg_xml.h:413
#define attr_matches(c, n, v)
Definition: xml.c:478
#define ID(x)
Definition: msg_xml.h:456
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:629
#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:440
char * name
Definition: pcmk_fence.c:31
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2675
void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml.c:2479
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2032
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1114
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:2983
#define crm_info(fmt, args...)
Definition: logging.h:360
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:2387
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:297