pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
xml.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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 <stdarg.h>
13 #include <stdint.h> // uint32_t
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/stat.h> // stat(), S_ISREG, etc.
18 #include <sys/types.h>
19 
20 #include <libxml/parser.h>
21 #include <libxml/tree.h>
22 
23 #include <crm/crm.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc.
26 #include "crmcommon_private.h"
27 
41 bool
42 pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
43  void *user_data)
44 {
45  if (!fn(xml, user_data)) {
46  return false;
47  }
48 
49  for (xml = pcmk__xml_first_child(xml); xml != NULL;
50  xml = pcmk__xml_next(xml)) {
51 
52  if (!pcmk__xml_tree_foreach(xml, fn, user_data)) {
53  return false;
54  }
55  }
56  return true;
57 }
58 
59 bool
60 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
61 {
62  if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
63  return FALSE;
64  } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
66  return FALSE;
67  } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
68  pcmk__xf_lazy)) {
69  return FALSE;
70  }
71  return TRUE;
72 }
73 
74 static inline void
75 set_parent_flag(xmlNode *xml, long flag)
76 {
77  for(; xml; xml = xml->parent) {
78  xml_node_private_t *nodepriv = xml->_private;
79 
80  if (nodepriv == NULL) {
81  /* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
82  } else {
83  pcmk__set_xml_flags(nodepriv, flag);
84  }
85  }
86 }
87 
88 void
89 pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
90 {
91  if(xml && xml->doc && xml->doc->_private){
92  /* During calls to xmlDocCopyNode(), xml->doc may be unset */
93  xml_doc_private_t *docpriv = xml->doc->_private;
94 
95  pcmk__set_xml_flags(docpriv, flag);
96  }
97 }
98 
99 // Mark document, element, and all element's parents as changed
100 void
102 {
104  set_parent_flag(xml, pcmk__xf_dirty);
105 }
106 
118 static bool
119 reset_xml_node_flags(xmlNode *xml, void *user_data)
120 {
121  xml_node_private_t *nodepriv = xml->_private;
122 
123  if (nodepriv != NULL) {
124  nodepriv->flags = pcmk__xf_none;
125  }
126  return true;
127 }
128 
140 static bool
141 mark_xml_dirty_created(xmlNode *xml, void *user_data)
142 {
143  xml_node_private_t *nodepriv = xml->_private;
144 
145  if (nodepriv != NULL) {
147  }
148  return true;
149 }
150 
159 void
161 {
162  CRM_ASSERT(xml != NULL);
163 
164  if (!pcmk__tracking_xml_changes(xml, false)) {
165  // Tracking is disabled for entire document
166  return;
167  }
168 
169  // Mark all parents and document dirty
171 
172  pcmk__xml_tree_foreach(xml, mark_xml_dirty_created, NULL);
173 }
174 
175 #define XML_DOC_PRIVATE_MAGIC 0x81726354UL
176 #define XML_NODE_PRIVATE_MAGIC 0x54637281UL
177 
178 // Free an XML object previously marked as deleted
179 static void
180 free_deleted_object(void *data)
181 {
182  if(data) {
183  pcmk__deleted_xml_t *deleted_obj = data;
184 
185  g_free(deleted_obj->path);
186  free(deleted_obj);
187  }
188 }
189 
190 // Free and NULL user, ACLs, and deleted objects in an XML node's private data
191 static void
192 reset_xml_private_data(xml_doc_private_t *docpriv)
193 {
194  if (docpriv != NULL) {
196 
197  free(docpriv->user);
198  docpriv->user = NULL;
199 
200  if (docpriv->acls != NULL) {
201  pcmk__free_acls(docpriv->acls);
202  docpriv->acls = NULL;
203  }
204 
205  if(docpriv->deleted_objs) {
206  g_list_free_full(docpriv->deleted_objs, free_deleted_object);
207  docpriv->deleted_objs = NULL;
208  }
209  }
210 }
211 
212 // Free all private data associated with an XML node
213 static void
214 free_private_data(xmlNode *node)
215 {
216  /* Note:
217 
218  This function frees private data assosciated with an XML node,
219  unless the function is being called as a result of internal
220  XSLT cleanup.
221 
222  That could happen through, for example, the following chain of
223  function calls:
224 
225  xsltApplyStylesheetInternal
226  -> xsltFreeTransformContext
227  -> xsltFreeRVTs
228  -> xmlFreeDoc
229 
230  And in that case, the node would fulfill three conditions:
231 
232  1. It would be a standalone document (i.e. it wouldn't be
233  part of a document)
234  2. It would have a space-prefixed name (for reference, please
235  see xsltInternals.h: XSLT_MARK_RES_TREE_FRAG)
236  3. It would carry its own payload in the _private field.
237 
238  We do not free data in this circumstance to avoid a failed
239  assertion on the XML_*_PRIVATE_MAGIC later.
240 
241  */
242  if (node->name == NULL || node->name[0] != ' ') {
243  if (node->_private) {
244  if (node->type == XML_DOCUMENT_NODE) {
245  reset_xml_private_data(node->_private);
246  } else {
247  CRM_ASSERT(((xml_node_private_t *) node->_private)->check
249  /* nothing dynamically allocated nested */
250  }
251  free(node->_private);
252  node->_private = NULL;
253  }
254  }
255 }
256 
257 // Allocate and initialize private data for an XML node
258 static void
259 new_private_data(xmlNode *node)
260 {
261  switch (node->type) {
262  case XML_DOCUMENT_NODE: {
263  xml_doc_private_t *docpriv =
265 
266  docpriv->check = XML_DOC_PRIVATE_MAGIC;
267  /* Flags will be reset if necessary when tracking is enabled */
269  node->_private = docpriv;
270  break;
271  }
272  case XML_ELEMENT_NODE:
273  case XML_ATTRIBUTE_NODE:
274  case XML_COMMENT_NODE: {
275  xml_node_private_t *nodepriv =
277 
278  nodepriv->check = XML_NODE_PRIVATE_MAGIC;
279  /* Flags will be reset if necessary when tracking is enabled */
281  node->_private = nodepriv;
282  if (pcmk__tracking_xml_changes(node, FALSE)) {
283  /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
284  * not hooked up at the point we are called
285  */
287  }
288  break;
289  }
290  case XML_TEXT_NODE:
291  case XML_DTD_NODE:
292  case XML_CDATA_SECTION_NODE:
293  break;
294  default:
295  /* Ignore */
296  crm_trace("Ignoring %p %d", node, node->type);
297  CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
298  break;
299  }
300 }
301 
302 void
303 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
304 {
305  xml_accept_changes(xml);
306  crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
308  if(enforce_acls) {
309  if(acl_source == NULL) {
310  acl_source = xml;
311  }
313  pcmk__unpack_acl(acl_source, xml, user);
314  pcmk__apply_acl(xml);
315  }
316 }
317 
318 bool xml_tracking_changes(xmlNode * xml)
319 {
320  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
321  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
323 }
324 
325 bool xml_document_dirty(xmlNode *xml)
326 {
327  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
328  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
330 }
331 
341 int
342 pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
343 {
344  int position = 0;
345 
346  for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
347  xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
348 
349  if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
350  position++;
351  }
352  }
353 
354  return position;
355 }
356 
368 static bool
369 accept_attr_deletions(xmlNode *xml, void *user_data)
370 {
371  reset_xml_node_flags(xml, NULL);
373  return true;
374 }
375 
384 xmlNode *
385 pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
386 {
387  CRM_CHECK(needle != NULL, return NULL);
388 
389  if (needle->type == XML_COMMENT_NODE) {
390  return pcmk__xc_match(haystack, needle, exact);
391 
392  } else {
393  const char *id = pcmk__xe_id(needle);
394  const char *attr = (id == NULL)? NULL : PCMK_XA_ID;
395 
396  return pcmk__xe_first_child(haystack, (const char *) needle->name, attr,
397  id);
398  }
399 }
400 
401 void
402 xml_accept_changes(xmlNode * xml)
403 {
404  xmlNode *top = NULL;
405  xml_doc_private_t *docpriv = NULL;
406 
407  if(xml == NULL) {
408  return;
409  }
410 
411  crm_trace("Accepting changes to %p", xml);
412  docpriv = xml->doc->_private;
413  top = xmlDocGetRootElement(xml->doc);
414 
415  reset_xml_private_data(xml->doc->_private);
416 
417  if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
418  docpriv->flags = pcmk__xf_none;
419  return;
420  }
421 
422  docpriv->flags = pcmk__xf_none;
423  pcmk__xml_tree_foreach(top, accept_attr_deletions, NULL);
424 }
425 
439 xmlNode *
440 pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
441  const char *attr_n, const char *attr_v)
442 {
443  xmlNode *child = NULL;
444  const char *parent_name = "<null>";
445 
446  CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL);
447 
448  if (parent != NULL) {
449  child = parent->children;
450  while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
451  child = child->next;
452  }
453 
454  parent_name = (const char *) parent->name;
455  }
456 
457  for (; child != NULL; child = pcmk__xe_next(child)) {
458  const char *value = NULL;
459 
460  if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) {
461  // Node name mismatch
462  continue;
463  }
464  if (attr_n == NULL) {
465  // No attribute match needed
466  return child;
467  }
468 
469  value = crm_element_value(child, attr_n);
470 
471  if ((attr_v == NULL) && (value != NULL)) {
472  // attr_v == NULL: Attribute attr_n must be set (to any value)
473  return child;
474  }
475  if ((attr_v != NULL) && (pcmk__str_eq(value, attr_v, pcmk__str_none))) {
476  // attr_v != NULL: Attribute attr_n must be set to value attr_v
477  return child;
478  }
479  }
480 
481  if (node_name == NULL) {
482  node_name = "(any)"; // For logging
483  }
484  if (attr_n != NULL) {
485  crm_trace("XML child node <%s %s=%s> not found in %s",
486  node_name, attr_n, attr_v, parent_name);
487  } else {
488  crm_trace("XML child node <%s> not found in %s",
489  node_name, parent_name);
490  }
491  return NULL;
492 }
493 
524 int
525 pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
526 {
527  const char *old_value = NULL;
528 
529  CRM_CHECK((target != NULL) && (name != NULL), return EINVAL);
530 
531  if (value == NULL) {
532  return pcmk_rc_ok;
533  }
534 
535  old_value = crm_element_value(target, name);
536 
537  // If no previous value, skip to default case and set the value unexpanded.
538  if (old_value != NULL) {
539  const char *n = name;
540  const char *v = value;
541 
542  // Stop at first character that differs between name and value
543  for (; (*n == *v) && (*n != '\0'); n++, v++);
544 
545  // If value begins with name followed by a "++" or "+="
546  if ((*n == '\0')
547  && (*v++ == '+')
548  && ((*v == '+') || (*v == '='))) {
549 
550  // If we're expanding ourselves, no previous value was set; use 0
551  int old_value_i = (old_value != value)? char2score(old_value) : 0;
552 
553  /* value="X++": new value of X is old_value + 1
554  * value="X+=Y": new value of X is old_value + Y (for some number Y)
555  */
556  int add = (*v == '+')? 1 : char2score(++v);
557 
558  crm_xml_add_int(target, name, pcmk__add_scores(old_value_i, add));
559  return pcmk_rc_ok;
560  }
561  }
562 
563  // Default case: set the attribute unexpanded (with value treated literally)
564  if (old_value != value) {
565  crm_xml_add(target, name, value);
566  }
567  return pcmk_rc_ok;
568 }
569 
583 int
584 pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
585 {
586  CRM_CHECK((src != NULL) && (target != NULL), return EINVAL);
587 
588  for (xmlAttr *attr = pcmk__xe_first_attr(src); attr != NULL;
589  attr = attr->next) {
590 
591  const char *name = (const char *) attr->name;
592  const char *value = pcmk__xml_attr_value(attr);
593 
595  && (crm_element_value(target, name) != NULL)) {
596  continue;
597  }
598 
600  pcmk__xe_set_score(target, name, value);
601  } else {
602  crm_xml_add(target, name, value);
603  }
604  }
605 
606  return pcmk_rc_ok;
607 }
608 
619 static int
620 remove_xe_attr(xmlNode *element, xmlAttr *attr)
621 {
622  if (attr == NULL) {
623  return pcmk_rc_ok;
624  }
625 
626  if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
627  // ACLs apply to element, not to particular attributes
628  crm_trace("ACLs prevent removal of attributes from %s element",
629  (const char *) element->name);
630  return EPERM;
631  }
632 
633  if (pcmk__tracking_xml_changes(element, false)) {
634  // Leave in place (marked for removal) until after diff is calculated
635  set_parent_flag(element, pcmk__xf_dirty);
636  pcmk__set_xml_flags((xml_node_private_t *) attr->_private,
638  } else {
639  xmlRemoveProp(attr);
640  }
641  return pcmk_rc_ok;
642 }
643 
651 void
652 pcmk__xe_remove_attr(xmlNode *element, const char *name)
653 {
654  if (name != NULL) {
655  remove_xe_attr(element, xmlHasProp(element, (pcmkXmlStr) name));
656  }
657 }
658 
673 bool
674 pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
675 {
676  const char *name = user_data;
677 
679  return true;
680 }
681 
691 void
693  bool (*match)(xmlAttrPtr, void *),
694  void *user_data)
695 {
696  xmlAttrPtr next = NULL;
697 
698  for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
699  next = a->next; // Grab now because attribute might get removed
700  if ((match == NULL) || match(a, user_data)) {
701  if (remove_xe_attr(element, a) != pcmk_rc_ok) {
702  return;
703  }
704  }
705  }
706 }
707 
719 xmlNode *
720 pcmk__xe_create(xmlNode *parent, const char *name)
721 {
722  xmlNode *node = NULL;
723 
724  CRM_ASSERT(!pcmk__str_empty(name));
725 
726  if (parent == NULL) {
727  xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
728 
729  pcmk__mem_assert(doc);
730 
731  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
732  pcmk__mem_assert(node);
733 
734  xmlDocSetRootElement(doc, node);
735 
736  } else {
737  node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
738  pcmk__mem_assert(node);
739  }
740 
742  return node;
743 }
744 
756 G_GNUC_PRINTF(2, 3)
757 void
758 pcmk__xe_set_content(xmlNode *node, const char *format, ...)
759 {
760  if (node != NULL) {
761  const char *content = NULL;
762  char *buf = NULL;
763 
764  if (strchr(format, '%') == NULL) {
765  // Nothing to format
766  content = format;
767 
768  } else {
769  va_list ap;
770 
771  va_start(ap, format);
772 
773  if (pcmk__str_eq(format, "%s", pcmk__str_none)) {
774  // No need to make a copy
775  content = va_arg(ap, const char *);
776 
777  } else {
778  CRM_ASSERT(vasprintf(&buf, format, ap) >= 0);
779  content = buf;
780  }
781  va_end(ap);
782  }
783 
784  xmlNodeSetContent(node, (pcmkXmlStr) content);
785  free(buf);
786  }
787 }
788 
794 void
796 {
797  xmlUnlinkNode(xml); // Detaches from parent and siblings
798  xmlFreeNode(xml); // Frees
799 }
800 
801 static void
802 free_xml_with_position(xmlNode *child, int position)
803 {
804  xmlDoc *doc = NULL;
805  xml_node_private_t *nodepriv = NULL;
806 
807  if (child == NULL) {
808  return;
809  }
810  doc = child->doc;
811  nodepriv = child->_private;
812 
813  if ((doc != NULL) && (xmlDocGetRootElement(doc) == child)) {
814  // Free everything
815  xmlFreeDoc(doc);
816  return;
817  }
818 
819  if (!pcmk__check_acl(child, NULL, pcmk__xf_acl_write)) {
820  GString *xpath = NULL;
821 
822  pcmk__if_tracing({}, return);
823  xpath = pcmk__element_xpath(child);
824  qb_log_from_external_source(__func__, __FILE__,
825  "Cannot remove %s %x", LOG_TRACE,
826  __LINE__, 0, xpath->str, nodepriv->flags);
827  g_string_free(xpath, TRUE);
828  return;
829  }
830 
831  if ((doc != NULL) && pcmk__tracking_xml_changes(child, false)
832  && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
833 
834  xml_doc_private_t *docpriv = doc->_private;
835  GString *xpath = pcmk__element_xpath(child);
836 
837  if (xpath != NULL) {
838  pcmk__deleted_xml_t *deleted_obj = NULL;
839 
840  crm_trace("Deleting %s %p from %p", xpath->str, child, doc);
841 
842  deleted_obj = pcmk__assert_alloc(1, sizeof(pcmk__deleted_xml_t));
843  deleted_obj->path = g_string_free(xpath, FALSE);
844  deleted_obj->position = -1;
845 
846  // Record the position only for XML comments for now
847  if (child->type == XML_COMMENT_NODE) {
848  if (position >= 0) {
849  deleted_obj->position = position;
850 
851  } else {
852  deleted_obj->position = pcmk__xml_position(child,
853  pcmk__xf_skip);
854  }
855  }
856 
857  docpriv->deleted_objs = g_list_append(docpriv->deleted_objs,
858  deleted_obj);
860  }
861  }
862  pcmk_free_xml_subtree(child);
863 }
864 
865 
866 void
867 free_xml(xmlNode * child)
868 {
869  free_xml_with_position(child, -1);
870 }
871 
882 xmlNode *
883 pcmk__xml_copy(xmlNode *parent, xmlNode *src)
884 {
885  xmlNode *copy = NULL;
886 
887  if (src == NULL) {
888  return NULL;
889  }
890 
891  if (parent == NULL) {
892  xmlDoc *doc = NULL;
893 
894  // The copy will be the root element of a new document
895  CRM_ASSERT(src->type == XML_ELEMENT_NODE);
896 
897  doc = xmlNewDoc(PCMK__XML_VERSION);
898  pcmk__mem_assert(doc);
899 
900  copy = xmlDocCopyNode(src, doc, 1);
901  pcmk__mem_assert(copy);
902 
903  xmlDocSetRootElement(doc, copy);
904 
905  } else {
906  copy = xmlDocCopyNode(src, parent->doc, 1);
907  pcmk__mem_assert(copy);
908 
909  xmlAddChild(parent, copy);
910  }
911 
913  return copy;
914 }
915 
922 void
923 pcmk__strip_xml_text(xmlNode *xml)
924 {
925  xmlNode *iter = xml->children;
926 
927  while (iter) {
928  xmlNode *next = iter->next;
929 
930  switch (iter->type) {
931  case XML_TEXT_NODE:
932  /* Remove it */
933  pcmk_free_xml_subtree(iter);
934  break;
935 
936  case XML_ELEMENT_NODE:
937  /* Search it */
938  pcmk__strip_xml_text(iter);
939  break;
940 
941  default:
942  /* Leave it */
943  break;
944  }
945 
946  iter = next;
947  }
948 }
949 
958 const char *
960 {
961  char *now_s = pcmk__epoch2str(NULL, 0);
962  const char *result = NULL;
963 
965  pcmk__s(now_s, "Could not determine current time"));
966  free(now_s);
967  return result;
968 }
969 
975 void
977 {
978  char *c;
979 
980  for (c = id; *c; ++c) {
981  /* @TODO Sanitize more comprehensively */
982  switch (*c) {
983  case ':':
984  case '#':
985  *c = '.';
986  }
987  }
988 }
989 
997 void
998 crm_xml_set_id(xmlNode *xml, const char *format, ...)
999 {
1000  va_list ap;
1001  int len = 0;
1002  char *id = NULL;
1003 
1004  /* equivalent to crm_strdup_printf() */
1005  va_start(ap, format);
1006  len = vasprintf(&id, format, ap);
1007  va_end(ap);
1008  CRM_ASSERT(len > 0);
1009 
1010  crm_xml_sanitize_id(id);
1011  crm_xml_add(xml, PCMK_XA_ID, id);
1012  free(id);
1013 }
1014 
1027 bool
1029 {
1030  if (text == NULL) {
1031  return false;
1032  }
1033 
1034  while (*text != '\0') {
1035  switch (type) {
1036  case pcmk__xml_escape_text:
1037  switch (*text) {
1038  case '<':
1039  case '>':
1040  case '&':
1041  return true;
1042  case '\n':
1043  case '\t':
1044  break;
1045  default:
1046  if (g_ascii_iscntrl(*text)) {
1047  return true;
1048  }
1049  break;
1050  }
1051  break;
1052 
1053  case pcmk__xml_escape_attr:
1054  switch (*text) {
1055  case '<':
1056  case '>':
1057  case '&':
1058  case '"':
1059  return true;
1060  default:
1061  if (g_ascii_iscntrl(*text)) {
1062  return true;
1063  }
1064  break;
1065  }
1066  break;
1067 
1069  switch (*text) {
1070  case '\n':
1071  case '\r':
1072  case '\t':
1073  case '"':
1074  return true;
1075  default:
1076  break;
1077  }
1078  break;
1079 
1080  default: // Invalid enum value
1081  CRM_ASSERT(false);
1082  break;
1083  }
1084 
1085  text = g_utf8_next_char(text);
1086  }
1087  return false;
1088 }
1089 
1109 gchar *
1111 {
1112  GString *copy = NULL;
1113 
1114  if (text == NULL) {
1115  return NULL;
1116  }
1117  copy = g_string_sized_new(strlen(text));
1118 
1119  while (*text != '\0') {
1120  // Don't escape any non-ASCII characters
1121  if ((*text & 0x80) != 0) {
1122  size_t bytes = g_utf8_next_char(text) - text;
1123 
1124  g_string_append_len(copy, text, bytes);
1125  text += bytes;
1126  continue;
1127  }
1128 
1129  switch (type) {
1130  case pcmk__xml_escape_text:
1131  switch (*text) {
1132  case '<':
1133  g_string_append(copy, PCMK__XML_ENTITY_LT);
1134  break;
1135  case '>':
1136  g_string_append(copy, PCMK__XML_ENTITY_GT);
1137  break;
1138  case '&':
1139  g_string_append(copy, PCMK__XML_ENTITY_AMP);
1140  break;
1141  case '\n':
1142  case '\t':
1143  g_string_append_c(copy, *text);
1144  break;
1145  default:
1146  if (g_ascii_iscntrl(*text)) {
1147  g_string_append_printf(copy, "&#x%.2X;", *text);
1148  } else {
1149  g_string_append_c(copy, *text);
1150  }
1151  break;
1152  }
1153  break;
1154 
1155  case pcmk__xml_escape_attr:
1156  switch (*text) {
1157  case '<':
1158  g_string_append(copy, PCMK__XML_ENTITY_LT);
1159  break;
1160  case '>':
1161  g_string_append(copy, PCMK__XML_ENTITY_GT);
1162  break;
1163  case '&':
1164  g_string_append(copy, PCMK__XML_ENTITY_AMP);
1165  break;
1166  case '"':
1167  g_string_append(copy, PCMK__XML_ENTITY_QUOT);
1168  break;
1169  default:
1170  if (g_ascii_iscntrl(*text)) {
1171  g_string_append_printf(copy, "&#x%.2X;", *text);
1172  } else {
1173  g_string_append_c(copy, *text);
1174  }
1175  break;
1176  }
1177  break;
1178 
1180  switch (*text) {
1181  case '"':
1182  g_string_append(copy, "\\\"");
1183  break;
1184  case '\n':
1185  g_string_append(copy, "\\n");
1186  break;
1187  case '\r':
1188  g_string_append(copy, "\\r");
1189  break;
1190  case '\t':
1191  g_string_append(copy, "\\t");
1192  break;
1193  default:
1194  g_string_append_c(copy, *text);
1195  break;
1196  }
1197  break;
1198 
1199  default: // Invalid enum value
1200  CRM_ASSERT(false);
1201  break;
1202  }
1203 
1204  text = g_utf8_next_char(text);
1205  }
1206  return g_string_free(copy, FALSE);
1207 }
1208 
1216 static void
1217 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
1218 {
1219  for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
1220  pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
1221  }
1222 }
1223 
1238 static void
1239 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
1240  const char *old_value)
1241 {
1242  xml_doc_private_t *docpriv = new_xml->doc->_private;
1243  xmlAttr *attr = NULL;
1244  xml_node_private_t *nodepriv;
1245 
1246  // Prevent the dirty flag being set recursively upwards
1248 
1249  // Restore the old value (and the tracking flag)
1250  attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1252 
1253  // Reset flags (so the attribute doesn't appear as newly created)
1254  nodepriv = attr->_private;
1255  nodepriv->flags = 0;
1256 
1257  // Check ACLs and mark restored value for later removal
1258  remove_xe_attr(new_xml, attr);
1259 
1260  crm_trace("XML attribute %s=%s was removed from %s",
1261  attr_name, old_value, element);
1262 }
1263 
1264 /*
1265  * \internal
1266  * \brief Check ACLs for a changed XML attribute
1267  */
1268 static void
1269 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
1270  const char *old_value)
1271 {
1272  char *vcopy = crm_element_value_copy(new_xml, attr_name);
1273 
1274  crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
1275  attr_name, old_value, vcopy, element);
1276 
1277  // Restore the original value
1278  xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1279 
1280  // Change it back to the new value, to check ACLs
1281  crm_xml_add(new_xml, attr_name, vcopy);
1282  free(vcopy);
1283 }
1284 
1296 static void
1297 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
1298  xmlAttr *new_attr, int p_old, int p_new)
1299 {
1300  xml_node_private_t *nodepriv = new_attr->_private;
1301 
1302  crm_trace("XML attribute %s moved from position %d to %d in %s",
1303  old_attr->name, p_old, p_new, element);
1304 
1305  // Mark document, element, and all element's parents as changed
1306  pcmk__mark_xml_node_dirty(new_xml);
1307 
1308  // Mark attribute as changed
1310 
1311  nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1313 }
1314 
1322 static void
1323 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1324 {
1325  xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1326 
1327  while (attr_iter != NULL) {
1328  const char *name = (const char *) attr_iter->name;
1329  xmlAttr *old_attr = attr_iter;
1330  xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1331  const char *old_value = pcmk__xml_attr_value(attr_iter);
1332 
1333  attr_iter = attr_iter->next;
1334  if (new_attr == NULL) {
1335  mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
1336  old_value);
1337 
1338  } else {
1339  xml_node_private_t *nodepriv = new_attr->_private;
1340  int new_pos = pcmk__xml_position((xmlNode*) new_attr,
1341  pcmk__xf_skip);
1342  int old_pos = pcmk__xml_position((xmlNode*) old_attr,
1343  pcmk__xf_skip);
1344  const char *new_value = crm_element_value(new_xml, name);
1345 
1346  // This attribute isn't new
1348 
1349  if (strcmp(new_value, old_value) != 0) {
1350  mark_attr_changed(new_xml, (const char *) old_xml->name, name,
1351  old_value);
1352 
1353  } else if ((old_pos != new_pos)
1354  && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
1355  mark_attr_moved(new_xml, (const char *) old_xml->name,
1356  old_attr, new_attr, old_pos, new_pos);
1357  }
1358  }
1359  }
1360 }
1361 
1371 static void
1372 mark_created_attrs(xmlNode *new_xml)
1373 {
1374  xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1375 
1376  while (attr_iter != NULL) {
1377  xmlAttr *new_attr = attr_iter;
1378  xml_node_private_t *nodepriv = attr_iter->_private;
1379 
1380  attr_iter = attr_iter->next;
1381  if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1382  const char *attr_name = (const char *) new_attr->name;
1383 
1384  crm_trace("Created new attribute %s=%s in %s",
1385  attr_name, pcmk__xml_attr_value(new_attr),
1386  new_xml->name);
1387 
1388  /* Check ACLs (we can't use the remove-then-create trick because it
1389  * would modify the attribute position).
1390  */
1391  if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
1392  pcmk__mark_xml_attr_dirty(new_attr);
1393  } else {
1394  // Creation was not allowed, so remove the attribute
1395  xmlUnsetProp(new_xml, new_attr->name);
1396  }
1397  }
1398  }
1399 }
1400 
1408 static void
1409 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1410 {
1411  set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
1412  xml_diff_old_attrs(old_xml, new_xml);
1413  mark_created_attrs(new_xml);
1414 }
1415 
1428 static void
1429 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1430 {
1431  // Re-create the child element so we can check ACLs
1432  xmlNode *candidate = pcmk__xml_copy(new_parent, old_child);
1433 
1434  // Clear flags on new child and its children
1435  pcmk__xml_tree_foreach(candidate, reset_xml_node_flags, NULL);
1436 
1437  // Check whether ACLs allow the deletion
1438  pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
1439 
1440  // Remove the child again (which will track it in document's deleted_objs)
1441  free_xml_with_position(candidate,
1442  pcmk__xml_position(old_child, pcmk__xf_skip));
1443 
1444  if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
1445  pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
1446  pcmk__xf_skip);
1447  }
1448 }
1449 
1450 static void
1451 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
1452  int p_old, int p_new)
1453 {
1454  xml_node_private_t *nodepriv = new_child->_private;
1455 
1456  crm_trace("Child element %s with "
1457  PCMK_XA_ID "='%s' moved from position %d to %d under %s",
1458  new_child->name, pcmk__s(pcmk__xe_id(new_child), "<no id>"),
1459  p_old, p_new, new_parent->name);
1460  pcmk__mark_xml_node_dirty(new_parent);
1462 
1463  if (p_old > p_new) {
1464  nodepriv = old_child->_private;
1465  } else {
1466  nodepriv = new_child->_private;
1467  }
1469 }
1470 
1471 // Given original and new XML, mark new XML portions that have changed
1472 static void
1473 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
1474 {
1475  xmlNode *old_child = NULL;
1476  xmlNode *new_child = NULL;
1477  xml_node_private_t *nodepriv = NULL;
1478 
1479  CRM_CHECK(new_xml != NULL, return);
1480  if (old_xml == NULL) {
1481  pcmk__xml_mark_created(new_xml);
1482  pcmk__apply_creation_acl(new_xml, check_top);
1483  return;
1484  }
1485 
1486  nodepriv = new_xml->_private;
1487  CRM_CHECK(nodepriv != NULL, return);
1488 
1489  if(nodepriv->flags & pcmk__xf_processed) {
1490  /* Avoid re-comparing nodes */
1491  return;
1492  }
1494 
1495  xml_diff_attrs(old_xml, new_xml);
1496 
1497  // Check for differences in the original children
1498  for (old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
1499  old_child = pcmk__xml_next(old_child)) {
1500 
1501  new_child = pcmk__xml_match(new_xml, old_child, true);
1502 
1503  if (new_child != NULL) {
1504  mark_xml_changes(old_child, new_child, true);
1505 
1506  } else {
1507  mark_child_deleted(old_child, new_xml);
1508  }
1509  }
1510 
1511  // Check for moved or created children
1512  new_child = pcmk__xml_first_child(new_xml);
1513  while (new_child != NULL) {
1514  xmlNode *next = pcmk__xml_next(new_child);
1515 
1516  old_child = pcmk__xml_match(old_xml, new_child, true);
1517 
1518  if (old_child == NULL) {
1519  // This is a newly created child
1520  nodepriv = new_child->_private;
1522 
1523  // May free new_child
1524  mark_xml_changes(old_child, new_child, true);
1525 
1526  } else {
1527  /* Check for movement, we already checked for differences */
1528  int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
1529  int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
1530 
1531  if(p_old != p_new) {
1532  mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
1533  }
1534  }
1535 
1536  new_child = next;
1537  }
1538 }
1539 
1540 void
1541 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
1542 {
1544  xml_calculate_changes(old_xml, new_xml);
1545 }
1546 
1547 // Called functions may set the \p pcmk__xf_skip flag on parts of \p old_xml
1548 void
1549 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
1550 {
1551  CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
1552  && pcmk__xe_is(old_xml, (const char *) new_xml->name)
1553  && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
1554  pcmk__str_none),
1555  return);
1556 
1557  if(xml_tracking_changes(new_xml) == FALSE) {
1558  xml_track_changes(new_xml, NULL, NULL, FALSE);
1559  }
1560 
1561  mark_xml_changes(old_xml, new_xml, FALSE);
1562 }
1563 
1572 xmlNode *
1573 pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
1574 {
1575  xmlNode *a_child = NULL;
1576  int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
1577 
1578  CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
1579 
1580  for (a_child = pcmk__xml_first_child(root); a_child != NULL;
1581  a_child = pcmk__xml_next(a_child)) {
1582  if (exact) {
1583  int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
1584  xml_node_private_t *nodepriv = a_child->_private;
1585 
1586  if (offset < search_offset) {
1587  continue;
1588 
1589  } else if (offset > search_offset) {
1590  return NULL;
1591  }
1592 
1593  if (pcmk_is_set(nodepriv->flags, pcmk__xf_skip)) {
1594  continue;
1595  }
1596  }
1597 
1598  if (a_child->type == XML_COMMENT_NODE
1599  && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
1600  return a_child;
1601 
1602  } else if (exact) {
1603  return NULL;
1604  }
1605  }
1606 
1607  return NULL;
1608 }
1609 
1621 void
1622 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
1623 {
1624  CRM_CHECK(update != NULL, return);
1625  CRM_CHECK(update->type == XML_COMMENT_NODE, return);
1626 
1627  if (target == NULL) {
1628  target = pcmk__xc_match(parent, update, false);
1629  }
1630 
1631  if (target == NULL) {
1632  pcmk__xml_copy(parent, update);
1633 
1634  } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
1635  xmlFree(target->content);
1636  target->content = xmlStrdup(update->content);
1637  }
1638 }
1639 
1674 void
1675 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
1676  uint32_t flags, bool as_diff)
1677 {
1678  /* @COMPAT Refactor further and staticize after v1 patchset deprecation.
1679  *
1680  * @COMPAT Drop as_diff argument when apply_xml_diff() is dropped.
1681  */
1682  const char *update_name = NULL;
1683  const char *update_id_attr = NULL;
1684  const char *update_id_val = NULL;
1685  char *trace_s = NULL;
1686 
1687  crm_log_xml_trace(update, "update");
1688  crm_log_xml_trace(target, "target");
1689 
1690  CRM_CHECK(update != NULL, goto done);
1691 
1692  if (update->type == XML_COMMENT_NODE) {
1693  pcmk__xc_update(parent, target, update);
1694  goto done;
1695  }
1696 
1697  update_name = (const char *) update->name;
1698 
1699  CRM_CHECK(update_name != NULL, goto done);
1700  CRM_CHECK((target != NULL) || (parent != NULL), goto done);
1701 
1702  update_id_val = pcmk__xe_id(update);
1703  if (update_id_val != NULL) {
1704  update_id_attr = PCMK_XA_ID;
1705 
1706  } else {
1707  update_id_val = crm_element_value(update, PCMK_XA_ID_REF);
1708  if (update_id_val != NULL) {
1709  update_id_attr = PCMK_XA_ID_REF;
1710  }
1711  }
1712 
1714  {
1715  if (update_id_attr != NULL) {
1716  trace_s = crm_strdup_printf("<%s %s=%s/>",
1717  update_name, update_id_attr,
1718  update_id_val);
1719  } else {
1720  trace_s = crm_strdup_printf("<%s/>", update_name);
1721  }
1722  },
1723  {}
1724  );
1725 
1726  if (target == NULL) {
1727  // Recursive call
1728  target = pcmk__xe_first_child(parent, update_name, update_id_attr,
1729  update_id_val);
1730  }
1731 
1732  if (target == NULL) {
1733  // Recursive call with no existing matching child
1734  target = pcmk__xe_create(parent, update_name);
1735  crm_trace("Added %s", pcmk__s(trace_s, update_name));
1736 
1737  } else {
1738  // Either recursive call with match, or top-level call
1739  crm_trace("Found node %s to update", pcmk__s(trace_s, update_name));
1740  }
1741 
1742  CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return);
1743 
1744  if (!as_diff) {
1745  pcmk__xe_copy_attrs(target, update, flags);
1746 
1747  } else {
1748  // Preserve order of attributes. Don't use pcmk__xe_copy_attrs().
1749  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
1750  a = a->next) {
1751  const char *p_value = pcmk__xml_attr_value(a);
1752 
1753  /* Remove it first so the ordering of the update is preserved */
1754  xmlUnsetProp(target, a->name);
1755  xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
1756  }
1757  }
1758 
1759  for (xmlNode *child = pcmk__xml_first_child(update); child != NULL;
1760  child = pcmk__xml_next(child)) {
1761 
1762  crm_trace("Updating child of %s", pcmk__s(trace_s, update_name));
1763  pcmk__xml_update(target, NULL, child, flags, as_diff);
1764  }
1765 
1766  crm_trace("Finished with %s", pcmk__s(trace_s, update_name));
1767 
1768 done:
1769  free(trace_s);
1770 }
1771 
1790 static bool
1791 delete_xe_if_matching(xmlNode *xml, void *user_data)
1792 {
1793  xmlNode *search = user_data;
1794 
1795  if (!pcmk__xe_is(search, (const char *) xml->name)) {
1796  // No match: either not both elements, or different element types
1797  return true;
1798  }
1799 
1800  for (const xmlAttr *attr = pcmk__xe_first_attr(search); attr != NULL;
1801  attr = attr->next) {
1802 
1803  const char *search_val = pcmk__xml_attr_value(attr);
1804  const char *xml_val = crm_element_value(xml, (const char *) attr->name);
1805 
1806  if (!pcmk__str_eq(search_val, xml_val, pcmk__str_casei)) {
1807  // No match: an attr in xml doesn't match the attr in search
1808  return true;
1809  }
1810  }
1811 
1812  crm_log_xml_trace(xml, "delete-match");
1813  crm_log_xml_trace(search, "delete-search");
1814  free_xml(xml);
1815 
1816  // Found a match and deleted it; stop traversing tree
1817  return false;
1818 }
1819 
1838 int
1839 pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
1840 {
1841  // See @COMPAT comment in pcmk__xe_replace_match()
1842  CRM_CHECK((xml != NULL) && (search != NULL), return EINVAL);
1843 
1844  for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
1845  xml = pcmk__xe_next(xml)) {
1846 
1847  if (!pcmk__xml_tree_foreach(xml, delete_xe_if_matching, search)) {
1848  // Found and deleted an element
1849  return pcmk_rc_ok;
1850  }
1851  }
1852 
1853  // No match found in this subtree
1854  return ENXIO;
1855 }
1856 
1868 static void
1869 replace_node(xmlNode *old, xmlNode *new)
1870 {
1871  new = xmlCopyNode(new, 1);
1872  pcmk__mem_assert(new);
1873 
1874  // May be unnecessary but avoids slight changes to some test outputs
1875  pcmk__xml_tree_foreach(new, reset_xml_node_flags, NULL);
1876 
1877  old = xmlReplaceNode(old, new);
1878 
1879  if (xml_tracking_changes(new)) {
1880  // Replaced sections may have included relevant ACLs
1881  pcmk__apply_acl(new);
1882  }
1883  xml_calculate_changes(old, new);
1884  xmlFreeNode(old);
1885 }
1886 
1904 static bool
1905 replace_xe_if_matching(xmlNode *xml, void *user_data)
1906 {
1907  xmlNode *replace = user_data;
1908  const char *xml_id = NULL;
1909  const char *replace_id = NULL;
1910 
1911  xml_id = pcmk__xe_id(xml);
1912  replace_id = pcmk__xe_id(replace);
1913 
1914  if (!pcmk__xe_is(replace, (const char *) xml->name)) {
1915  // No match: either not both elements, or different element types
1916  return true;
1917  }
1918 
1919  if ((replace_id != NULL)
1920  && !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) {
1921 
1922  // No match: ID was provided in replace and doesn't match xml's ID
1923  return true;
1924  }
1925 
1926  crm_log_xml_trace(xml, "replace-match");
1927  crm_log_xml_trace(replace, "replace-with");
1928  replace_node(xml, replace);
1929 
1930  // Found a match and replaced it; stop traversing tree
1931  return false;
1932 }
1933 
1951 int
1952 pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
1953 {
1954  /* @COMPAT Some of this behavior (like not matching the tree root, which is
1955  * allowed by pcmk__xe_update_match()) is questionable for general use but
1956  * required for backward compatibility by cib_process_replace() and
1957  * cib_process_delete(). Behavior can change at a major version release if
1958  * desired.
1959  */
1960  CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL);
1961 
1962  for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
1963  xml = pcmk__xe_next(xml)) {
1964 
1965  if (!pcmk__xml_tree_foreach(xml, replace_xe_if_matching, replace)) {
1966  // Found and replaced an element
1967  return pcmk_rc_ok;
1968  }
1969  }
1970 
1971  // No match found in this subtree
1972  return ENXIO;
1973 }
1974 
1976 struct update_data {
1977  xmlNode *update;
1978  uint32_t flags;
1979 };
1980 
2002 static bool
2003 update_xe_if_matching(xmlNode *xml, void *user_data)
2004 {
2005  struct update_data *data = user_data;
2006  xmlNode *update = data->update;
2007 
2008  if (!pcmk__xe_is(update, (const char *) xml->name)) {
2009  // No match: either not both elements, or different element types
2010  return true;
2011  }
2012 
2013  if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) {
2014  // No match: ID mismatch
2015  return true;
2016  }
2017 
2018  crm_log_xml_trace(xml, "update-match");
2019  crm_log_xml_trace(update, "update-with");
2020  pcmk__xml_update(NULL, xml, update, data->flags, false);
2021 
2022  // Found a match and replaced it; stop traversing tree
2023  return false;
2024 }
2025 
2045 int
2046 pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
2047 {
2048  /* @COMPAT In pcmk__xe_delete_match() and pcmk__xe_replace_match(), we
2049  * compare IDs only if the equivalent of the update argument has an ID.
2050  * Here, we're stricter: we consider it a mismatch if only one element has
2051  * an ID attribute, or if both elements have IDs but they don't match.
2052  *
2053  * Perhaps we should align the behavior at a major version release.
2054  */
2055  struct update_data data = {
2056  .update = update,
2057  .flags = flags,
2058  };
2059 
2060  CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL);
2061 
2062  if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, &data)) {
2063  // Found and updated an element
2064  return pcmk_rc_ok;
2065  }
2066 
2067  // No match found in this subtree
2068  return ENXIO;
2069 }
2070 
2071 xmlNode *
2072 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2073 {
2074  xmlNode *child = NULL;
2075  GSList *nvpairs = NULL;
2076  xmlNode *result = NULL;
2077 
2078  CRM_CHECK(input != NULL, return NULL);
2079 
2080  result = pcmk__xe_create(parent, (const char *) input->name);
2081  nvpairs = pcmk_xml_attrs2nvpairs(input);
2082  nvpairs = pcmk_sort_nvpairs(nvpairs);
2083  pcmk_nvpairs2xml_attrs(nvpairs, result);
2084  pcmk_free_nvpairs(nvpairs);
2085 
2086  for (child = pcmk__xe_first_child(input, NULL, NULL, NULL); child != NULL;
2087  child = pcmk__xe_next(child)) {
2088 
2089  if (recursive) {
2090  sorted_xml(child, result, recursive);
2091  } else {
2092  pcmk__xml_copy(result, child);
2093  }
2094  }
2095 
2096  return result;
2097 }
2098 
2107 xmlNode *
2108 pcmk__xe_next_same(const xmlNode *node)
2109 {
2110  for (xmlNode *match = pcmk__xe_next(node); match != NULL;
2111  match = pcmk__xe_next(match)) {
2112 
2113  if (pcmk__xe_is(match, (const char *) node->name)) {
2114  return match;
2115  }
2116  }
2117  return NULL;
2118 }
2119 
2120 void
2122 {
2123  static bool init = true;
2124 
2125  if(init) {
2126  init = false;
2127  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
2128  * pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
2129  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
2130  * less than 1 second.
2131  */
2132  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2133 
2134  /* Populate and free the _private field when nodes are created and destroyed */
2135  xmlDeregisterNodeDefault(free_private_data);
2136  xmlRegisterNodeDefault(new_private_data);
2137 
2138  crm_schema_init();
2139  }
2140 }
2141 
2142 void
2144 {
2146  xmlCleanupParser();
2147 }
2148 
2149 #define XPATH_MAX 512
2150 
2151 xmlNode *
2152 expand_idref(xmlNode * input, xmlNode * top)
2153 {
2154  char *xpath = NULL;
2155  const char *ref = NULL;
2156  xmlNode *result = NULL;
2157 
2158  if (input == NULL) {
2159  return NULL;
2160  }
2161 
2163  if (ref == NULL) {
2164  return input;
2165  }
2166 
2167  if (top == NULL) {
2168  top = input;
2169  }
2170 
2171  xpath = crm_strdup_printf("//%s[@" PCMK_XA_ID "='%s']", input->name, ref);
2172  result = get_xpath_object(xpath, top, LOG_DEBUG);
2173  if (result == NULL) { // Not possible with schema validation enabled
2174  pcmk__config_err("Ignoring invalid %s configuration: "
2175  PCMK_XA_ID_REF " '%s' does not reference "
2176  "a valid object " CRM_XS " xpath=%s",
2177  input->name, ref, xpath);
2178  }
2179  free(xpath);
2180  return result;
2181 }
2182 
2183 char *
2185 {
2186  static const char *base = NULL;
2187  char *ret = NULL;
2188 
2189  if (base == NULL) {
2191  }
2192  if (pcmk__str_empty(base)) {
2193  base = CRM_SCHEMA_DIRECTORY;
2194  }
2195 
2196  switch (ns) {
2199  ret = strdup(base);
2200  break;
2203  ret = crm_strdup_printf("%s/base", base);
2204  break;
2205  default:
2206  crm_err("XML artefact family specified as %u not recognized", ns);
2207  }
2208  return ret;
2209 }
2210 
2211 static char *
2212 find_artefact(enum pcmk__xml_artefact_ns ns, const char *path, const char *filespec)
2213 {
2214  char *ret = NULL;
2215 
2216  switch (ns) {
2219  if (pcmk__ends_with(filespec, ".rng")) {
2220  ret = crm_strdup_printf("%s/%s", path, filespec);
2221  } else {
2222  ret = crm_strdup_printf("%s/%s.rng", path, filespec);
2223  }
2224  break;
2227  if (pcmk__ends_with(filespec, ".xsl")) {
2228  ret = crm_strdup_printf("%s/%s", path, filespec);
2229  } else {
2230  ret = crm_strdup_printf("%s/%s.xsl", path, filespec);
2231  }
2232  break;
2233  default:
2234  crm_err("XML artefact family specified as %u not recognized", ns);
2235  }
2236 
2237  return ret;
2238 }
2239 
2240 char *
2241 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2242 {
2243  struct stat sb;
2244  char *base = pcmk__xml_artefact_root(ns);
2245  char *ret = NULL;
2246 
2247  ret = find_artefact(ns, base, filespec);
2248  free(base);
2249 
2250  if (stat(ret, &sb) != 0 || !S_ISREG(sb.st_mode)) {
2251  const char *remote_schema_dir = pcmk__remote_schema_dir();
2252 
2253  free(ret);
2254  ret = find_artefact(ns, remote_schema_dir, filespec);
2255  }
2256 
2257  return ret;
2258 }
2259 
2260 void
2261 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2262 {
2263  while (true) {
2264  const char *name, *value;
2265 
2266  name = va_arg(pairs, const char *);
2267  if (name == NULL) {
2268  return;
2269  }
2270 
2271  value = va_arg(pairs, const char *);
2272  if (value != NULL) {
2273  crm_xml_add(node, name, value);
2274  }
2275  }
2276 }
2277 
2278 void
2279 pcmk__xe_set_props(xmlNodePtr node, ...)
2280 {
2281  va_list pairs;
2282  va_start(pairs, node);
2283  pcmk__xe_set_propv(node, pairs);
2284  va_end(pairs);
2285 }
2286 
2287 int
2288 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
2289  int (*handler)(xmlNode *xml, void *userdata),
2290  void *userdata)
2291 {
2292  xmlNode *children = (xml? xml->children : NULL);
2293 
2294  CRM_ASSERT(handler != NULL);
2295 
2296  for (xmlNode *node = children; node != NULL; node = node->next) {
2297  if ((node->type == XML_ELEMENT_NODE)
2298  && ((child_element_name == NULL)
2299  || pcmk__xe_is(node, child_element_name))) {
2300  int rc = handler(node, userdata);
2301 
2302  if (rc != pcmk_rc_ok) {
2303  return rc;
2304  }
2305  }
2306  }
2307 
2308  return pcmk_rc_ok;
2309 }
2310 
2311 // Deprecated functions kept only for backward API compatibility
2312 // LCOV_EXCL_START
2313 
2314 #include <crm/common/xml_compat.h>
2315 
2316 xmlNode *
2317 find_entity(xmlNode *parent, const char *node_name, const char *id)
2318 {
2319  return pcmk__xe_first_child(parent, node_name,
2320  ((id == NULL)? id : PCMK_XA_ID), id);
2321 }
2322 
2323 void
2325 {
2326  free_xml(data);
2327 }
2328 
2329 xmlDoc *
2330 getDocPtr(xmlNode *node)
2331 {
2332  xmlDoc *doc = NULL;
2333 
2334  CRM_CHECK(node != NULL, return NULL);
2335 
2336  doc = node->doc;
2337  if (doc == NULL) {
2338  doc = xmlNewDoc(PCMK__XML_VERSION);
2339  xmlDocSetRootElement(doc, node);
2340  }
2341  return doc;
2342 }
2343 
2344 xmlNode *
2345 add_node_copy(xmlNode *parent, xmlNode *src_node)
2346 {
2347  xmlNode *child = NULL;
2348 
2349  CRM_CHECK((parent != NULL) && (src_node != NULL), return NULL);
2350 
2351  child = xmlDocCopyNode(src_node, parent->doc, 1);
2352  if (child == NULL) {
2353  return NULL;
2354  }
2355  xmlAddChild(parent, child);
2356  pcmk__xml_mark_created(child);
2357  return child;
2358 }
2359 
2360 int
2361 add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
2362 {
2363  add_node_copy(parent, child);
2364  free_xml(child);
2365  return 1;
2366 }
2367 
2368 gboolean
2369 xml_has_children(const xmlNode * xml_root)
2370 {
2371  if (xml_root != NULL && xml_root->children != NULL) {
2372  return TRUE;
2373  }
2374  return FALSE;
2375 }
2376 
2377 static char *
2378 replace_text(char *text, size_t *index, size_t *length, const char *replace)
2379 {
2380  // We have space for 1 char already
2381  size_t offset = strlen(replace) - 1;
2382 
2383  if (offset > 0) {
2384  *length += offset;
2385  text = pcmk__realloc(text, *length + 1);
2386 
2387  // Shift characters to the right to make room for the replacement string
2388  for (size_t i = *length; i > (*index + offset); i--) {
2389  text[i] = text[i - offset];
2390  }
2391  }
2392 
2393  // Replace the character at index by the replacement string
2394  memcpy(text + *index, replace, offset + 1);
2395 
2396  // Reset index to the end of replacement string
2397  *index += offset;
2398  return text;
2399 }
2400 
2401 char *
2402 crm_xml_escape(const char *text)
2403 {
2404  size_t length = 0;
2405  char *copy = NULL;
2406 
2407  if (text == NULL) {
2408  return NULL;
2409  }
2410 
2411  length = strlen(text);
2412  copy = pcmk__str_copy(text);
2413  for (size_t index = 0; index <= length; index++) {
2414  if(copy[index] & 0x80 && copy[index+1] & 0x80){
2415  index++;
2416  continue;
2417  }
2418  switch (copy[index]) {
2419  case 0:
2420  // Sanity only; loop should stop at the last non-null byte
2421  break;
2422  case '<':
2423  copy = replace_text(copy, &index, &length, "&lt;");
2424  break;
2425  case '>':
2426  copy = replace_text(copy, &index, &length, "&gt;");
2427  break;
2428  case '"':
2429  copy = replace_text(copy, &index, &length, "&quot;");
2430  break;
2431  case '\'':
2432  copy = replace_text(copy, &index, &length, "&apos;");
2433  break;
2434  case '&':
2435  copy = replace_text(copy, &index, &length, "&amp;");
2436  break;
2437  case '\t':
2438  /* Might as well just expand to a few spaces... */
2439  copy = replace_text(copy, &index, &length, " ");
2440  break;
2441  case '\n':
2442  copy = replace_text(copy, &index, &length, "\\n");
2443  break;
2444  case '\r':
2445  copy = replace_text(copy, &index, &length, "\\r");
2446  break;
2447  default:
2448  /* Check for and replace non-printing characters with their octal equivalent */
2449  if(copy[index] < ' ' || copy[index] > '~') {
2450  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
2451 
2452  copy = replace_text(copy, &index, &length, replace);
2453  free(replace);
2454  }
2455  }
2456  }
2457  return copy;
2458 }
2459 
2460 xmlNode *
2461 copy_xml(xmlNode *src)
2462 {
2463  xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
2464  xmlNode *copy = NULL;
2465 
2466  pcmk__mem_assert(doc);
2467 
2468  copy = xmlDocCopyNode(src, doc, 1);
2469  pcmk__mem_assert(copy);
2470 
2471  xmlDocSetRootElement(doc, copy);
2472  return copy;
2473 }
2474 
2475 xmlNode *
2476 create_xml_node(xmlNode *parent, const char *name)
2477 {
2478  // Like pcmk__xe_create(), but returns NULL on failure
2479  xmlNode *node = NULL;
2480 
2481  CRM_CHECK(!pcmk__str_empty(name), return NULL);
2482 
2483  if (parent == NULL) {
2484  xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
2485 
2486  if (doc == NULL) {
2487  return NULL;
2488  }
2489 
2490  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
2491  if (node == NULL) {
2492  xmlFreeDoc(doc);
2493  return NULL;
2494  }
2495  xmlDocSetRootElement(doc, node);
2496 
2497  } else {
2498  node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
2499  if (node == NULL) {
2500  return NULL;
2501  }
2502  }
2503  pcmk__xml_mark_created(node);
2504  return node;
2505 }
2506 
2507 xmlNode *
2508 pcmk_create_xml_text_node(xmlNode *parent, const char *name,
2509  const char *content)
2510 {
2511  xmlNode *node = pcmk__xe_create(parent, name);
2512 
2513  pcmk__xe_set_content(node, "%s", content);
2514  return node;
2515 }
2516 
2517 xmlNode *
2518 pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id,
2519  const char *class_name, const char *text)
2520 {
2521  xmlNode *node = pcmk__html_create(parent, element_name, id, class_name);
2522 
2523  pcmk__xe_set_content(node, "%s", text);
2524  return node;
2525 }
2526 
2527 xmlNode *
2528 first_named_child(const xmlNode *parent, const char *name)
2529 {
2530  return pcmk__xe_first_child(parent, name, NULL, NULL);
2531 }
2532 
2533 xmlNode *
2534 find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
2535 {
2536  xmlNode *result = NULL;
2537 
2538  if (search_path == NULL) {
2539  crm_warn("Will never find <NULL>");
2540  return NULL;
2541  }
2542 
2543  result = pcmk__xe_first_child(root, search_path, NULL, NULL);
2544 
2545  if (must_find && (result == NULL)) {
2546  crm_warn("Could not find %s in %s",
2547  search_path,
2548  ((root != NULL)? (const char *) root->name : "<NULL>"));
2549  }
2550 
2551  return result;
2552 }
2553 
2554 xmlNode *
2555 crm_next_same_xml(const xmlNode *sibling)
2556 {
2557  return pcmk__xe_next_same(sibling);
2558 }
2559 
2560 void
2561 xml_remove_prop(xmlNode * obj, const char *name)
2562 {
2563  pcmk__xe_remove_attr(obj, name);
2564 }
2565 
2566 gboolean
2567 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2568 {
2569  bool is_match = false;
2570  const char *child_id = NULL;
2571  const char *update_id = NULL;
2572 
2573  CRM_CHECK(child != NULL, return FALSE);
2574  CRM_CHECK(update != NULL, return FALSE);
2575 
2576  child_id = pcmk__xe_id(child);
2577  update_id = pcmk__xe_id(update);
2578 
2579  /* Match element name and (if provided in update XML) element ID. Don't
2580  * match search root (child is search root if parent == NULL).
2581  */
2582  is_match = (parent != NULL)
2583  && pcmk__xe_is(update, (const char *) child->name)
2584  && ((update_id == NULL)
2585  || pcmk__str_eq(update_id, child_id, pcmk__str_none));
2586 
2587  /* For deletion, match all attributes provided in update. A matching node
2588  * can have additional attributes, but values must match for provided ones.
2589  */
2590  if (is_match && delete_only) {
2591  for (xmlAttr *attr = pcmk__xe_first_attr(update); attr != NULL;
2592  attr = attr->next) {
2593  const char *name = (const char *) attr->name;
2594  const char *update_val = pcmk__xml_attr_value(attr);
2595  const char *child_val = crm_element_value(child, name);
2596 
2597  if (!pcmk__str_eq(update_val, child_val, pcmk__str_casei)) {
2598  is_match = false;
2599  break;
2600  }
2601  }
2602  }
2603 
2604  if (is_match) {
2605  if (delete_only) {
2606  crm_log_xml_trace(child, "delete-match");
2607  crm_log_xml_trace(update, "delete-search");
2608  free_xml(child);
2609 
2610  } else {
2611  crm_log_xml_trace(child, "replace-match");
2612  crm_log_xml_trace(update, "replace-with");
2613  replace_node(child, update);
2614  }
2615  return TRUE;
2616  }
2617 
2618  // Current node not a match; search the rest of the subtree depth-first
2619  parent = child;
2620  for (child = pcmk__xml_first_child(parent); child != NULL;
2621  child = pcmk__xml_next(child)) {
2622 
2623  // Only delete/replace the first match
2624  if (replace_xml_child(parent, child, update, delete_only)) {
2625  return TRUE;
2626  }
2627  }
2628 
2629  // No match found in this subtree
2630  return FALSE;
2631 }
2632 
2633 gboolean
2634 update_xml_child(xmlNode *child, xmlNode *to_update)
2635 {
2636  return pcmk__xe_update_match(child, to_update,
2638 }
2639 
2640 int
2641 find_xml_children(xmlNode **children, xmlNode *root, const char *tag,
2642  const char *field, const char *value, gboolean search_matches)
2643 {
2644  int match_found = 0;
2645 
2646  CRM_CHECK(root != NULL, return FALSE);
2647  CRM_CHECK(children != NULL, return FALSE);
2648 
2649  if ((tag != NULL) && !pcmk__xe_is(root, tag)) {
2650 
2651  } else if ((value != NULL)
2652  && !pcmk__str_eq(value, crm_element_value(root, field),
2653  pcmk__str_casei)) {
2654 
2655  } else {
2656  if (*children == NULL) {
2657  *children = pcmk__xe_create(NULL, __func__);
2658  }
2659  pcmk__xml_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,
2669  search_matches);
2670  }
2671  }
2672 
2673  return match_found;
2674 }
2675 
2676 void
2678 {
2679  /* TODO: Remove recursion and use xpath searches for value++ */
2680  xmlNode *child = NULL;
2681 
2682  for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
2683  const char *p_name = (const char *) a->name;
2684  const char *p_value = pcmk__xml_attr_value(a);
2685 
2686  expand_plus_plus(target, p_name, p_value);
2687  }
2688  for (child = pcmk__xe_first_child(target, NULL, NULL, NULL); child != NULL;
2689  child = pcmk__xe_next(child)) {
2690 
2691  fix_plus_plus_recursive(child);
2692  }
2693 }
2694 
2695 void
2696 copy_in_properties(xmlNode *target, const xmlNode *src)
2697 {
2698  if (src == NULL) {
2699  crm_warn("No node to copy properties from");
2700 
2701  } else if (target == NULL) {
2702  crm_err("No node to copy properties into");
2703 
2704  } else {
2705  for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
2706  const char *p_name = (const char *) a->name;
2707  const char *p_value = pcmk__xml_attr_value(a);
2708 
2709  expand_plus_plus(target, p_name, p_value);
2710  if (xml_acl_denied(target)) {
2711  crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name);
2712  return;
2713  }
2714  }
2715  }
2716 }
2717 
2718 void
2719 expand_plus_plus(xmlNode * target, const char *name, const char *value)
2720 {
2721  pcmk__xe_set_score(target, name, value);
2722 }
2723 
2724 // LCOV_EXCL_STOP
2725 // End deprecated API
#define LOG_TRACE
Definition: logging.h:38
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml.c:2288
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:60
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:1541
A dumping ground.
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition: nvpair.c:145
#define PCMK__XML_ENTITY_LT
char data[0]
Definition: cpg.c:58
#define pcmk__if_tracing(if_action, else_action)
bool pcmk__xml_tree_foreach(xmlNode *xml, bool(*fn)(xmlNode *, void *), void *user_data)
Definition: xml.c:42
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml.c:652
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:101
void pcmk__free_acls(GList *acls)
Definition: acl.c:43
const char * name
Definition: cib.c:26
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition: xml.c:303
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml_attr.c:31
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:1549
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:348
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:976
#define pcmk__config_err(fmt...)
void fix_plus_plus_recursive(xmlNode *target)
Definition: xml.c:2677
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition: xml.c:2121
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2528
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:720
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:301
void pcmk_free_xml_subtree(xmlNode *xml)
Definition: xml.c:795
#define PCMK_XA_CIB_LAST_WRITTEN
Definition: xml_names.h:239
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml.c:2279
int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
Definition: xml.c:1839
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition: acl.c:573
#define XML_DOC_PRIVATE_MAGIC
Definition: xml.c:175
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:228
void crm_schema_init(void)
Definition: schemas.c:470
enum crm_ais_msg_types type
Definition: cpg.c:51
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Definition: xml.c:2555
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:883
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:608
void crm_schema_cleanup(void)
Definition: schemas.c:643
const char * pcmk__env_option(const char *option)
Definition: options.c:1088
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:440
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:189
Deprecated Pacemaker XML API.
G_GNUC_INTERNAL bool pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data)
Definition: xml_attr.c:43
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, uint32_t flags, bool as_diff)
Definition: xml.c:1675
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:2641
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Definition: xml.c:2719
gchar * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1110
#define PCMK__ENV_SCHEMA_DIRECTORY
int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition: xml.c:342
xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
Definition: xml.c:385
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition: xml.c:2184
#define crm_warn(fmt, args...)
Definition: logging.h:394
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml.c:2261
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:923
xmlNode * copy_xml(xmlNode *src)
Definition: xml.c:2461
int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
Definition: xml.c:1952
pcmk__xml_escape_type
Definition: xml_internal.h:276
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2152
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:674
#define PCMK_XA_ID_REF
Definition: xml_names.h:298
#define PCMK__XML_ENTITY_GT
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:2476
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
void free_xml(xmlNode *child)
Definition: xml.c:867
#define crm_trace(fmt, args...)
Definition: logging.h:404
#define XML_NODE_PRIVATE_MAGIC
Definition: xml.c:176
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
void pcmk__mark_xml_node_dirty(xmlNode *xml)
Definition: xml.c:101
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:98
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Definition: xml.c:89
Wrappers for and extensions to libxml2.
bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
Definition: xml.c:674
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition: xml.c:998
#define PCMK_XA_ID
Definition: xml_names.h:296
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition: xml.c:2241
void crm_xml_cleanup(void)
Definition: xml.c:2143
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition: xml.c:2345
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:2330
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition: acl.c:288
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:116
#define pcmk__str_copy(str)
xmlNode * pcmk__html_create(xmlNode *parent, const char *name, const char *id, const char *class)
Definition: output_html.c:484
int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
Definition: xml.c:584
const xmlChar * pcmkXmlStr
Definition: xml.h:41
xml_private_flags
Definition: xml_internal.h:519
const char * target
Definition: pcmk_fence.c:29
int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
Definition: xml.c:2046
#define CRM_XS
Definition: logging.h:56
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1028
void copy_in_properties(xmlNode *target, const xmlNode *src)
Definition: xml.c:2696
pcmk__xml_artefact_ns
Definition: xml_internal.h:207
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2108
pcmk__action_result_t result
Definition: pcmk_fence.c:35
void pcmk__xml_mark_created(xmlNode *xml)
Definition: xml.c:160
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:2369
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:391
#define CRM_ASSERT(expr)
Definition: results.h:42
#define CRM_SCHEMA_DIRECTORY
Definition: config.h:45
char * pcmk__epoch2str(const time_t *source, uint32_t flags)
Definition: iso8601.c:2075
const char * pcmk__remote_schema_dir(void)
Definition: schemas.c:1571
xmlNode * input
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:658
#define PCMK__XML_VERSION
libxml2 supports only XML version 1.0, at least as of libxml2-2.12.5
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:230
void xml_accept_changes(xmlNode *xml)
Definition: xml.c:402
#define pcmk__mem_assert(ptr)
void crm_destroy_xml(gpointer data)
Definition: xml.c:2324
#define PCMK__XML_ENTITY_QUOT
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition: nvpair.c:200
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition: acl.c:616
char * crm_xml_escape(const char *text)
Definition: xml.c:2402
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
Definition: xml.c:2518
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2634
GString * pcmk__element_xpath(const xmlNode *xml)
Definition: xpath.c:256
void pcmk__xe_set_content(xmlNode *node, const char *format,...)
Definition: xml.c:758
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:2508
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2072
#define crm_log_xml_trace(xml, text)
Definition: logging.h:412
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:2534
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:318
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:692
const char * parent
Definition: cib.c:27
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:2361
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
#define PCMK__XML_ENTITY_AMP
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2567
void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml.c:1622
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2561
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:959
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:2317
GSList * pcmk_xml_attrs2nvpairs(const xmlNode *xml)
Create a list of name/value pairs from an XML node&#39;s attributes.
Definition: nvpair.c:160
xmlNode * pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
Definition: xml.c:1573
uint64_t flags
Definition: remote.c:215
Don&#39;t overwrite existing values.
Definition: xml_internal.h:441
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:325
int pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
Definition: xml.c:525