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