pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
41bool
42pcmk__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
59bool
60pcmk__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,
69 return FALSE;
70 }
71 return TRUE;
72}
73
74static inline void
75set_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
88void
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
100void
102{
104 set_parent_flag(xml, pcmk__xf_dirty);
105}
106
118static bool
119reset_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
140static bool
141mark_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
159void
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
179static void
180free_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
191static void
192reset_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
213static void
214free_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
258static void
259new_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
302void
303xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
304{
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
318bool 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
325bool 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
341int
342pcmk__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
368static bool
369accept_attr_deletions(xmlNode *xml, void *user_data)
370{
371 reset_xml_node_flags(xml, NULL);
373 return true;
374}
375
384xmlNode *
385pcmk__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
401void
402xml_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
439xmlNode *
440pcmk__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
524int
525pcmk__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
583int
584pcmk__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
601 } else {
602 crm_xml_add(target, name, value);
603 }
604 }
605
606 return pcmk_rc_ok;
607}
608
619static int
620remove_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
651void
652pcmk__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
673bool
674pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
675{
676 const char *name = user_data;
677
679 return true;
680}
681
691void
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
719xmlNode *
720pcmk__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
756G_GNUC_PRINTF(2, 3)
757void
758pcmk__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
794void
796{
797 xmlUnlinkNode(xml); // Detaches from parent and siblings
798 xmlFreeNode(xml); // Frees
799}
800
801static void
802free_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,
854 }
855 }
856
857 docpriv->deleted_objs = g_list_append(docpriv->deleted_objs,
858 deleted_obj);
860 }
861 }
863}
864
865
866void
867free_xml(xmlNode * child)
868{
869 free_xml_with_position(child, -1);
870}
871
882xmlNode *
883pcmk__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
922void
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 */
934 break;
935
936 case XML_ELEMENT_NODE:
937 /* Search it */
939 break;
940
941 default:
942 /* Leave it */
943 break;
944 }
945
946 iter = next;
947 }
948}
949
958const 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
975void
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
997void
998crm_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
1011 crm_xml_add(xml, PCMK_XA_ID, id);
1012 free(id);
1013}
1014
1027bool
1029{
1030 if (text == NULL) {
1031 return false;
1032 }
1033
1034 while (*text != '\0') {
1035 switch (type) {
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
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
1109gchar *
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) {
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
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
1216static void
1217set_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
1238static void
1239mark_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 */
1268static void
1269mark_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
1296static void
1297mark_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
1307
1308 // Mark attribute as changed
1310
1311 nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1313}
1314
1322static void
1323xml_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,
1342 int old_pos = pcmk__xml_position((xmlNode*) old_attr,
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
1371static void
1372mark_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
1408static void
1409xml_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
1428static void
1429mark_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),
1447 }
1448}
1449
1450static void
1451mark_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
1472static void
1473mark_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
1540void
1541xml_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
1548void
1549xml_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),
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
1572xmlNode *
1573pcmk__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
1621void
1622pcmk__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
1674void
1675pcmk__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) {
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
1768done:
1769 free(trace_s);
1770}
1771
1790static bool
1791delete_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
1838int
1839pcmk__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
1868static void
1869replace_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
1904static bool
1905replace_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
1951int
1952pcmk__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
1976struct update_data {
1977 xmlNode *update;
1978 uint32_t flags;
1979};
1980
2002static bool
2003update_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
2045int
2046pcmk__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
2071xmlNode *
2072sorted_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);
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
2107xmlNode *
2108pcmk__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
2120void
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
2139 }
2140}
2141
2142void
2144{
2146 xmlCleanupParser();
2147}
2148
2149#define XPATH_MAX 512
2150
2151xmlNode *
2152expand_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
2183char *
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
2211static char *
2212find_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
2240char *
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
2260void
2261pcmk__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
2278void
2279pcmk__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
2287int
2288pcmk__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
2316xmlNode *
2317find_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
2323void
2325{
2326 free_xml(data);
2327}
2328
2329xmlDoc *
2330getDocPtr(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
2344xmlNode *
2345add_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);
2357 return child;
2358}
2359
2360int
2361add_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
2368gboolean
2369xml_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
2377static char *
2378replace_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
2401char *
2402crm_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
2460xmlNode *
2461copy_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
2475xmlNode *
2476create_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 }
2504 return node;
2505}
2506
2507xmlNode *
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
2517xmlNode *
2518pcmk_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
2527xmlNode *
2528first_named_child(const xmlNode *parent, const char *name)
2529{
2530 return pcmk__xe_first_child(parent, name, NULL, NULL);
2531}
2532
2533xmlNode *
2534find_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
2554xmlNode *
2555crm_next_same_xml(const xmlNode *sibling)
2556{
2557 return pcmk__xe_next_same(sibling);
2558}
2559
2560void
2561xml_remove_prop(xmlNode * obj, const char *name)
2562{
2564}
2565
2566gboolean
2567replace_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
2633gboolean
2634update_xml_child(xmlNode *child, xmlNode *to_update)
2635{
2636 return pcmk__xe_update_match(child, to_update,
2638}
2639
2640int
2641find_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
2676void
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
2692 }
2693}
2694
2695void
2696copy_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
2718void
2719expand_plus_plus(xmlNode * target, const char *name, const char *value)
2720{
2722}
2723
2724// LCOV_EXCL_STOP
2725// End deprecated API
void pcmk__free_acls(GList *acls)
Definition acl.c:43
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition acl.c:288
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition acl.c:573
void pcmk__apply_acl(xmlNode *xml)
Definition acl.c:230
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition acl.c:616
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition acl.c:658
const char * parent
Definition cib.c:27
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:297
uint64_t flags
Definition remote.c:3
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
#define CRM_SCHEMA_DIRECTORY
Definition config.h:45
enum crm_ais_msg_types type
Definition cpg.c:3
char data[0]
Definition cpg.c:10
A dumping ground.
G_GNUC_INTERNAL bool pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data)
Definition xml_attr.c:43
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
#define PCMK__XML_ENTITY_QUOT
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition xml_attr.c:31
#define PCMK__XML_ENTITY_GT
#define PCMK__XML_VERSION
libxml2 supports only XML version 1.0, at least as of libxml2-2.12.5
#define PCMK__XML_ENTITY_AMP
#define PCMK__XML_ENTITY_LT
char * pcmk__epoch2str(const time_t *source, uint32_t flags)
Definition iso8601.c:2075
#define crm_warn(fmt, args...)
Definition logging.h:392
#define CRM_XS
Definition logging.h:56
#define CRM_LOG_ASSERT(expr)
Definition logging.h:228
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_err(fmt, args...)
Definition logging.h:389
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define crm_trace(fmt, args...)
Definition logging.h:402
#define LOG_TRACE
Definition logging.h:38
#define pcmk__config_err(fmt...)
#define pcmk__if_tracing(if_action, else_action)
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition nvpair.c:145
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 pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition nvpair.c:200
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition nvpair.c:101
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
GSList * pcmk_xml_attrs2nvpairs(const xmlNode *xml)
Create a list of name/value pairs from an XML node's attributes.
Definition nvpair.c:160
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
#define PCMK__ENV_SCHEMA_DIRECTORY
const char * pcmk__env_option(const char *option)
Definition options.c:1088
xmlNode * pcmk__html_create(xmlNode *parent, const char *name, const char *id, const char *class)
pcmk__action_result_t result
Definition pcmk_fence.c:35
const char * target
Definition pcmk_fence.c:29
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
#define pcmk__mem_assert(ptr)
const char * pcmk__remote_schema_dir(void)
Definition schemas.c:1571
void crm_schema_cleanup(void)
Definition schemas.c:643
void crm_schema_init(void)
Definition schemas.c:470
int char2score(const char *score)
Get the integer value of a score string.
Definition scores.c:36
int pcmk__add_scores(int score1, int score2)
Definition scores.c:116
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:608
#define pcmk__str_copy(str)
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition xml.c:2634
void pcmk__xml_mark_created(xmlNode *xml)
Definition xml.c:160
gboolean xml_has_children(const xmlNode *xml_root)
Definition xml.c:2369
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition xml.c:652
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition xml.c:60
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
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition xml.c:2361
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:883
int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
Definition xml.c:2046
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Definition xml.c:89
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition xml.c:2345
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2152
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 * pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
Definition xml.c:1573
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition xml.c:2528
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition xml.c:2508
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition xml.c:2108
void copy_in_properties(xmlNode *target, const xmlNode *src)
Definition xml.c:2696
void fix_plus_plus_recursive(xmlNode *target)
Definition xml.c:2677
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition xml.c:692
void xml_accept_changes(xmlNode *xml)
Definition xml.c:402
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, uint32_t flags, bool as_diff)
Definition xml.c:1675
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition xml.c:2184
bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:1028
int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
Definition xml.c:584
bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
Definition xml.c:674
xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
Definition xml.c:385
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition xml.c:2121
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition xml.c:959
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition xml.c:2288
xmlNode * copy_xml(xmlNode *src)
Definition xml.c:2461
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition xml.c:998
bool pcmk__xml_tree_foreach(xmlNode *xml, bool(*fn)(xmlNode *, void *), void *user_data)
Definition xml.c:42
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition xml.c:720
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Definition xml.c:2555
gchar * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:1110
int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition xml.c:342
void pcmk__strip_xml_text(xmlNode *xml)
Definition xml.c:923
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition xml.c:1549
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition xml.c:2261
char * crm_xml_escape(const char *text)
Definition xml.c:2402
int pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
Definition xml.c:525
xmlDoc * getDocPtr(xmlNode *node)
Definition xml.c:2330
bool xml_tracking_changes(xmlNode *xml)
Definition xml.c:318
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition xml.c:2279
void crm_xml_cleanup(void)
Definition xml.c:2143
bool xml_document_dirty(xmlNode *xml)
Definition xml.c:325
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
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition xml.c:303
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition xml.c:1541
void free_xml(xmlNode *child)
Definition xml.c:867
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition xml.c:976
void crm_destroy_xml(gpointer data)
Definition xml.c:2324
int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
Definition xml.c:1839
int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
Definition xml.c:1952
void pcmk__mark_xml_node_dirty(xmlNode *xml)
Definition xml.c:101
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition xml.c:2317
#define XML_NODE_PRIVATE_MAGIC
Definition xml.c:176
void pcmk__xe_set_content(xmlNode *node, const char *format,...)
Definition xml.c:758
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition xml.c:2534
#define XML_DOC_PRIVATE_MAGIC
Definition xml.c:175
void pcmk_free_xml_subtree(xmlNode *xml)
Definition xml.c:795
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:2476
void xml_remove_prop(xmlNode *obj, const char *name)
Definition xml.c:2561
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Definition xml.c:2719
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition xml.c:2072
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition xml.c:2241
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition xml.c:2641
Wrappers for and extensions to libxml2.
const xmlChar * pcmkXmlStr
Definition xml.h:41
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition xpath.c:189
Deprecated Pacemaker XML API.
@ pcmk__xaf_no_overwrite
Don't overwrite existing values.
@ pcmk__xaf_score_update
xml_private_flags
@ pcmk__xf_deleted
@ pcmk__xf_acl_enabled
@ pcmk__xf_created
@ pcmk__xf_dirty
@ pcmk__xf_skip
@ pcmk__xf_acl_write
@ pcmk__xf_lazy
@ pcmk__xf_tracking
@ pcmk__xf_none
@ pcmk__xf_processed
@ pcmk__xf_moved
GString * pcmk__element_xpath(const xmlNode *xml)
Definition xpath.c:256
pcmk__xml_escape_type
@ pcmk__xml_escape_attr_pretty
@ pcmk__xml_escape_attr
@ pcmk__xml_escape_text
pcmk__xml_artefact_ns
@ pcmk__xml_artefact_ns_legacy_xslt
@ pcmk__xml_artefact_ns_legacy_rng
@ pcmk__xml_artefact_ns_base_rng
@ pcmk__xml_artefact_ns_base_xslt
#define PCMK_XA_CIB_LAST_WRITTEN
Definition xml_names.h:239
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_ID_REF
Definition xml_names.h:298