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