pacemaker  2.1.9-49aab99839
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 
29 #define XML_VERSION ((pcmkXmlStr) "1.0")
30 
44 bool
45 pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
46  void *user_data)
47 {
48  if (xml == NULL) {
49  return true;
50  }
51 
52  if (!fn(xml, user_data)) {
53  return false;
54  }
55 
56  for (xml = pcmk__xml_first_child(xml); xml != NULL;
57  xml = pcmk__xml_next(xml)) {
58 
59  if (!pcmk__xml_tree_foreach(xml, fn, user_data)) {
60  return false;
61  }
62  }
63  return true;
64 }
65 
66 bool
67 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
68 {
69  if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
70  return FALSE;
71  } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
73  return FALSE;
74  } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
75  pcmk__xf_lazy)) {
76  return FALSE;
77  }
78  return TRUE;
79 }
80 
81 void
82 pcmk__xml_set_parent_flags(xmlNode *xml, uint64_t flags)
83 {
84  for (; xml != NULL; xml = xml->parent) {
85  xml_node_private_t *nodepriv = xml->_private;
86 
87  if (nodepriv != NULL) {
88  pcmk__set_xml_flags(nodepriv, flags);
89  }
90  }
91 }
92 
93 void
94 pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
95 {
96  if (xml != NULL) {
97  xml_doc_private_t *docpriv = xml->doc->_private;
98 
99  pcmk__set_xml_flags(docpriv, flag);
100  }
101 }
102 
103 // Mark document, element, and all element's parents as changed
104 void
106 {
109 }
110 
122 static bool
123 reset_xml_node_flags(xmlNode *xml, void *user_data)
124 {
125  xml_node_private_t *nodepriv = xml->_private;
126 
127  if (nodepriv != NULL) {
128  nodepriv->flags = pcmk__xf_none;
129  }
130  return true;
131 }
132 
144 static bool
145 mark_xml_dirty_created(xmlNode *xml, void *user_data)
146 {
147  xml_node_private_t *nodepriv = xml->_private;
148 
149  if (nodepriv != NULL) {
151  }
152  return true;
153 }
154 
163 static void
164 mark_xml_tree_dirty_created(xmlNode *xml)
165 {
166  pcmk__assert(xml != NULL);
167 
168  if (!pcmk__tracking_xml_changes(xml, false)) {
169  // Tracking is disabled for entire document
170  return;
171  }
172 
173  // Mark all parents and document dirty
175 
176  pcmk__xml_tree_foreach(xml, mark_xml_dirty_created, NULL);
177 }
178 
179 // Free an XML object previously marked as deleted
180 static void
181 free_deleted_object(void *data)
182 {
183  if(data) {
184  pcmk__deleted_xml_t *deleted_obj = data;
185 
186  g_free(deleted_obj->path);
187  free(deleted_obj);
188  }
189 }
190 
191 // Free and NULL user, ACLs, and deleted objects in an XML node's private data
192 static void
193 reset_xml_private_data(xml_doc_private_t *docpriv)
194 {
195  if (docpriv != NULL) {
197 
198  free(docpriv->user);
199  docpriv->user = NULL;
200 
201  if (docpriv->acls != NULL) {
202  pcmk__free_acls(docpriv->acls);
203  docpriv->acls = NULL;
204  }
205 
206  if(docpriv->deleted_objs) {
207  g_list_free_full(docpriv->deleted_objs, free_deleted_object);
208  docpriv->deleted_objs = NULL;
209  }
210  }
211 }
212 
224 static bool
225 new_private_data(xmlNode *node, void *user_data)
226 {
227  CRM_CHECK(node != NULL, return true);
228 
229  if (node->_private != NULL) {
230  return true;
231  }
232 
233  switch (node->type) {
234  case XML_DOCUMENT_NODE:
235  {
236  xml_doc_private_t *docpriv =
238 
240  node->_private = docpriv;
242  }
243  break;
244 
245  case XML_ELEMENT_NODE:
246  case XML_ATTRIBUTE_NODE:
247  case XML_COMMENT_NODE:
248  {
249  xml_node_private_t *nodepriv =
251 
253  node->_private = nodepriv;
255 
256  for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
257  iter = iter->next) {
258 
259  new_private_data((xmlNode *) iter, user_data);
260  }
261  }
262  break;
263 
264  case XML_TEXT_NODE:
265  case XML_DTD_NODE:
266  case XML_CDATA_SECTION_NODE:
267  return true;
268 
269  default:
270  CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
271  return true;
272  }
273 
274  if (pcmk__tracking_xml_changes(node, false)) {
276  }
277  return true;
278 }
279 
291 static bool
292 free_private_data(xmlNode *node, void *user_data)
293 {
294  CRM_CHECK(node != NULL, return true);
295 
296  if (node->_private == NULL) {
297  return true;
298  }
299 
300  if (node->type == XML_DOCUMENT_NODE) {
301  reset_xml_private_data((xml_doc_private_t *) node->_private);
302 
303  } else {
304  xml_node_private_t *nodepriv = node->_private;
305 
307 
308  for (xmlAttr *iter = pcmk__xe_first_attr(node); iter != NULL;
309  iter = iter->next) {
310 
311  free_private_data((xmlNode *) iter, user_data);
312  }
313  }
314  free(node->_private);
315  node->_private = NULL;
316  return true;
317 }
318 
325 void
327 {
328  pcmk__xml_tree_foreach(xml, new_private_data, NULL);
329 }
330 
337 void
339 {
340  pcmk__xml_tree_foreach(xml, free_private_data, NULL);
341 }
342 
343 void
344 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
345 {
346  xml_accept_changes(xml);
347  crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
349  if(enforce_acls) {
350  if(acl_source == NULL) {
351  acl_source = xml;
352  }
354  pcmk__unpack_acl(acl_source, xml, user);
355  pcmk__apply_acl(xml);
356  }
357 }
358 
359 bool xml_tracking_changes(xmlNode * xml)
360 {
361  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
362  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
364 }
365 
366 bool xml_document_dirty(xmlNode *xml)
367 {
368  return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
369  && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
371 }
372 
382 int
383 pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
384 {
385  int position = 0;
386 
387  for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
388  xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
389 
390  if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
391  position++;
392  }
393  }
394 
395  return position;
396 }
397 
409 static bool
410 accept_attr_deletions(xmlNode *xml, void *user_data)
411 {
412  reset_xml_node_flags(xml, NULL);
414  return true;
415 }
416 
425 xmlNode *
426 pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
427 {
428  CRM_CHECK(needle != NULL, return NULL);
429 
430  if (needle->type == XML_COMMENT_NODE) {
431  return pcmk__xc_match(haystack, needle, exact);
432 
433  } else {
434  const char *id = pcmk__xe_id(needle);
435  const char *attr = (id == NULL)? NULL : PCMK_XA_ID;
436 
437  return pcmk__xe_first_child(haystack, (const char *) needle->name, attr,
438  id);
439  }
440 }
441 
442 void
443 xml_accept_changes(xmlNode * xml)
444 {
445  xmlNode *top = NULL;
446  xml_doc_private_t *docpriv = NULL;
447 
448  if(xml == NULL) {
449  return;
450  }
451 
452  crm_trace("Accepting changes to %p", xml);
453  docpriv = xml->doc->_private;
454  top = xmlDocGetRootElement(xml->doc);
455 
456  reset_xml_private_data(xml->doc->_private);
457 
458  if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
459  docpriv->flags = pcmk__xf_none;
460  return;
461  }
462 
463  docpriv->flags = pcmk__xf_none;
464  pcmk__xml_tree_foreach(top, accept_attr_deletions, NULL);
465 }
466 
480 xmlNode *
481 pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
482  const char *attr_n, const char *attr_v)
483 {
484  xmlNode *child = NULL;
485  const char *parent_name = "<null>";
486 
487  CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL);
488 
489  if (parent != NULL) {
490  child = parent->children;
491  while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
492  child = child->next;
493  }
494 
495  parent_name = (const char *) parent->name;
496  }
497 
498  for (; child != NULL; child = pcmk__xe_next(child)) {
499  const char *value = NULL;
500 
501  if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) {
502  // Node name mismatch
503  continue;
504  }
505  if (attr_n == NULL) {
506  // No attribute match needed
507  return child;
508  }
509 
510  value = crm_element_value(child, attr_n);
511 
512  if ((attr_v == NULL) && (value != NULL)) {
513  // attr_v == NULL: Attribute attr_n must be set (to any value)
514  return child;
515  }
516  if ((attr_v != NULL) && (pcmk__str_eq(value, attr_v, pcmk__str_none))) {
517  // attr_v != NULL: Attribute attr_n must be set to value attr_v
518  return child;
519  }
520  }
521 
522  if (node_name == NULL) {
523  node_name = "(any)"; // For logging
524  }
525  if (attr_n != NULL) {
526  crm_trace("XML child node <%s %s=%s> not found in %s",
527  node_name, attr_n, attr_v, parent_name);
528  } else {
529  crm_trace("XML child node <%s> not found in %s",
530  node_name, parent_name);
531  }
532  return NULL;
533 }
534 
548 int
549 pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score,
550  int default_score)
551 {
552  const char *value = NULL;
553 
554  CRM_CHECK((xml != NULL) && (name != NULL), return EINVAL);
555  value = crm_element_value(xml, name);
556  return pcmk_parse_score(value, score, default_score);
557 }
558 
590 int
591 pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
592 {
593  const char *old_value = NULL;
594 
595  CRM_CHECK((target != NULL) && (name != NULL), return EINVAL);
596 
597  if (value == NULL) {
598  // @TODO Maybe instead delete the attribute or set it to 0
599  return pcmk_rc_ok;
600  }
601 
602  old_value = crm_element_value(target, name);
603 
604  // If no previous value, skip to default case and set the value unexpanded.
605  if (old_value != NULL) {
606  const char *n = name;
607  const char *v = value;
608 
609  // Stop at first character that differs between name and value
610  for (; (*n == *v) && (*n != '\0'); n++, v++);
611 
612  // If value begins with name followed by a "++" or "+="
613  if ((*n == '\0')
614  && (*v++ == '+')
615  && ((*v == '+') || (*v == '='))) {
616 
617  int add = 1;
618  int old_value_i = 0;
619  int rc = pcmk_rc_ok;
620 
621  // If we're expanding ourselves, no previous value was set; use 0
622  if (old_value != value) {
623  rc = pcmk_parse_score(old_value, &old_value_i, 0);
624  if (rc != pcmk_rc_ok) {
625  // @TODO This is inconsistent with old_value==NULL
626  crm_trace("Using 0 before incrementing %s because '%s' "
627  "is not a score", name, old_value);
628  }
629  }
630 
631  /* value="X++": new value of X is old_value + 1
632  * value="X+=Y": new value of X is old_value + Y (for some number Y)
633  */
634  if (*v != '+') {
635  rc = pcmk_parse_score(++v, &add, 0);
636  if (rc != pcmk_rc_ok) {
637  // @TODO We should probably skip expansion instead
638  crm_trace("Not incrementing %s because '%s' does not have "
639  "a valid increment", name, value);
640  }
641  }
642 
643  crm_xml_add_int(target, name, pcmk__add_scores(old_value_i, add));
644  return pcmk_rc_ok;
645  }
646  }
647 
648  // Default case: set the attribute unexpanded (with value treated literally)
649  if (old_value != value) {
650  crm_xml_add(target, name, value);
651  }
652  return pcmk_rc_ok;
653 }
654 
668 int
669 pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
670 {
671  CRM_CHECK((src != NULL) && (target != NULL), return EINVAL);
672 
673  for (xmlAttr *attr = pcmk__xe_first_attr(src); attr != NULL;
674  attr = attr->next) {
675 
676  const char *name = (const char *) attr->name;
677  const char *value = pcmk__xml_attr_value(attr);
678 
680  && (crm_element_value(target, name) != NULL)) {
681  continue;
682  }
683 
685  pcmk__xe_set_score(target, name, value);
686  } else {
687  crm_xml_add(target, name, value);
688  }
689  }
690 
691  return pcmk_rc_ok;
692 }
693 
701 void
702 pcmk__xe_remove_attr(xmlNode *element, const char *name)
703 {
704  if (name != NULL) {
705  pcmk__xa_remove(xmlHasProp(element, (pcmkXmlStr) name), false);
706  }
707 }
708 
723 bool
724 pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
725 {
726  const char *name = user_data;
727 
729  return true;
730 }
731 
741 void
743  bool (*match)(xmlAttrPtr, void *),
744  void *user_data)
745 {
746  xmlAttrPtr next = NULL;
747 
748  for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
749  next = a->next; // Grab now because attribute might get removed
750  if ((match == NULL) || match(a, user_data)) {
751  if (pcmk__xa_remove(a, false) != pcmk_rc_ok) {
752  return;
753  }
754  }
755  }
756 }
757 
769 xmlNode *
770 pcmk__xe_create(xmlNode *parent, const char *name)
771 {
772  xmlNode *node = NULL;
773 
774  pcmk__assert(!pcmk__str_empty(name));
775 
776  if (parent == NULL) {
777  xmlDoc *doc = pcmk__xml_new_doc();
778 
779  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
780  pcmk__mem_assert(node);
781 
782  xmlDocSetRootElement(doc, node);
783 
784  } else {
785  node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
786  pcmk__mem_assert(node);
787  }
788 
790  return node;
791 }
792 
802 xmlDoc *
804 {
805  xmlDoc *doc = xmlNewDoc(XML_VERSION);
806 
807  pcmk__mem_assert(doc);
808  pcmk__xml_new_private_data((xmlNode *) doc);
809  return doc;
810 }
811 
818 void
819 pcmk__xml_free_doc(xmlDoc *doc)
820 {
821  if (doc != NULL) {
822  pcmk__xml_free_private_data((xmlNode *) doc);
823  xmlFreeDoc(doc);
824  }
825 }
826 
838 G_GNUC_PRINTF(2, 3)
839 void
840 pcmk__xe_set_content(xmlNode *node, const char *format, ...)
841 {
842  if (node != NULL) {
843  const char *content = NULL;
844  char *buf = NULL;
845 
846  /* xmlNodeSetContent() frees node->children and replaces it with new
847  * text. If this function is called for a node that already has a non-
848  * text child, it's a bug.
849  */
850  CRM_CHECK((node->children == NULL)
851  || (node->children->type == XML_TEXT_NODE),
852  return);
853 
854  if (strchr(format, '%') == NULL) {
855  // Nothing to format
856  content = format;
857 
858  } else {
859  va_list ap;
860 
861  va_start(ap, format);
862 
863  if (pcmk__str_eq(format, "%s", pcmk__str_none)) {
864  // No need to make a copy
865  content = va_arg(ap, const char *);
866 
867  } else {
868  pcmk__assert(vasprintf(&buf, format, ap) >= 0);
869  content = buf;
870  }
871  va_end(ap);
872  }
873 
874  xmlNodeSetContent(node, (pcmkXmlStr) content);
875  free(buf);
876  }
877 }
878 
884 void
886 {
888  xmlUnlinkNode(xml); // Detaches from parent and siblings
889  xmlFreeNode(xml); // Frees
890 }
891 
892 static void
893 free_xml_with_position(xmlNode *child, int position)
894 {
895  xmlDoc *doc = NULL;
896  xml_node_private_t *nodepriv = NULL;
897 
898  if (child == NULL) {
899  return;
900  }
901  doc = child->doc;
902  nodepriv = child->_private;
903 
904  if ((doc != NULL) && (xmlDocGetRootElement(doc) == child)) {
905  // Free everything
906  pcmk__xml_free_doc(doc);
907  return;
908  }
909 
910  if (!pcmk__check_acl(child, NULL, pcmk__xf_acl_write)) {
911  GString *xpath = NULL;
912 
913  pcmk__if_tracing({}, return);
914  xpath = pcmk__element_xpath(child);
915  qb_log_from_external_source(__func__, __FILE__,
916  "Cannot remove %s %x", LOG_TRACE,
917  __LINE__, 0, xpath->str, nodepriv->flags);
918  g_string_free(xpath, TRUE);
919  return;
920  }
921 
922  if ((doc != NULL) && pcmk__tracking_xml_changes(child, false)
923  && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
924 
925  xml_doc_private_t *docpriv = doc->_private;
926  GString *xpath = pcmk__element_xpath(child);
927 
928  if (xpath != NULL) {
929  pcmk__deleted_xml_t *deleted_obj = NULL;
930 
931  crm_trace("Deleting %s %p from %p", xpath->str, child, doc);
932 
933  deleted_obj = pcmk__assert_alloc(1, sizeof(pcmk__deleted_xml_t));
934  deleted_obj->path = g_string_free(xpath, FALSE);
935  deleted_obj->position = -1;
936 
937  // Record the position only for XML comments for now
938  if (child->type == XML_COMMENT_NODE) {
939  if (position >= 0) {
940  deleted_obj->position = position;
941 
942  } else {
943  deleted_obj->position = pcmk__xml_position(child,
944  pcmk__xf_skip);
945  }
946  }
947 
948  docpriv->deleted_objs = g_list_append(docpriv->deleted_objs,
949  deleted_obj);
951  }
952  }
953  pcmk_free_xml_subtree(child);
954 }
955 
956 
957 void
958 free_xml(xmlNode * child)
959 {
960  free_xml_with_position(child, -1);
961 }
962 
973 xmlNode *
974 pcmk__xml_copy(xmlNode *parent, xmlNode *src)
975 {
976  xmlNode *copy = NULL;
977 
978  if (src == NULL) {
979  return NULL;
980  }
981 
982  if (parent == NULL) {
983  xmlDoc *doc = NULL;
984 
985  // The copy will be the root element of a new document
986  pcmk__assert(src->type == XML_ELEMENT_NODE);
987 
988  doc = pcmk__xml_new_doc();
989  copy = xmlDocCopyNode(src, doc, 1);
990  pcmk__mem_assert(copy);
991 
992  xmlDocSetRootElement(doc, copy);
993 
994  } else {
995  copy = xmlDocCopyNode(src, parent->doc, 1);
996  pcmk__mem_assert(copy);
997 
998  xmlAddChild(parent, copy);
999  }
1000 
1002  return copy;
1003 }
1004 
1011 void
1013 {
1014  xmlNode *iter = xml->children;
1015 
1016  while (iter) {
1017  xmlNode *next = iter->next;
1018 
1019  switch (iter->type) {
1020  case XML_TEXT_NODE:
1021  /* Remove it */
1022  pcmk_free_xml_subtree(iter);
1023  break;
1024 
1025  case XML_ELEMENT_NODE:
1026  /* Search it */
1027  pcmk__strip_xml_text(iter);
1028  break;
1029 
1030  default:
1031  /* Leave it */
1032  break;
1033  }
1034 
1035  iter = next;
1036  }
1037 }
1038 
1047 const char *
1049 {
1050  char *now_s = pcmk__epoch2str(NULL, 0);
1051  const char *result = NULL;
1052 
1054  pcmk__s(now_s, "Could not determine current time"));
1055  free(now_s);
1056  return result;
1057 }
1058 
1064 void
1066 {
1067  char *c;
1068 
1069  for (c = id; *c; ++c) {
1070  /* @TODO Sanitize more comprehensively */
1071  switch (*c) {
1072  case ':':
1073  case '#':
1074  *c = '.';
1075  }
1076  }
1077 }
1078 
1086 void
1087 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1088 {
1089  va_list ap;
1090  int len = 0;
1091  char *id = NULL;
1092 
1093  /* equivalent to crm_strdup_printf() */
1094  va_start(ap, format);
1095  len = vasprintf(&id, format, ap);
1096  va_end(ap);
1097  pcmk__assert(len > 0);
1098 
1099  crm_xml_sanitize_id(id);
1100  crm_xml_add(xml, PCMK_XA_ID, id);
1101  free(id);
1102 }
1103 
1116 bool
1118 {
1119  if (text == NULL) {
1120  return false;
1121  }
1122 
1123  while (*text != '\0') {
1124  switch (type) {
1125  case pcmk__xml_escape_text:
1126  switch (*text) {
1127  case '<':
1128  case '>':
1129  case '&':
1130  return true;
1131  case '\n':
1132  case '\t':
1133  break;
1134  default:
1135  if (g_ascii_iscntrl(*text)) {
1136  return true;
1137  }
1138  break;
1139  }
1140  break;
1141 
1142  case pcmk__xml_escape_attr:
1143  switch (*text) {
1144  case '<':
1145  case '>':
1146  case '&':
1147  case '"':
1148  return true;
1149  default:
1150  if (g_ascii_iscntrl(*text)) {
1151  return true;
1152  }
1153  break;
1154  }
1155  break;
1156 
1158  switch (*text) {
1159  case '\n':
1160  case '\r':
1161  case '\t':
1162  case '"':
1163  return true;
1164  default:
1165  break;
1166  }
1167  break;
1168 
1169  default: // Invalid enum value
1170  pcmk__assert(false);
1171  break;
1172  }
1173 
1174  text = g_utf8_next_char(text);
1175  }
1176  return false;
1177 }
1178 
1198 gchar *
1200 {
1201  GString *copy = NULL;
1202 
1203  if (text == NULL) {
1204  return NULL;
1205  }
1206  copy = g_string_sized_new(strlen(text));
1207 
1208  while (*text != '\0') {
1209  // Don't escape any non-ASCII characters
1210  if ((*text & 0x80) != 0) {
1211  size_t bytes = g_utf8_next_char(text) - text;
1212 
1213  g_string_append_len(copy, text, bytes);
1214  text += bytes;
1215  continue;
1216  }
1217 
1218  switch (type) {
1219  case pcmk__xml_escape_text:
1220  switch (*text) {
1221  case '<':
1222  g_string_append(copy, PCMK__XML_ENTITY_LT);
1223  break;
1224  case '>':
1225  g_string_append(copy, PCMK__XML_ENTITY_GT);
1226  break;
1227  case '&':
1228  g_string_append(copy, PCMK__XML_ENTITY_AMP);
1229  break;
1230  case '\n':
1231  case '\t':
1232  g_string_append_c(copy, *text);
1233  break;
1234  default:
1235  if (g_ascii_iscntrl(*text)) {
1236  g_string_append_printf(copy, "&#x%.2X;", *text);
1237  } else {
1238  g_string_append_c(copy, *text);
1239  }
1240  break;
1241  }
1242  break;
1243 
1244  case pcmk__xml_escape_attr:
1245  switch (*text) {
1246  case '<':
1247  g_string_append(copy, PCMK__XML_ENTITY_LT);
1248  break;
1249  case '>':
1250  g_string_append(copy, PCMK__XML_ENTITY_GT);
1251  break;
1252  case '&':
1253  g_string_append(copy, PCMK__XML_ENTITY_AMP);
1254  break;
1255  case '"':
1256  g_string_append(copy, PCMK__XML_ENTITY_QUOT);
1257  break;
1258  default:
1259  if (g_ascii_iscntrl(*text)) {
1260  g_string_append_printf(copy, "&#x%.2X;", *text);
1261  } else {
1262  g_string_append_c(copy, *text);
1263  }
1264  break;
1265  }
1266  break;
1267 
1269  switch (*text) {
1270  case '"':
1271  g_string_append(copy, "\\\"");
1272  break;
1273  case '\n':
1274  g_string_append(copy, "\\n");
1275  break;
1276  case '\r':
1277  g_string_append(copy, "\\r");
1278  break;
1279  case '\t':
1280  g_string_append(copy, "\\t");
1281  break;
1282  default:
1283  g_string_append_c(copy, *text);
1284  break;
1285  }
1286  break;
1287 
1288  default: // Invalid enum value
1289  pcmk__assert(false);
1290  break;
1291  }
1292 
1293  text = g_utf8_next_char(text);
1294  }
1295  return g_string_free(copy, FALSE);
1296 }
1297 
1305 static void
1306 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
1307 {
1308  for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
1309  pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
1310  }
1311 }
1312 
1327 static void
1328 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
1329  const char *old_value)
1330 {
1331  xml_doc_private_t *docpriv = new_xml->doc->_private;
1332  xmlAttr *attr = NULL;
1333  xml_node_private_t *nodepriv;
1334 
1335  /* Restore the old value (without setting dirty flag recursively upwards or
1336  * checking ACLs)
1337  */
1339  crm_xml_add(new_xml, attr_name, old_value);
1341 
1342  // Reset flags (so the attribute doesn't appear as newly created)
1343  attr = xmlHasProp(new_xml, (pcmkXmlStr) attr_name);
1344  nodepriv = attr->_private;
1345  nodepriv->flags = 0;
1346 
1347  // Check ACLs and mark restored value for later removal
1348  pcmk__xa_remove(attr, false);
1349 
1350  crm_trace("XML attribute %s=%s was removed from %s",
1351  attr_name, old_value, element);
1352 }
1353 
1354 /*
1355  * \internal
1356  * \brief Check ACLs for a changed XML attribute
1357  */
1358 static void
1359 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
1360  const char *old_value)
1361 {
1362  xml_doc_private_t *docpriv = new_xml->doc->_private;
1363  char *vcopy = crm_element_value_copy(new_xml, attr_name);
1364 
1365  crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
1366  attr_name, old_value, vcopy, element);
1367 
1368  // Restore the original value (without checking ACLs)
1370  crm_xml_add(new_xml, attr_name, old_value);
1372 
1373  // Change it back to the new value, to check ACLs
1374  crm_xml_add(new_xml, attr_name, vcopy);
1375  free(vcopy);
1376 }
1377 
1389 static void
1390 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
1391  xmlAttr *new_attr, int p_old, int p_new)
1392 {
1393  xml_node_private_t *nodepriv = new_attr->_private;
1394 
1395  crm_trace("XML attribute %s moved from position %d to %d in %s",
1396  old_attr->name, p_old, p_new, element);
1397 
1398  // Mark document, element, and all element's parents as changed
1399  pcmk__mark_xml_node_dirty(new_xml);
1400 
1401  // Mark attribute as changed
1403 
1404  nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1406 }
1407 
1415 static void
1416 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1417 {
1418  xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1419 
1420  while (attr_iter != NULL) {
1421  const char *name = (const char *) attr_iter->name;
1422  xmlAttr *old_attr = attr_iter;
1423  xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1424  const char *old_value = pcmk__xml_attr_value(attr_iter);
1425 
1426  attr_iter = attr_iter->next;
1427  if (new_attr == NULL) {
1428  mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
1429  old_value);
1430 
1431  } else {
1432  xml_node_private_t *nodepriv = new_attr->_private;
1433  int new_pos = pcmk__xml_position((xmlNode*) new_attr,
1434  pcmk__xf_skip);
1435  int old_pos = pcmk__xml_position((xmlNode*) old_attr,
1436  pcmk__xf_skip);
1437  const char *new_value = crm_element_value(new_xml, name);
1438 
1439  // This attribute isn't new
1441 
1442  if (strcmp(new_value, old_value) != 0) {
1443  mark_attr_changed(new_xml, (const char *) old_xml->name, name,
1444  old_value);
1445 
1446  } else if ((old_pos != new_pos)
1447  && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
1448  mark_attr_moved(new_xml, (const char *) old_xml->name,
1449  old_attr, new_attr, old_pos, new_pos);
1450  }
1451  }
1452  }
1453 }
1454 
1464 static void
1465 mark_created_attrs(xmlNode *new_xml)
1466 {
1467  xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1468 
1469  while (attr_iter != NULL) {
1470  xmlAttr *new_attr = attr_iter;
1471  xml_node_private_t *nodepriv = attr_iter->_private;
1472 
1473  attr_iter = attr_iter->next;
1474  if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1475  const char *attr_name = (const char *) new_attr->name;
1476 
1477  crm_trace("Created new attribute %s=%s in %s",
1478  attr_name, pcmk__xml_attr_value(new_attr),
1479  new_xml->name);
1480 
1481  /* Check ACLs (we can't use the remove-then-create trick because it
1482  * would modify the attribute position).
1483  */
1484  if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
1485  pcmk__mark_xml_attr_dirty(new_attr);
1486  } else {
1487  // Creation was not allowed, so remove the attribute
1488  pcmk__xa_remove(new_attr, true);
1489  }
1490  }
1491  }
1492 }
1493 
1501 static void
1502 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1503 {
1504  set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
1505  xml_diff_old_attrs(old_xml, new_xml);
1506  mark_created_attrs(new_xml);
1507 }
1508 
1521 static void
1522 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1523 {
1524  // Re-create the child element so we can check ACLs
1525  xmlNode *candidate = pcmk__xml_copy(new_parent, old_child);
1526 
1527  // Clear flags on new child and its children
1528  pcmk__xml_tree_foreach(candidate, reset_xml_node_flags, NULL);
1529 
1530  // Check whether ACLs allow the deletion
1531  pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
1532 
1533  // Remove the child again (which will track it in document's deleted_objs)
1534  free_xml_with_position(candidate,
1535  pcmk__xml_position(old_child, pcmk__xf_skip));
1536 
1537  if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
1538  pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
1539  pcmk__xf_skip);
1540  }
1541 }
1542 
1543 static void
1544 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
1545  int p_old, int p_new)
1546 {
1547  xml_node_private_t *nodepriv = new_child->_private;
1548 
1549  crm_trace("Child element %s with "
1550  PCMK_XA_ID "='%s' moved from position %d to %d under %s",
1551  new_child->name, pcmk__s(pcmk__xe_id(new_child), "<no id>"),
1552  p_old, p_new, new_parent->name);
1553  pcmk__mark_xml_node_dirty(new_parent);
1555 
1556  if (p_old > p_new) {
1557  nodepriv = old_child->_private;
1558  } else {
1559  nodepriv = new_child->_private;
1560  }
1562 }
1563 
1564 // Given original and new XML, mark new XML portions that have changed
1565 static void
1566 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
1567 {
1568  xmlNode *old_child = NULL;
1569  xmlNode *new_child = NULL;
1570  xml_node_private_t *nodepriv = NULL;
1571 
1572  CRM_CHECK(new_xml != NULL, return);
1573  if (old_xml == NULL) {
1574  mark_xml_tree_dirty_created(new_xml);
1575  pcmk__apply_creation_acl(new_xml, check_top);
1576  return;
1577  }
1578 
1579  nodepriv = new_xml->_private;
1580  CRM_CHECK(nodepriv != NULL, return);
1581 
1582  if(nodepriv->flags & pcmk__xf_processed) {
1583  /* Avoid re-comparing nodes */
1584  return;
1585  }
1587 
1588  xml_diff_attrs(old_xml, new_xml);
1589 
1590  // Check for differences in the original children
1591  for (old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
1592  old_child = pcmk__xml_next(old_child)) {
1593 
1594  new_child = pcmk__xml_match(new_xml, old_child, true);
1595 
1596  if (new_child != NULL) {
1597  mark_xml_changes(old_child, new_child, true);
1598 
1599  } else {
1600  mark_child_deleted(old_child, new_xml);
1601  }
1602  }
1603 
1604  // Check for moved or created children
1605  new_child = pcmk__xml_first_child(new_xml);
1606  while (new_child != NULL) {
1607  xmlNode *next = pcmk__xml_next(new_child);
1608 
1609  old_child = pcmk__xml_match(old_xml, new_child, true);
1610 
1611  if (old_child == NULL) {
1612  // This is a newly created child
1613  nodepriv = new_child->_private;
1615 
1616  // May free new_child
1617  mark_xml_changes(old_child, new_child, true);
1618 
1619  } else {
1620  /* Check for movement, we already checked for differences */
1621  int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
1622  int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
1623 
1624  if(p_old != p_new) {
1625  mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
1626  }
1627  }
1628 
1629  new_child = next;
1630  }
1631 }
1632 
1633 void
1634 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
1635 {
1637  xml_calculate_changes(old_xml, new_xml);
1638 }
1639 
1640 // Called functions may set the \p pcmk__xf_skip flag on parts of \p old_xml
1641 void
1642 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
1643 {
1644  CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
1645  && pcmk__xe_is(old_xml, (const char *) new_xml->name)
1646  && pcmk__str_eq(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
1647  pcmk__str_none),
1648  return);
1649 
1650  if(xml_tracking_changes(new_xml) == FALSE) {
1651  xml_track_changes(new_xml, NULL, NULL, FALSE);
1652  }
1653 
1654  mark_xml_changes(old_xml, new_xml, FALSE);
1655 }
1656 
1691 void
1692 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
1693  uint32_t flags, bool as_diff)
1694 {
1695  /* @COMPAT Refactor further and staticize after v1 patchset deprecation.
1696  *
1697  * @COMPAT Drop as_diff argument when apply_xml_diff() is dropped.
1698  */
1699  const char *update_name = NULL;
1700  const char *update_id_attr = NULL;
1701  const char *update_id_val = NULL;
1702  char *trace_s = NULL;
1703 
1704  crm_log_xml_trace(update, "update");
1705  crm_log_xml_trace(target, "target");
1706 
1707  CRM_CHECK(update != NULL, goto done);
1708 
1709  if (update->type == XML_COMMENT_NODE) {
1710  pcmk__xc_update(parent, target, update);
1711  goto done;
1712  }
1713 
1714  update_name = (const char *) update->name;
1715 
1716  CRM_CHECK(update_name != NULL, goto done);
1717  CRM_CHECK((target != NULL) || (parent != NULL), goto done);
1718 
1719  update_id_val = pcmk__xe_id(update);
1720  if (update_id_val != NULL) {
1721  update_id_attr = PCMK_XA_ID;
1722 
1723  } else {
1724  update_id_val = crm_element_value(update, PCMK_XA_ID_REF);
1725  if (update_id_val != NULL) {
1726  update_id_attr = PCMK_XA_ID_REF;
1727  }
1728  }
1729 
1731  {
1732  if (update_id_attr != NULL) {
1733  trace_s = crm_strdup_printf("<%s %s=%s/>",
1734  update_name, update_id_attr,
1735  update_id_val);
1736  } else {
1737  trace_s = crm_strdup_printf("<%s/>", update_name);
1738  }
1739  },
1740  {}
1741  );
1742 
1743  if (target == NULL) {
1744  // Recursive call
1745  target = pcmk__xe_first_child(parent, update_name, update_id_attr,
1746  update_id_val);
1747  }
1748 
1749  if (target == NULL) {
1750  // Recursive call with no existing matching child
1751  target = pcmk__xe_create(parent, update_name);
1752  crm_trace("Added %s", pcmk__s(trace_s, update_name));
1753 
1754  } else {
1755  // Either recursive call with match, or top-level call
1756  crm_trace("Found node %s to update", pcmk__s(trace_s, update_name));
1757  }
1758 
1759  CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return);
1760 
1761  if (!as_diff) {
1762  pcmk__xe_copy_attrs(target, update, flags);
1763 
1764  } else {
1765  // Preserve order of attributes. Don't use pcmk__xe_copy_attrs().
1766  for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
1767  a = a->next) {
1768  const char *p_value = pcmk__xml_attr_value(a);
1769  xmlAttr *old_attr = xmlHasProp(target, a->name);
1770 
1771  /* Remove it first so the ordering of the update is preserved */
1772  if (old_attr != NULL) {
1773  pcmk__xa_remove(old_attr, true);
1774  }
1775  pcmk__xe_set_attr_force(target, (const char *) a->name, p_value);
1776  }
1777  }
1778 
1779  for (xmlNode *child = pcmk__xml_first_child(update); child != NULL;
1780  child = pcmk__xml_next(child)) {
1781 
1782  crm_trace("Updating child of %s", pcmk__s(trace_s, update_name));
1783  pcmk__xml_update(target, NULL, child, flags, as_diff);
1784  }
1785 
1786  crm_trace("Finished with %s", pcmk__s(trace_s, update_name));
1787 
1788 done:
1789  free(trace_s);
1790 }
1791 
1810 static bool
1811 delete_xe_if_matching(xmlNode *xml, void *user_data)
1812 {
1813  xmlNode *search = user_data;
1814 
1815  if (!pcmk__xe_is(search, (const char *) xml->name)) {
1816  // No match: either not both elements, or different element types
1817  return true;
1818  }
1819 
1820  for (const xmlAttr *attr = pcmk__xe_first_attr(search); attr != NULL;
1821  attr = attr->next) {
1822 
1823  const char *search_val = pcmk__xml_attr_value(attr);
1824  const char *xml_val = crm_element_value(xml, (const char *) attr->name);
1825 
1826  if (!pcmk__str_eq(search_val, xml_val, pcmk__str_casei)) {
1827  // No match: an attr in xml doesn't match the attr in search
1828  return true;
1829  }
1830  }
1831 
1832  crm_log_xml_trace(xml, "delete-match");
1833  crm_log_xml_trace(search, "delete-search");
1834  free_xml(xml);
1835 
1836  // Found a match and deleted it; stop traversing tree
1837  return false;
1838 }
1839 
1858 int
1859 pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
1860 {
1861  // See @COMPAT comment in pcmk__xe_replace_match()
1862  CRM_CHECK((xml != NULL) && (search != NULL), return EINVAL);
1863 
1864  for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
1865  xml = pcmk__xe_next(xml)) {
1866 
1867  if (!pcmk__xml_tree_foreach(xml, delete_xe_if_matching, search)) {
1868  // Found and deleted an element
1869  return pcmk_rc_ok;
1870  }
1871  }
1872 
1873  // No match found in this subtree
1874  return ENXIO;
1875 }
1876 
1888 static void
1889 replace_node(xmlNode *old, xmlNode *new)
1890 {
1891  // Pass old for its doc; it won't remain the parent of new
1892  new = pcmk__xml_copy(old, new);
1893  old = xmlReplaceNode(old, new);
1894 
1895  // old == NULL means memory allocation error
1896  pcmk__assert(old != NULL);
1897 
1898  // May be unnecessary but avoids slight changes to some test outputs
1899  pcmk__xml_tree_foreach(new, reset_xml_node_flags, NULL);
1900 
1901  if (xml_tracking_changes(new)) {
1902  // Replaced sections may have included relevant ACLs
1903  pcmk__apply_acl(new);
1904  }
1905  xml_calculate_changes(old, new);
1906  pcmk_free_xml_subtree(old);
1907 }
1908 
1926 static bool
1927 replace_xe_if_matching(xmlNode *xml, void *user_data)
1928 {
1929  xmlNode *replace = user_data;
1930  const char *xml_id = NULL;
1931  const char *replace_id = NULL;
1932 
1933  xml_id = pcmk__xe_id(xml);
1934  replace_id = pcmk__xe_id(replace);
1935 
1936  if (!pcmk__xe_is(replace, (const char *) xml->name)) {
1937  // No match: either not both elements, or different element types
1938  return true;
1939  }
1940 
1941  if ((replace_id != NULL)
1942  && !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) {
1943 
1944  // No match: ID was provided in replace and doesn't match xml's ID
1945  return true;
1946  }
1947 
1948  crm_log_xml_trace(xml, "replace-match");
1949  crm_log_xml_trace(replace, "replace-with");
1950  replace_node(xml, replace);
1951 
1952  // Found a match and replaced it; stop traversing tree
1953  return false;
1954 }
1955 
1973 int
1974 pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
1975 {
1976  /* @COMPAT Some of this behavior (like not matching the tree root, which is
1977  * allowed by pcmk__xe_update_match()) is questionable for general use but
1978  * required for backward compatibility by cib_process_replace() and
1979  * cib_process_delete(). Behavior can change at a major version release if
1980  * desired.
1981  */
1982  CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL);
1983 
1984  for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
1985  xml = pcmk__xe_next(xml)) {
1986 
1987  if (!pcmk__xml_tree_foreach(xml, replace_xe_if_matching, replace)) {
1988  // Found and replaced an element
1989  return pcmk_rc_ok;
1990  }
1991  }
1992 
1993  // No match found in this subtree
1994  return ENXIO;
1995 }
1996 
1998 struct update_data {
1999  xmlNode *update;
2000  uint32_t flags;
2001 };
2002 
2024 static bool
2025 update_xe_if_matching(xmlNode *xml, void *user_data)
2026 {
2027  struct update_data *data = user_data;
2028  xmlNode *update = data->update;
2029 
2030  if (!pcmk__xe_is(update, (const char *) xml->name)) {
2031  // No match: either not both elements, or different element types
2032  return true;
2033  }
2034 
2035  if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) {
2036  // No match: ID mismatch
2037  return true;
2038  }
2039 
2040  crm_log_xml_trace(xml, "update-match");
2041  crm_log_xml_trace(update, "update-with");
2042  pcmk__xml_update(NULL, xml, update, data->flags, false);
2043 
2044  // Found a match and replaced it; stop traversing tree
2045  return false;
2046 }
2047 
2067 int
2068 pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
2069 {
2070  /* @COMPAT In pcmk__xe_delete_match() and pcmk__xe_replace_match(), we
2071  * compare IDs only if the equivalent of the update argument has an ID.
2072  * Here, we're stricter: we consider it a mismatch if only one element has
2073  * an ID attribute, or if both elements have IDs but they don't match.
2074  *
2075  * Perhaps we should align the behavior at a major version release.
2076  */
2077  struct update_data data = {
2078  .update = update,
2079  .flags = flags,
2080  };
2081 
2082  CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL);
2083 
2084  if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, &data)) {
2085  // Found and updated an element
2086  return pcmk_rc_ok;
2087  }
2088 
2089  // No match found in this subtree
2090  return ENXIO;
2091 }
2092 
2093 xmlNode *
2094 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2095 {
2096  xmlNode *child = NULL;
2097  GSList *nvpairs = NULL;
2098  xmlNode *result = NULL;
2099 
2100  CRM_CHECK(input != NULL, return NULL);
2101 
2102  result = pcmk__xe_create(parent, (const char *) input->name);
2103  nvpairs = pcmk_xml_attrs2nvpairs(input);
2104  nvpairs = pcmk_sort_nvpairs(nvpairs);
2105  pcmk_nvpairs2xml_attrs(nvpairs, result);
2106  pcmk_free_nvpairs(nvpairs);
2107 
2108  for (child = pcmk__xe_first_child(input, NULL, NULL, NULL); child != NULL;
2109  child = pcmk__xe_next(child)) {
2110 
2111  if (recursive) {
2112  sorted_xml(child, result, recursive);
2113  } else {
2114  pcmk__xml_copy(result, child);
2115  }
2116  }
2117 
2118  return result;
2119 }
2120 
2129 xmlNode *
2130 pcmk__xe_next_same(const xmlNode *node)
2131 {
2132  for (xmlNode *match = pcmk__xe_next(node); match != NULL;
2133  match = pcmk__xe_next(match)) {
2134 
2135  if (pcmk__xe_is(match, (const char *) node->name)) {
2136  return match;
2137  }
2138  }
2139  return NULL;
2140 }
2141 
2142 void
2144 {
2145  static bool init = true;
2146 
2147  if(init) {
2148  init = false;
2149  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
2150  * pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
2151  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
2152  * less than 1 second.
2153  */
2154  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2155 
2156  crm_schema_init();
2157  }
2158 }
2159 
2160 void
2162 {
2164  xmlCleanupParser();
2165 }
2166 
2167 #define XPATH_MAX 512
2168 
2169 xmlNode *
2170 expand_idref(xmlNode * input, xmlNode * top)
2171 {
2172  char *xpath = NULL;
2173  const char *ref = NULL;
2174  xmlNode *result = NULL;
2175 
2176  if (input == NULL) {
2177  return NULL;
2178  }
2179 
2181  if (ref == NULL) {
2182  return input;
2183  }
2184 
2185  if (top == NULL) {
2186  top = input;
2187  }
2188 
2189  xpath = crm_strdup_printf("//%s[@" PCMK_XA_ID "='%s']", input->name, ref);
2190  result = get_xpath_object(xpath, top, LOG_DEBUG);
2191  if (result == NULL) { // Not possible with schema validation enabled
2192  pcmk__config_err("Ignoring invalid %s configuration: "
2193  PCMK_XA_ID_REF " '%s' does not reference "
2194  "a valid object " CRM_XS " xpath=%s",
2195  input->name, ref, xpath);
2196  }
2197  free(xpath);
2198  return result;
2199 }
2200 
2201 char *
2203 {
2204  static const char *base = NULL;
2205  char *ret = NULL;
2206 
2207  if (base == NULL) {
2209  }
2210  if (pcmk__str_empty(base)) {
2211  base = CRM_SCHEMA_DIRECTORY;
2212  }
2213 
2214  switch (ns) {
2217  ret = strdup(base);
2218  break;
2221  ret = crm_strdup_printf("%s/base", base);
2222  break;
2223  default:
2224  crm_err("XML artefact family specified as %u not recognized", ns);
2225  }
2226  return ret;
2227 }
2228 
2229 static char *
2230 find_artefact(enum pcmk__xml_artefact_ns ns, const char *path, const char *filespec)
2231 {
2232  char *ret = NULL;
2233 
2234  switch (ns) {
2237  if (pcmk__ends_with(filespec, ".rng")) {
2238  ret = crm_strdup_printf("%s/%s", path, filespec);
2239  } else {
2240  ret = crm_strdup_printf("%s/%s.rng", path, filespec);
2241  }
2242  break;
2245  if (pcmk__ends_with(filespec, ".xsl")) {
2246  ret = crm_strdup_printf("%s/%s", path, filespec);
2247  } else {
2248  ret = crm_strdup_printf("%s/%s.xsl", path, filespec);
2249  }
2250  break;
2251  default:
2252  crm_err("XML artefact family specified as %u not recognized", ns);
2253  }
2254 
2255  return ret;
2256 }
2257 
2258 char *
2259 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2260 {
2261  struct stat sb;
2262  char *base = pcmk__xml_artefact_root(ns);
2263  char *ret = NULL;
2264 
2265  ret = find_artefact(ns, base, filespec);
2266  free(base);
2267 
2268  if (stat(ret, &sb) != 0 || !S_ISREG(sb.st_mode)) {
2269  const char *remote_schema_dir = pcmk__remote_schema_dir();
2270 
2271  free(ret);
2272  ret = find_artefact(ns, remote_schema_dir, filespec);
2273  }
2274 
2275  return ret;
2276 }
2277 
2278 void
2279 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2280 {
2281  while (true) {
2282  const char *name, *value;
2283 
2284  name = va_arg(pairs, const char *);
2285  if (name == NULL) {
2286  return;
2287  }
2288 
2289  value = va_arg(pairs, const char *);
2290  if (value != NULL) {
2291  crm_xml_add(node, name, value);
2292  }
2293  }
2294 }
2295 
2296 void
2297 pcmk__xe_set_props(xmlNodePtr node, ...)
2298 {
2299  va_list pairs;
2300  va_start(pairs, node);
2301  pcmk__xe_set_propv(node, pairs);
2302  va_end(pairs);
2303 }
2304 
2305 int
2306 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
2307  int (*handler)(xmlNode *xml, void *userdata),
2308  void *userdata)
2309 {
2310  xmlNode *children = (xml? xml->children : NULL);
2311 
2312  pcmk__assert(handler != NULL);
2313 
2314  for (xmlNode *node = children; node != NULL; node = node->next) {
2315  if ((node->type == XML_ELEMENT_NODE)
2316  && ((child_element_name == NULL)
2317  || pcmk__xe_is(node, child_element_name))) {
2318  int rc = handler(node, userdata);
2319 
2320  if (rc != pcmk_rc_ok) {
2321  return rc;
2322  }
2323  }
2324  }
2325 
2326  return pcmk_rc_ok;
2327 }
2328 
2329 // Deprecated functions kept only for backward API compatibility
2330 // LCOV_EXCL_START
2331 
2332 #include <crm/common/xml_compat.h>
2333 
2334 xmlNode *
2335 find_entity(xmlNode *parent, const char *node_name, const char *id)
2336 {
2337  return pcmk__xe_first_child(parent, node_name,
2338  ((id == NULL)? id : PCMK_XA_ID), id);
2339 }
2340 
2341 void
2343 {
2344  free_xml(data);
2345 }
2346 
2347 xmlDoc *
2348 getDocPtr(xmlNode *node)
2349 {
2350  xmlDoc *doc = NULL;
2351 
2352  CRM_CHECK(node != NULL, return NULL);
2353 
2354  doc = node->doc;
2355  if (doc == NULL) {
2356  doc = pcmk__xml_new_doc();
2357  xmlDocSetRootElement(doc, node);
2358  }
2359  return doc;
2360 }
2361 
2362 xmlNode *
2363 add_node_copy(xmlNode *parent, xmlNode *src_node)
2364 {
2365  xmlNode *child = NULL;
2366 
2367  CRM_CHECK((parent != NULL) && (src_node != NULL), return NULL);
2368 
2369  child = xmlDocCopyNode(src_node, parent->doc, 1);
2370  if (child == NULL) {
2371  return NULL;
2372  }
2373  xmlAddChild(parent, child);
2375  return child;
2376 }
2377 
2378 int
2379 add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
2380 {
2381  add_node_copy(parent, child);
2382  free_xml(child);
2383  return 1;
2384 }
2385 
2386 gboolean
2387 xml_has_children(const xmlNode * xml_root)
2388 {
2389  if (xml_root != NULL && xml_root->children != NULL) {
2390  return TRUE;
2391  }
2392  return FALSE;
2393 }
2394 
2395 static char *
2396 replace_text(char *text, size_t *index, size_t *length, const char *replace)
2397 {
2398  // We have space for 1 char already
2399  size_t offset = strlen(replace) - 1;
2400 
2401  if (offset > 0) {
2402  *length += offset;
2403  text = pcmk__realloc(text, *length + 1);
2404 
2405  // Shift characters to the right to make room for the replacement string
2406  for (size_t i = *length; i > (*index + offset); i--) {
2407  text[i] = text[i - offset];
2408  }
2409  }
2410 
2411  // Replace the character at index by the replacement string
2412  memcpy(text + *index, replace, offset + 1);
2413 
2414  // Reset index to the end of replacement string
2415  *index += offset;
2416  return text;
2417 }
2418 
2419 char *
2420 crm_xml_escape(const char *text)
2421 {
2422  size_t length = 0;
2423  char *copy = NULL;
2424 
2425  if (text == NULL) {
2426  return NULL;
2427  }
2428 
2429  length = strlen(text);
2430  copy = pcmk__str_copy(text);
2431  for (size_t index = 0; index <= length; index++) {
2432  if(copy[index] & 0x80 && copy[index+1] & 0x80){
2433  index++;
2434  continue;
2435  }
2436  switch (copy[index]) {
2437  case 0:
2438  // Sanity only; loop should stop at the last non-null byte
2439  break;
2440  case '<':
2441  copy = replace_text(copy, &index, &length, "&lt;");
2442  break;
2443  case '>':
2444  copy = replace_text(copy, &index, &length, "&gt;");
2445  break;
2446  case '"':
2447  copy = replace_text(copy, &index, &length, "&quot;");
2448  break;
2449  case '\'':
2450  copy = replace_text(copy, &index, &length, "&apos;");
2451  break;
2452  case '&':
2453  copy = replace_text(copy, &index, &length, "&amp;");
2454  break;
2455  case '\t':
2456  /* Might as well just expand to a few spaces... */
2457  copy = replace_text(copy, &index, &length, " ");
2458  break;
2459  case '\n':
2460  copy = replace_text(copy, &index, &length, "\\n");
2461  break;
2462  case '\r':
2463  copy = replace_text(copy, &index, &length, "\\r");
2464  break;
2465  default:
2466  /* Check for and replace non-printing characters with their octal equivalent */
2467  if(copy[index] < ' ' || copy[index] > '~') {
2468  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
2469 
2470  copy = replace_text(copy, &index, &length, replace);
2471  free(replace);
2472  }
2473  }
2474  }
2475  return copy;
2476 }
2477 
2478 xmlNode *
2479 copy_xml(xmlNode *src)
2480 {
2481  xmlDoc *doc = pcmk__xml_new_doc();
2482  xmlNode *copy = NULL;
2483 
2484  copy = xmlDocCopyNode(src, doc, 1);
2485  pcmk__mem_assert(copy);
2486 
2487  xmlDocSetRootElement(doc, copy);
2489  return copy;
2490 }
2491 
2492 xmlNode *
2493 create_xml_node(xmlNode *parent, const char *name)
2494 {
2495  // Like pcmk__xe_create(), but returns NULL on failure
2496  xmlNode *node = NULL;
2497 
2498  CRM_CHECK(!pcmk__str_empty(name), return NULL);
2499 
2500  if (parent == NULL) {
2501  xmlDoc *doc = pcmk__xml_new_doc();
2502 
2503  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
2504  if (node == NULL) {
2505  pcmk__xml_free_doc(doc);
2506  return NULL;
2507  }
2508  xmlDocSetRootElement(doc, node);
2509 
2510  } else {
2511  node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
2512  if (node == NULL) {
2513  return NULL;
2514  }
2515  }
2517  return node;
2518 }
2519 
2520 xmlNode *
2521 pcmk_create_xml_text_node(xmlNode *parent, const char *name,
2522  const char *content)
2523 {
2524  xmlNode *node = pcmk__xe_create(parent, name);
2525 
2526  pcmk__xe_set_content(node, "%s", content);
2527  return node;
2528 }
2529 
2530 xmlNode *
2531 pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id,
2532  const char *class_name, const char *text)
2533 {
2534  xmlNode *node = pcmk__html_create(parent, element_name, id, class_name);
2535 
2536  pcmk__xe_set_content(node, "%s", text);
2537  return node;
2538 }
2539 
2540 xmlNode *
2541 first_named_child(const xmlNode *parent, const char *name)
2542 {
2543  return pcmk__xe_first_child(parent, name, NULL, NULL);
2544 }
2545 
2546 xmlNode *
2547 find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
2548 {
2549  xmlNode *result = NULL;
2550 
2551  if (search_path == NULL) {
2552  crm_warn("Will never find <NULL>");
2553  return NULL;
2554  }
2555 
2556  result = pcmk__xe_first_child(root, search_path, NULL, NULL);
2557 
2558  if (must_find && (result == NULL)) {
2559  crm_warn("Could not find %s in %s",
2560  search_path,
2561  ((root != NULL)? (const char *) root->name : "<NULL>"));
2562  }
2563 
2564  return result;
2565 }
2566 
2567 xmlNode *
2568 crm_next_same_xml(const xmlNode *sibling)
2569 {
2570  return pcmk__xe_next_same(sibling);
2571 }
2572 
2573 void
2574 xml_remove_prop(xmlNode * obj, const char *name)
2575 {
2576  pcmk__xe_remove_attr(obj, name);
2577 }
2578 
2579 gboolean
2580 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2581 {
2582  bool is_match = false;
2583  const char *child_id = NULL;
2584  const char *update_id = NULL;
2585 
2586  CRM_CHECK(child != NULL, return FALSE);
2587  CRM_CHECK(update != NULL, return FALSE);
2588 
2589  child_id = pcmk__xe_id(child);
2590  update_id = pcmk__xe_id(update);
2591 
2592  /* Match element name and (if provided in update XML) element ID. Don't
2593  * match search root (child is search root if parent == NULL).
2594  */
2595  is_match = (parent != NULL)
2596  && pcmk__xe_is(update, (const char *) child->name)
2597  && ((update_id == NULL)
2598  || pcmk__str_eq(update_id, child_id, pcmk__str_none));
2599 
2600  /* For deletion, match all attributes provided in update. A matching node
2601  * can have additional attributes, but values must match for provided ones.
2602  */
2603  if (is_match && delete_only) {
2604  for (xmlAttr *attr = pcmk__xe_first_attr(update); attr != NULL;
2605  attr = attr->next) {
2606  const char *name = (const char *) attr->name;
2607  const char *update_val = pcmk__xml_attr_value(attr);
2608  const char *child_val = crm_element_value(child, name);
2609 
2610  if (!pcmk__str_eq(update_val, child_val, pcmk__str_casei)) {
2611  is_match = false;
2612  break;
2613  }
2614  }
2615  }
2616 
2617  if (is_match) {
2618  if (delete_only) {
2619  crm_log_xml_trace(child, "delete-match");
2620  crm_log_xml_trace(update, "delete-search");
2621  free_xml(child);
2622 
2623  } else {
2624  crm_log_xml_trace(child, "replace-match");
2625  crm_log_xml_trace(update, "replace-with");
2626  replace_node(child, update);
2627  }
2628  return TRUE;
2629  }
2630 
2631  // Current node not a match; search the rest of the subtree depth-first
2632  parent = child;
2633  for (child = pcmk__xml_first_child(parent); child != NULL;
2634  child = pcmk__xml_next(child)) {
2635 
2636  // Only delete/replace the first match
2637  if (replace_xml_child(parent, child, update, delete_only)) {
2638  return TRUE;
2639  }
2640  }
2641 
2642  // No match found in this subtree
2643  return FALSE;
2644 }
2645 
2646 gboolean
2647 update_xml_child(xmlNode *child, xmlNode *to_update)
2648 {
2649  return pcmk__xe_update_match(child, to_update,
2651 }
2652 
2653 int
2654 find_xml_children(xmlNode **children, xmlNode *root, const char *tag,
2655  const char *field, const char *value, gboolean search_matches)
2656 {
2657  int match_found = 0;
2658 
2659  CRM_CHECK(root != NULL, return FALSE);
2660  CRM_CHECK(children != NULL, return FALSE);
2661 
2662  if ((tag != NULL) && !pcmk__xe_is(root, tag)) {
2663 
2664  } else if ((value != NULL)
2665  && !pcmk__str_eq(value, crm_element_value(root, field),
2666  pcmk__str_casei)) {
2667 
2668  } else {
2669  if (*children == NULL) {
2670  *children = pcmk__xe_create(NULL, __func__);
2671  }
2672  pcmk__xml_copy(*children, root);
2673  match_found = 1;
2674  }
2675 
2676  if (search_matches || match_found == 0) {
2677  xmlNode *child = NULL;
2678 
2679  for (child = pcmk__xml_first_child(root); child != NULL;
2680  child = pcmk__xml_next(child)) {
2681  match_found += find_xml_children(children, child, tag, field, value,
2682  search_matches);
2683  }
2684  }
2685 
2686  return match_found;
2687 }
2688 
2689 void
2691 {
2692  /* TODO: Remove recursion and use xpath searches for value++ */
2693  xmlNode *child = NULL;
2694 
2695  for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
2696  const char *p_name = (const char *) a->name;
2697  const char *p_value = pcmk__xml_attr_value(a);
2698 
2699  expand_plus_plus(target, p_name, p_value);
2700  }
2701  for (child = pcmk__xe_first_child(target, NULL, NULL, NULL); child != NULL;
2702  child = pcmk__xe_next(child)) {
2703 
2704  fix_plus_plus_recursive(child);
2705  }
2706 }
2707 
2708 void
2709 copy_in_properties(xmlNode *target, const xmlNode *src)
2710 {
2711  if (src == NULL) {
2712  crm_warn("No node to copy properties from");
2713 
2714  } else if (target == NULL) {
2715  crm_err("No node to copy properties into");
2716 
2717  } else {
2718  for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
2719  const char *p_name = (const char *) a->name;
2720  const char *p_value = pcmk__xml_attr_value(a);
2721 
2722  expand_plus_plus(target, p_name, p_value);
2723  if (xml_acl_denied(target)) {
2724  crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name);
2725  return;
2726  }
2727  }
2728  }
2729 }
2730 
2731 void
2732 expand_plus_plus(xmlNode * target, const char *name, const char *value)
2733 {
2734  pcmk__xe_set_score(target, name, value);
2735 }
2736 
2737 // LCOV_EXCL_STOP
2738 // 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:2306
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:67
#define XML_VERSION
libxml2 supports only XML version 1.0, at least as of libxml2-2.12.5
Definition: xml.c:29
void pcmk__xml_free_private_data(xmlNode *xml)
Definition: xml.c:338
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:1634
A dumping ground.
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition: nvpair.c:144
#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:45
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml.c:702
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:103
void pcmk__free_acls(GList *acls)
Definition: acl.c: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:344
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml_attr.c:75
xmlDoc * pcmk__xml_new_doc(void)
Definition: xml.c:803
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:1642
#define PCMK__XML_DOC_PRIVATE_MAGIC
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:360
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:1065
#define pcmk__config_err(fmt...)
void fix_plus_plus_recursive(xmlNode *target)
Definition: xml.c:2690
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition: xml.c:2143
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2541
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770
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:313
void pcmk__xml_new_private_data(xmlNode *xml)
Definition: xml.c:326
G_GNUC_INTERNAL xmlAttr * pcmk__xe_set_attr_force(xmlNode *node, const char *name, const char *value)
Definition: nvpair.c:290
void pcmk_free_xml_subtree(xmlNode *xml)
Definition: xml.c:885
#define PCMK_XA_CIB_LAST_WRITTEN
Definition: xml_names.h:244
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml.c:2297
int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
Definition: xml.c:1859
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition: acl.c:573
#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:2568
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:974
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:620
void crm_schema_cleanup(void)
Definition: schemas.c:643
const char * pcmk__env_option(const char *option)
Definition: options.c:1094
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
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:87
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, uint32_t flags, bool as_diff)
Definition: xml.c:1692
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:2654
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Definition: xml.c:2732
gchar * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1199
#define PCMK__ENV_SCHEMA_DIRECTORY
int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition: xml.c:383
xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
Definition: xml.c:426
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition: xml.c:2202
#define crm_warn(fmt, args...)
Definition: logging.h:394
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml.c:2279
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:1012
xmlNode * copy_xml(xmlNode *src)
Definition: xml.c:2479
G_GNUC_INTERNAL void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml_comment.c:99
int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
Definition: xml.c:1974
pcmk__xml_escape_type
Definition: xml_internal.h:276
void pcmk__xml_free_doc(xmlDoc *doc)
Definition: xml.c:819
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2170
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:758
#define PCMK_XA_ID_REF
Definition: xml_names.h:303
#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:458
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:2493
void free_xml(xmlNode *child)
Definition: xml.c:958
#define crm_trace(fmt, args...)
Definition: logging.h:404
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
void pcmk__mark_xml_node_dirty(xmlNode *xml)
Definition: xml.c:105
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:94
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Definition: xml.c:94
void pcmk__xml_set_parent_flags(xmlNode *xml, uint64_t flags)
Definition: xml.c:82
Wrappers for and extensions to libxml2.
bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
Definition: xml.c:724
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition: xml.c:1087
#define PCMK_XA_ID
Definition: xml_names.h:301
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition: xml.c:2259
void crm_xml_cleanup(void)
Definition: xml.c:2161
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition: xml.c:2363
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:2348
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:167
#define pcmk__str_copy(str)
xmlNode * pcmk__html_create(xmlNode *parent, const char *name, const char *id, const char *class)
Definition: output_html.c:483
int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
Definition: xml.c:669
const xmlChar * pcmkXmlStr
Definition: xml.h:41
#define PCMK__XML_NODE_PRIVATE_MAGIC
#define pcmk__assert(expr)
xml_private_flags
Definition: xml_internal.h:524
const char * target
Definition: pcmk_fence.c:29
int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
Definition: xml.c:2068
#define CRM_XS
Definition: logging.h:56
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:1117
void copy_in_properties(xmlNode *target, const xmlNode *src)
Definition: xml.c:2709
pcmk__xml_artefact_ns
Definition: xml_internal.h:207
int pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score, int default_score)
Definition: xml.c:549
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
pcmk__action_result_t result
Definition: pcmk_fence.c:35
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:2387
const char * path
Definition: cib.c:28
#define crm_err(fmt, args...)
Definition: logging.h:391
#define CRM_SCHEMA_DIRECTORY
Definition: config.h:45
char * pcmk__epoch2str(const time_t *source, uint32_t flags)
Definition: iso8601.c:2147
const char * pcmk__remote_schema_dir(void)
Definition: schemas.c:1577
xmlNode * input
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:658
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:230
void xml_accept_changes(xmlNode *xml)
Definition: xml.c:443
#define pcmk__mem_assert(ptr)
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition: scores.c:39
void crm_destroy_xml(gpointer data)
Definition: xml.c:2342
#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:199
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:2420
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
Definition: xml.c:2531
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2647
GString * pcmk__element_xpath(const xmlNode *xml)
Definition: xpath.c:256
void pcmk__xe_set_content(xmlNode *node, const char *format,...)
Definition: xml.c:840
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:2521
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2094
#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:2547
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:359
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:742
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:2379
G_GNUC_INTERNAL xmlNode * pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
Definition: xml_comment.c:50
#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:2580
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2574
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1048
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:2335
GSList * pcmk_xml_attrs2nvpairs(const xmlNode *xml)
Create a list of name/value pairs from an XML node&#39;s attributes.
Definition: nvpair.c:159
G_GNUC_INTERNAL int pcmk__xa_remove(xmlAttr *attr, bool force)
Definition: xml_attr.c:45
uint64_t flags
Definition: remote.c:215
Don&#39;t overwrite existing values.
Definition: xml_internal.h:443
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:366
int pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
Definition: xml.c:591