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