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