pacemaker 2.1.5-a3f44794f94
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
48bool
49pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
50{
51 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
52 return FALSE;
53 } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
55 return FALSE;
56 } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
58 return FALSE;
59 }
60 return TRUE;
61}
62
73static inline void
74insert_prefix(int options, GString *buffer, int depth)
75{
76 int spaces = 0;
77
79 return;
80 }
81 CRM_ASSERT(buffer != NULL);
82 CRM_CHECK(depth >= 0, depth = 0);
83
84 /* With -O2, this loop is faster than one g_string_append_printf() call with
85 * width == 2 * depth
86 */
87 spaces = 2 * depth;
88 for (int lpc = 0; lpc < spaces; lpc++) {
89 g_string_append_c(buffer, ' ');
90 }
91}
92
93static inline void
94set_parent_flag(xmlNode *xml, long flag)
95{
96 for(; xml; xml = xml->parent) {
97 xml_node_private_t *nodepriv = xml->_private;
98
99 if (nodepriv == NULL) {
100 /* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
101 } else {
102 pcmk__set_xml_flags(nodepriv, flag);
103 }
104 }
105}
106
107void
109{
110 if(xml && xml->doc && xml->doc->_private){
111 /* During calls to xmlDocCopyNode(), xml->doc may be unset */
112 xml_doc_private_t *docpriv = xml->doc->_private;
113
114 pcmk__set_xml_flags(docpriv, flag);
115 }
116}
117
118// Mark document, element, and all element's parents as changed
119static inline void
120mark_xml_node_dirty(xmlNode *xml)
121{
123 set_parent_flag(xml, pcmk__xf_dirty);
124}
125
126// Clear flags on XML node and its children
127static void
128reset_xml_node_flags(xmlNode *xml)
129{
130 xmlNode *cIter = NULL;
131 xml_node_private_t *nodepriv = xml->_private;
132
133 if (nodepriv) {
134 nodepriv->flags = 0;
135 }
136
137 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
138 cIter = pcmk__xml_next(cIter)) {
139 reset_xml_node_flags(cIter);
140 }
141}
142
143// Set xpf_created flag on XML node and any children
144void
146{
147 xmlNode *cIter = NULL;
148 xml_node_private_t *nodepriv = xml->_private;
149
150 if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) {
151 if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
153 mark_xml_node_dirty(xml);
154 }
155 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
156 cIter = pcmk__xml_next(cIter)) {
158 }
159 }
160}
161
162void
164{
165 xmlNode *parent = a->parent;
166 xml_node_private_t *nodepriv = a->_private;
167
170 mark_xml_node_dirty(parent);
171}
172
173#define XML_DOC_PRIVATE_MAGIC 0x81726354UL
174#define XML_NODE_PRIVATE_MAGIC 0x54637281UL
175
176// Free an XML object previously marked as deleted
177static void
178free_deleted_object(void *data)
179{
180 if(data) {
181 pcmk__deleted_xml_t *deleted_obj = data;
182
183 free(deleted_obj->path);
184 free(deleted_obj);
185 }
186}
187
188// Free and NULL user, ACLs, and deleted objects in an XML node's private data
189static void
190reset_xml_private_data(xml_doc_private_t *docpriv)
191{
192 if (docpriv != NULL) {
194
195 free(docpriv->user);
196 docpriv->user = NULL;
197
198 if (docpriv->acls != NULL) {
199 pcmk__free_acls(docpriv->acls);
200 docpriv->acls = NULL;
201 }
202
203 if(docpriv->deleted_objs) {
204 g_list_free_full(docpriv->deleted_objs, free_deleted_object);
205 docpriv->deleted_objs = NULL;
206 }
207 }
208}
209
210// Free all private data associated with an XML node
211static void
212free_private_data(xmlNode *node)
213{
214 /* Note:
215
216 This function frees private data assosciated with an XML node,
217 unless the function is being called as a result of internal
218 XSLT cleanup.
219
220 That could happen through, for example, the following chain of
221 function calls:
222
223 xsltApplyStylesheetInternal
224 -> xsltFreeTransformContext
225 -> xsltFreeRVTs
226 -> xmlFreeDoc
227
228 And in that case, the node would fulfill three conditions:
229
230 1. It would be a standalone document (i.e. it wouldn't be
231 part of a document)
232 2. It would have a space-prefixed name (for reference, please
233 see xsltInternals.h: XSLT_MARK_RES_TREE_FRAG)
234 3. It would carry its own payload in the _private field.
235
236 We do not free data in this circumstance to avoid a failed
237 assertion on the XML_*_PRIVATE_MAGIC later.
238
239 */
240 if (node->name == NULL || node->name[0] != ' ') {
241 if (node->_private) {
242 if (node->type == XML_DOCUMENT_NODE) {
243 reset_xml_private_data(node->_private);
244 } else {
245 CRM_ASSERT(((xml_node_private_t *) node->_private)->check
247 /* nothing dynamically allocated nested */
248 }
249 free(node->_private);
250 node->_private = NULL;
251 }
252 }
253}
254
255// Allocate and initialize private data for an XML node
256static void
257new_private_data(xmlNode *node)
258{
259 switch (node->type) {
260 case XML_DOCUMENT_NODE: {
261 xml_doc_private_t *docpriv = NULL;
262 docpriv = calloc(1, sizeof(xml_doc_private_t));
263 CRM_ASSERT(docpriv != NULL);
264 docpriv->check = XML_DOC_PRIVATE_MAGIC;
265 /* Flags will be reset if necessary when tracking is enabled */
267 node->_private = docpriv;
268 break;
269 }
270 case XML_ELEMENT_NODE:
271 case XML_ATTRIBUTE_NODE:
272 case XML_COMMENT_NODE: {
273 xml_node_private_t *nodepriv = NULL;
274 nodepriv = calloc(1, sizeof(xml_node_private_t));
275 CRM_ASSERT(nodepriv != NULL);
276 nodepriv->check = XML_NODE_PRIVATE_MAGIC;
277 /* Flags will be reset if necessary when tracking is enabled */
279 node->_private = nodepriv;
280 if (pcmk__tracking_xml_changes(node, FALSE)) {
281 /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
282 * not hooked up at the point we are called
283 */
284 mark_xml_node_dirty(node);
285 }
286 break;
287 }
288 case XML_TEXT_NODE:
289 case XML_DTD_NODE:
290 case XML_CDATA_SECTION_NODE:
291 break;
292 default:
293 /* Ignore */
294 crm_trace("Ignoring %p %d", node, node->type);
295 CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
296 break;
297 }
298}
299
300void
301xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
302{
304 crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
306 if(enforce_acls) {
307 if(acl_source == NULL) {
308 acl_source = xml;
309 }
311 pcmk__unpack_acl(acl_source, xml, user);
312 pcmk__apply_acl(xml);
313 }
314}
315
316bool xml_tracking_changes(xmlNode * xml)
317{
318 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
319 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
321}
322
323bool xml_document_dirty(xmlNode *xml)
324{
325 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
326 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
328}
329
339int
340pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
341{
342 int position = 0;
343
344 for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
345 xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
346
347 if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
348 position++;
349 }
350 }
351
352 return position;
353}
354
355// This also clears attribute's flags if not marked as deleted
356static bool
357marked_as_deleted(xmlAttrPtr a, void *user_data)
358{
359 xml_node_private_t *nodepriv = a->_private;
360
361 if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
362 return true;
363 }
364 nodepriv->flags = pcmk__xf_none;
365 return false;
366}
367
368// Remove all attributes marked as deleted from an XML node
369static void
370accept_attr_deletions(xmlNode *xml)
371{
372 // Clear XML node's flags
373 ((xml_node_private_t *) xml->_private)->flags = pcmk__xf_none;
374
375 // Remove this XML node's attributes that were marked as deleted
376 pcmk__xe_remove_matching_attrs(xml, marked_as_deleted, NULL);
377
378 // Recursively do the same for this XML node's children
379 for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
380 cIter = pcmk__xml_next(cIter)) {
381 accept_attr_deletions(cIter);
382 }
383}
384
393xmlNode *
394pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
395{
396 CRM_CHECK(needle != NULL, return NULL);
397
398 if (needle->type == XML_COMMENT_NODE) {
399 return pcmk__xc_match(haystack, needle, exact);
400
401 } else {
402 const char *id = ID(needle);
403 const char *attr = (id == NULL)? NULL : XML_ATTR_ID;
404
405 return pcmk__xe_match(haystack, crm_element_name(needle), attr, id);
406 }
407}
408
409void
410xml_log_changes(uint8_t log_level, const char *function, xmlNode * xml)
411{
412 GList *gIter = NULL;
413 xml_doc_private_t *docpriv = NULL;
414
415 if (log_level == LOG_NEVER) {
416 return;
417 }
418
419 CRM_ASSERT(xml);
420 CRM_ASSERT(xml->doc);
421
422 docpriv = xml->doc->_private;
423 if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
424 return;
425 }
426
427 for(gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
428 pcmk__deleted_xml_t *deleted_obj = gIter->data;
429
430 if (deleted_obj->position >= 0) {
431 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)",
432 deleted_obj->path, deleted_obj->position);
433
434 } else {
435 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s",
436 deleted_obj->path);
437 }
438 }
439
440 log_data_element(log_level, __FILE__, function, __LINE__, "+ ", xml, 0,
442}
443
444void
445xml_accept_changes(xmlNode * xml)
446{
447 xmlNode *top = NULL;
448 xml_doc_private_t *docpriv = NULL;
449
450 if(xml == NULL) {
451 return;
452 }
453
454 crm_trace("Accepting changes to %p", xml);
455 docpriv = xml->doc->_private;
456 top = xmlDocGetRootElement(xml->doc);
457
458 reset_xml_private_data(xml->doc->_private);
459
460 if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
461 docpriv->flags = pcmk__xf_none;
462 return;
463 }
464
465 docpriv->flags = pcmk__xf_none;
466 accept_attr_deletions(top);
467}
468
469xmlNode *
470find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
471{
472 xmlNode *a_child = NULL;
473 const char *name = "NULL";
474
475 if (root != NULL) {
476 name = crm_element_name(root);
477 }
478
479 if (search_path == NULL) {
480 crm_warn("Will never find <NULL>");
481 return NULL;
482 }
483
484 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
485 a_child = pcmk__xml_next(a_child)) {
486 if (strcmp((const char *)a_child->name, search_path) == 0) {
487/* crm_trace("returning node (%s).", crm_element_name(a_child)); */
488 return a_child;
489 }
490 }
491
492 if (must_find) {
493 crm_warn("Could not find %s in %s.", search_path, name);
494 } else if (root != NULL) {
495 crm_trace("Could not find %s in %s.", search_path, name);
496 } else {
497 crm_trace("Could not find %s in <NULL>.", search_path);
498 }
499
500 return NULL;
501}
502
503#define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \
504 (v), pcmk__str_none)
505
519xmlNode *
520pcmk__xe_match(const xmlNode *parent, const char *node_name,
521 const char *attr_n, const char *attr_v)
522{
523 /* ensure attr_v specified when attr_n is */
524 CRM_CHECK(attr_n == NULL || attr_v != NULL, return NULL);
525
526 for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
527 child = pcmk__xml_next(child)) {
528 if (pcmk__str_eq(node_name, (const char *) (child->name),
530 && ((attr_n == NULL) || attr_matches(child, attr_n, attr_v))) {
531 return child;
532 }
533 }
534 crm_trace("XML child node <%s%s%s%s%s> not found in %s",
535 (node_name? node_name : "(any)"),
536 (attr_n? " " : ""),
537 (attr_n? attr_n : ""),
538 (attr_n? "=" : ""),
539 (attr_n? attr_v : ""),
540 crm_element_name(parent));
541 return NULL;
542}
543
544void
545copy_in_properties(xmlNode * target, xmlNode * src)
546{
547 if (src == NULL) {
548 crm_warn("No node to copy properties from");
549
550 } else if (target == NULL) {
551 crm_err("No node to copy properties into");
552
553 } else {
554 for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
555 const char *p_name = (const char *) a->name;
556 const char *p_value = pcmk__xml_attr_value(a);
557
558 expand_plus_plus(target, p_name, p_value);
559 if (xml_acl_denied(target)) {
560 crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name);
561 return;
562 }
563 }
564 }
565
566 return;
567}
568
577void
579{
580 /* TODO: Remove recursion and use xpath searches for value++ */
581 xmlNode *child = NULL;
582
583 for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
584 const char *p_name = (const char *) a->name;
585 const char *p_value = pcmk__xml_attr_value(a);
586
587 expand_plus_plus(target, p_name, p_value);
588 }
589 for (child = pcmk__xml_first_child(target); child != NULL;
590 child = pcmk__xml_next(child)) {
592 }
593}
594
611void
612expand_plus_plus(xmlNode * target, const char *name, const char *value)
613{
614 int offset = 1;
615 int name_len = 0;
616 int int_value = 0;
617 int value_len = 0;
618
619 const char *old_value = NULL;
620
621 if (target == NULL || value == NULL || name == NULL) {
622 return;
623 }
624
625 old_value = crm_element_value(target, name);
626
627 if (old_value == NULL) {
628 /* if no previous value, set unexpanded */
629 goto set_unexpanded;
630
631 } else if (strstr(value, name) != value) {
632 goto set_unexpanded;
633 }
634
635 name_len = strlen(name);
636 value_len = strlen(value);
637 if (value_len < (name_len + 2)
638 || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
639 goto set_unexpanded;
640 }
641
642 /* if we are expanding ourselves,
643 * then no previous value was set and leave int_value as 0
644 */
645 if (old_value != value) {
646 int_value = char2score(old_value);
647 }
648
649 if (value[name_len + 1] != '+') {
650 const char *offset_s = value + (name_len + 2);
651
652 offset = char2score(offset_s);
653 }
654 int_value += offset;
655
656 if (int_value > INFINITY) {
657 int_value = (int)INFINITY;
658 }
659
660 crm_xml_add_int(target, name, int_value);
661 return;
662
663 set_unexpanded:
664 if (old_value == value) {
665 /* the old value is already set, nothing to do */
666 return;
667 }
668 crm_xml_add(target, name, value);
669 return;
670}
671
681void
683 bool (*match)(xmlAttrPtr, void *),
684 void *user_data)
685{
686 xmlAttrPtr next = NULL;
687
688 for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
689 next = a->next; // Grab now because attribute might get removed
690 if ((match == NULL) || match(a, user_data)) {
691 if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
692 crm_trace("ACLs prevent removal of attributes (%s and "
693 "possibly others) from %s element",
694 (const char *) a->name, (const char *) element->name);
695 return; // ACLs apply to element, not particular attributes
696 }
697
698 if (pcmk__tracking_xml_changes(element, false)) {
699 // Leave (marked for removal) until after diff is calculated
700 set_parent_flag(element, pcmk__xf_dirty);
703 } else {
704 xmlRemoveProp(a);
705 }
706 }
707 }
708}
709
710xmlDoc *
711getDocPtr(xmlNode * node)
712{
713 xmlDoc *doc = NULL;
714
715 CRM_CHECK(node != NULL, return NULL);
716
717 doc = node->doc;
718 if (doc == NULL) {
719 doc = xmlNewDoc((pcmkXmlStr) "1.0");
720 xmlDocSetRootElement(doc, node);
721 xmlSetTreeDoc(node, doc);
722 }
723 return doc;
724}
725
726xmlNode *
727add_node_copy(xmlNode * parent, xmlNode * src_node)
728{
729 xmlNode *child = NULL;
730 xmlDoc *doc = getDocPtr(parent);
731
732 CRM_CHECK(src_node != NULL, return NULL);
733
734 child = xmlDocCopyNode(src_node, doc, 1);
735 xmlAddChild(parent, child);
737 return child;
738}
739
740int
741add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child)
742{
743 add_node_copy(parent, child);
744 free_xml(child);
745 return 1;
746}
747
748xmlNode *
749create_xml_node(xmlNode * parent, const char *name)
750{
751 xmlDoc *doc = NULL;
752 xmlNode *node = NULL;
753
754 if (pcmk__str_empty(name)) {
755 CRM_CHECK(name != NULL && name[0] == 0, return NULL);
756 return NULL;
757 }
758
759 if (parent == NULL) {
760 doc = xmlNewDoc((pcmkXmlStr) "1.0");
761 node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
762 xmlDocSetRootElement(doc, node);
763
764 } else {
765 doc = getDocPtr(parent);
766 node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
767 xmlAddChild(parent, node);
768 }
770 return node;
771}
772
773xmlNode *
774pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content)
775{
776 xmlNode *node = create_xml_node(parent, name);
777
778 if (node != NULL) {
779 xmlNodeSetContent(node, (pcmkXmlStr) content);
780 }
781
782 return node;
783}
784
785xmlNode *
786pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id,
787 const char *class_name, const char *text)
788{
789 xmlNode *node = pcmk_create_xml_text_node(parent, element_name, text);
790
791 if (class_name != NULL) {
792 crm_xml_add(node, "class", class_name);
793 }
794
795 if (id != NULL) {
796 crm_xml_add(node, "id", id);
797 }
798
799 return node;
800}
801
807void
809{
810 xmlUnlinkNode(xml); // Detaches from parent and siblings
811 xmlFreeNode(xml); // Frees
812}
813
814static void
815free_xml_with_position(xmlNode * child, int position)
816{
817 if (child != NULL) {
818 xmlNode *top = NULL;
819 xmlDoc *doc = child->doc;
820 xml_node_private_t *nodepriv = child->_private;
821 xml_doc_private_t *docpriv = NULL;
822
823 if (doc != NULL) {
824 top = xmlDocGetRootElement(doc);
825 }
826
827 if (doc != NULL && top == child) {
828 /* Free everything */
829 xmlFreeDoc(doc);
830
831 } else if (pcmk__check_acl(child, NULL, pcmk__xf_acl_write) == FALSE) {
832 GString *xpath = NULL;
833
834 pcmk__log_else(LOG_TRACE, return);
835 xpath = pcmk__element_xpath(child);
836 qb_log_from_external_source(__func__, __FILE__,
837 "Cannot remove %s %x", LOG_TRACE,
838 __LINE__, 0, (const char *) xpath->str,
839 nodepriv->flags);
840 g_string_free(xpath, TRUE);
841 return;
842
843 } else {
844 if (doc && pcmk__tracking_xml_changes(child, FALSE)
845 && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
846
847 GString *xpath = pcmk__element_xpath(child);
848
849 if (xpath != NULL) {
850 pcmk__deleted_xml_t *deleted_obj = NULL;
851
852 crm_trace("Deleting %s %p from %p",
853 (const char *) xpath->str, child, doc);
854
855 deleted_obj = calloc(1, sizeof(pcmk__deleted_xml_t));
856 deleted_obj->path = strdup((const char *) xpath->str);
857
858 CRM_ASSERT(deleted_obj->path != NULL);
859 g_string_free(xpath, TRUE);
860
861 deleted_obj->position = -1;
862 /* Record the "position" only for XML comments for now */
863 if (child->type == XML_COMMENT_NODE) {
864 if (position >= 0) {
865 deleted_obj->position = position;
866
867 } else {
868 deleted_obj->position = pcmk__xml_position(child,
870 }
871 }
872
873 docpriv = doc->_private;
874 docpriv->deleted_objs = g_list_append(docpriv->deleted_objs, deleted_obj);
876 }
877 }
879 }
880 }
881}
882
883
884void
885free_xml(xmlNode * child)
886{
887 free_xml_with_position(child, -1);
888}
889
890xmlNode *
891copy_xml(xmlNode * src)
892{
893 xmlDoc *doc = xmlNewDoc((pcmkXmlStr) "1.0");
894 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
895
896 xmlDocSetRootElement(doc, copy);
897 xmlSetTreeDoc(copy, doc);
898 return copy;
899}
900
901static void
902log_xmllib_err(void *ctx, const char *fmt, ...)
903G_GNUC_PRINTF(2, 3);
904
905// Log an XML library error
906static void
907log_xmllib_err(void *ctx, const char *fmt, ...)
908{
909 va_list ap;
910 static struct qb_log_callsite *xml_error_cs = NULL;
911
912 if (xml_error_cs == NULL) {
913 xml_error_cs = qb_log_callsite_get(
914 __func__, __FILE__, "xml library error", LOG_TRACE, __LINE__, crm_trace_nonlog);
915 }
916
917 va_start(ap, fmt);
918 if (xml_error_cs && xml_error_cs->targets) {
919 PCMK__XML_LOG_BASE(LOG_ERR, TRUE,
920 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, "xml library error",
921 TRUE, TRUE),
922 "XML Error: ", fmt, ap);
923 } else {
924 PCMK__XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
925 }
926 va_end(ap);
927}
928
929xmlNode *
930string2xml(const char *input)
931{
932 xmlNode *xml = NULL;
933 xmlDocPtr output = NULL;
934 xmlParserCtxtPtr ctxt = NULL;
935 xmlErrorPtr last_error = NULL;
936
937 if (input == NULL) {
938 crm_err("Can't parse NULL input");
939 return NULL;
940 }
941
942 /* create a parser context */
943 ctxt = xmlNewParserCtxt();
944 CRM_CHECK(ctxt != NULL, return NULL);
945
946 xmlCtxtResetLastError(ctxt);
947 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
948 output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
950 if (output) {
951 xml = xmlDocGetRootElement(output);
952 }
953 last_error = xmlCtxtGetLastError(ctxt);
954 if (last_error && last_error->code != XML_ERR_OK) {
955 /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
956 /*
957 * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
958 * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
959 */
960 crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
961 last_error->domain, last_error->level, last_error->code, last_error->message);
962
963 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
964 CRM_LOG_ASSERT("Cannot parse an empty string");
965
966 } else if (last_error->code != XML_ERR_DOCUMENT_END) {
967 crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
968 input);
969 if (xml != NULL) {
970 crm_log_xml_err(xml, "Partial");
971 }
972
973 } else {
974 int len = strlen(input);
975 int lpc = 0;
976
977 while(lpc < len) {
978 crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
979 lpc += 80;
980 }
981
982 CRM_LOG_ASSERT("String parsing error");
983 }
984 }
985
986 xmlFreeParserCtxt(ctxt);
987 return xml;
988}
989
990xmlNode *
992{
993 size_t data_length = 0;
994 size_t read_chars = 0;
995
996 char *xml_buffer = NULL;
997 xmlNode *xml_obj = NULL;
998
999 do {
1000 xml_buffer = pcmk__realloc(xml_buffer, data_length + PCMK__BUFFER_SIZE);
1001 read_chars = fread(xml_buffer + data_length, 1, PCMK__BUFFER_SIZE,
1002 stdin);
1003 data_length += read_chars;
1004 } while (read_chars == PCMK__BUFFER_SIZE);
1005
1006 if (data_length == 0) {
1007 crm_warn("No XML supplied on stdin");
1008 free(xml_buffer);
1009 return NULL;
1010 }
1011
1012 xml_buffer[data_length] = '\0';
1013 xml_obj = string2xml(xml_buffer);
1014 free(xml_buffer);
1015
1016 crm_log_xml_trace(xml_obj, "Created fragment");
1017 return xml_obj;
1018}
1019
1020static char *
1021decompress_file(const char *filename)
1022{
1023 char *buffer = NULL;
1024 int rc = 0;
1025 size_t length = 0, read_len = 0;
1026 BZFILE *bz_file = NULL;
1027 FILE *input = fopen(filename, "r");
1028
1029 if (input == NULL) {
1030 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
1031 return NULL;
1032 }
1033
1034 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
1035 if (rc != BZ_OK) {
1036 crm_err("Could not prepare to read compressed %s: %s "
1037 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1038 BZ2_bzReadClose(&rc, bz_file);
1039 fclose(input);
1040 return NULL;
1041 }
1042
1043 rc = BZ_OK;
1044 // cppcheck seems not to understand the abort-logic in pcmk__realloc
1045 // cppcheck-suppress memleak
1046 while (rc == BZ_OK) {
1047 buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1);
1048 read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
1049
1050 crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
1051
1052 if (rc == BZ_OK || rc == BZ_STREAM_END) {
1053 length += read_len;
1054 }
1055 }
1056
1057 buffer[length] = '\0';
1058
1059 if (rc != BZ_STREAM_END) {
1060 crm_err("Could not read compressed %s: %s "
1061 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1062 free(buffer);
1063 buffer = NULL;
1064 }
1065
1066 BZ2_bzReadClose(&rc, bz_file);
1067 fclose(input);
1068 return buffer;
1069}
1070
1077void
1079{
1080 xmlNode *iter = xml->children;
1081
1082 while (iter) {
1083 xmlNode *next = iter->next;
1084
1085 switch (iter->type) {
1086 case XML_TEXT_NODE:
1087 /* Remove it */
1089 break;
1090
1091 case XML_ELEMENT_NODE:
1092 /* Search it */
1094 break;
1095
1096 default:
1097 /* Leave it */
1098 break;
1099 }
1100
1101 iter = next;
1102 }
1103}
1104
1105xmlNode *
1106filename2xml(const char *filename)
1107{
1108 xmlNode *xml = NULL;
1109 xmlDocPtr output = NULL;
1110 bool uncompressed = true;
1111 xmlParserCtxtPtr ctxt = NULL;
1112 xmlErrorPtr last_error = NULL;
1113
1114 /* create a parser context */
1115 ctxt = xmlNewParserCtxt();
1116 CRM_CHECK(ctxt != NULL, return NULL);
1117
1118 xmlCtxtResetLastError(ctxt);
1119 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1120
1121 if (filename) {
1122 uncompressed = !pcmk__ends_with_ext(filename, ".bz2");
1123 }
1124
1125 if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
1126 /* STDIN_FILENO == fileno(stdin) */
1127 output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
1129
1130 } else if (uncompressed) {
1131 output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
1132
1133 } else {
1134 char *input = decompress_file(filename);
1135
1136 output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
1138 free(input);
1139 }
1140
1141 if (output && (xml = xmlDocGetRootElement(output))) {
1143 }
1144
1145 last_error = xmlCtxtGetLastError(ctxt);
1146 if (last_error && last_error->code != XML_ERR_OK) {
1147 /* crm_abort(__FILE__,__func__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
1148 /*
1149 * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
1150 * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
1151 */
1152 crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
1153 last_error->domain, last_error->level, last_error->code, last_error->message);
1154
1155 if (last_error && last_error->code != XML_ERR_OK) {
1156 crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
1157 if (xml != NULL) {
1158 crm_log_xml_err(xml, "Partial");
1159 }
1160 }
1161 }
1162
1163 xmlFreeParserCtxt(ctxt);
1164 return xml;
1165}
1166
1175const char *
1177{
1178 const char *now_str = pcmk__epoch2str(NULL);
1179
1181 now_str ? now_str : "Could not determine current time");
1182}
1183
1189void
1191{
1192 char *c;
1193
1194 for (c = id; *c; ++c) {
1195 /* @TODO Sanitize more comprehensively */
1196 switch (*c) {
1197 case ':':
1198 case '#':
1199 *c = '.';
1200 }
1201 }
1202}
1203
1211void
1212crm_xml_set_id(xmlNode *xml, const char *format, ...)
1213{
1214 va_list ap;
1215 int len = 0;
1216 char *id = NULL;
1217
1218 /* equivalent to crm_strdup_printf() */
1219 va_start(ap, format);
1220 len = vasprintf(&id, format, ap);
1221 va_end(ap);
1222 CRM_ASSERT(len > 0);
1223
1225 crm_xml_add(xml, XML_ATTR_ID, id);
1226 free(id);
1227}
1228
1241static int
1242write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
1243 bool compress, unsigned int *nbytes)
1244{
1245 int rc = pcmk_rc_ok;
1246 char *buffer = NULL;
1247
1248 *nbytes = 0;
1249 crm_log_xml_trace(xml_node, "writing");
1250
1251 buffer = dump_xml_formatted(xml_node);
1252 CRM_CHECK(buffer && strlen(buffer),
1253 crm_log_xml_warn(xml_node, "formatting failed");
1254 rc = pcmk_rc_error;
1255 goto bail);
1256
1257 if (compress) {
1258 unsigned int in = 0;
1259 BZFILE *bz_file = NULL;
1260
1261 rc = BZ_OK;
1262 bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1263 if (rc != BZ_OK) {
1264 crm_warn("Not compressing %s: could not prepare file stream: %s "
1265 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1266 } else {
1267 BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1268 if (rc != BZ_OK) {
1269 crm_warn("Not compressing %s: could not compress data: %s "
1270 CRM_XS " bzerror=%d errno=%d",
1271 filename, bz2_strerror(rc), rc, errno);
1272 }
1273 }
1274
1275 if (rc == BZ_OK) {
1276 BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1277 if (rc != BZ_OK) {
1278 crm_warn("Not compressing %s: could not write compressed data: %s "
1279 CRM_XS " bzerror=%d errno=%d",
1280 filename, bz2_strerror(rc), rc, errno);
1281 *nbytes = 0; // retry without compression
1282 } else {
1283 crm_trace("Compressed XML for %s from %u bytes to %u",
1284 filename, in, *nbytes);
1285 }
1286 }
1287 rc = pcmk_rc_ok; // Either true, or we'll retry without compression
1288 }
1289
1290 if (*nbytes == 0) {
1291 rc = fprintf(stream, "%s", buffer);
1292 if (rc < 0) {
1293 rc = errno;
1294 crm_perror(LOG_ERR, "writing %s", filename);
1295 } else {
1296 *nbytes = (unsigned int) rc;
1297 rc = pcmk_rc_ok;
1298 }
1299 }
1300
1301 bail:
1302
1303 if (fflush(stream) != 0) {
1304 rc = errno;
1305 crm_perror(LOG_ERR, "flushing %s", filename);
1306 }
1307
1308 /* Don't report error if the file does not support synchronization */
1309 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1310 rc = errno;
1311 crm_perror(LOG_ERR, "synchronizing %s", filename);
1312 }
1313
1314 fclose(stream);
1315
1316 crm_trace("Saved %d bytes to %s as XML", *nbytes, filename);
1317 free(buffer);
1318
1319 return rc;
1320}
1321
1332int
1333write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
1334{
1335 FILE *stream = NULL;
1336 unsigned int nbytes = 0;
1337 int rc = pcmk_rc_ok;
1338
1339 CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
1340 stream = fdopen(fd, "w");
1341 if (stream == NULL) {
1342 return -errno;
1343 }
1344 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1345 if (rc != pcmk_rc_ok) {
1346 return pcmk_rc2legacy(rc);
1347 }
1348 return (int) nbytes;
1349}
1350
1360int
1361write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
1362{
1363 FILE *stream = NULL;
1364 unsigned int nbytes = 0;
1365 int rc = pcmk_rc_ok;
1366
1367 CRM_CHECK(xml_node && filename, return -EINVAL);
1368 stream = fopen(filename, "w");
1369 if (stream == NULL) {
1370 return -errno;
1371 }
1372 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1373 if (rc != pcmk_rc_ok) {
1374 return pcmk_rc2legacy(rc);
1375 }
1376 return (int) nbytes;
1377}
1378
1379// Replace a portion of a dynamically allocated string (reallocating memory)
1380static char *
1381replace_text(char *text, int start, size_t *length, const char *replace)
1382{
1383 size_t offset = strlen(replace) - 1; // We have space for 1 char already
1384
1385 *length += offset;
1386 text = pcmk__realloc(text, *length);
1387
1388 for (size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1389 text[lpc] = text[lpc - offset];
1390 }
1391
1392 memcpy(text + start, replace, offset + 1);
1393 return text;
1394}
1395
1405char *
1406crm_xml_escape(const char *text)
1407{
1408 size_t length;
1409 char *copy;
1410
1411 /*
1412 * When xmlCtxtReadDoc() parses &lt; and friends in a
1413 * value, it converts them to their human readable
1414 * form.
1415 *
1416 * If one uses xmlNodeDump() to convert it back to a
1417 * string, all is well, because special characters are
1418 * converted back to their escape sequences.
1419 *
1420 * However xmlNodeDump() is randomly dog slow, even with the same
1421 * input. So we need to replicate the escaping in our custom
1422 * version so that the result can be re-parsed by xmlCtxtReadDoc()
1423 * when necessary.
1424 */
1425
1426 if (text == NULL) {
1427 return NULL;
1428 }
1429
1430 length = 1 + strlen(text);
1431 copy = strdup(text);
1432 CRM_ASSERT(copy != NULL);
1433 for (size_t index = 0; index < length; index++) {
1434 if(copy[index] & 0x80 && copy[index+1] & 0x80){
1435 index++;
1436 break;
1437 }
1438 switch (copy[index]) {
1439 case 0:
1440 break;
1441 case '<':
1442 copy = replace_text(copy, index, &length, "&lt;");
1443 break;
1444 case '>':
1445 copy = replace_text(copy, index, &length, "&gt;");
1446 break;
1447 case '"':
1448 copy = replace_text(copy, index, &length, "&quot;");
1449 break;
1450 case '\'':
1451 copy = replace_text(copy, index, &length, "&apos;");
1452 break;
1453 case '&':
1454 copy = replace_text(copy, index, &length, "&amp;");
1455 break;
1456 case '\t':
1457 /* Might as well just expand to a few spaces... */
1458 copy = replace_text(copy, index, &length, " ");
1459 break;
1460 case '\n':
1461 copy = replace_text(copy, index, &length, "\\n");
1462 break;
1463 case '\r':
1464 copy = replace_text(copy, index, &length, "\\r");
1465 break;
1466 default:
1467 /* Check for and replace non-printing characters with their octal equivalent */
1468 if(copy[index] < ' ' || copy[index] > '~') {
1469 char *replace = crm_strdup_printf("\\%.3o", copy[index]);
1470
1471 copy = replace_text(copy, index, &length, replace);
1472 free(replace);
1473 }
1474 }
1475 }
1476 return copy;
1477}
1478
1479/* Keep this inline. De-inlining resulted in a 0.6% average slowdown in
1480 * crm_simulate on cts/scheduler/xml during testing.
1481 */
1482
1491static inline void
1492dump_xml_attr(const xmlAttr *attr, int options, GString *buffer)
1493{
1494 char *p_value = NULL;
1495 const char *p_name = NULL;
1496 xml_node_private_t *nodepriv = NULL;
1497
1498 CRM_ASSERT(buffer != NULL);
1499 if (attr == NULL || attr->children == NULL) {
1500 return;
1501 }
1502
1503 nodepriv = attr->_private;
1504 if (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1505 return;
1506 }
1507
1508 p_name = (const char *) attr->name;
1509 p_value = crm_xml_escape((const char *)attr->children->content);
1510 pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"",
1511 NULL);
1512
1513 free(p_value);
1514}
1515
1516// Log an XML element or comment (and any children) in a formatted way
1517static void
1518log_xml_node_and_children(GString *buffer, int log_level, const char *file,
1519 const char *function, int line, const char *prefix,
1520 const xmlNode *data, int depth, int options)
1521{
1522 const char *name = NULL;
1523 const char *hidden = NULL;
1524
1525 xmlNode *child = NULL;
1526
1527 CRM_ASSERT(buffer != NULL);
1528
1529 if ((data == NULL) || (log_level == LOG_NEVER)
1530 || ((data->type != XML_COMMENT_NODE)
1531 && (data->type != XML_ELEMENT_NODE))) {
1532 return;
1533 }
1534
1535 name = crm_element_name(data);
1536
1537 g_string_truncate(buffer, 0);
1538
1539 if (pcmk_is_set(options, xml_log_option_open)) {
1540 insert_prefix(options, buffer, depth);
1541
1542 if (data->type == XML_COMMENT_NODE) {
1543 pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->",
1544 NULL);
1545
1546 } else {
1547 pcmk__g_strcat(buffer, "<", name, NULL);
1548
1549 hidden = crm_element_value(data, "hidden");
1550 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL;
1551 a = a->next) {
1552
1553 xml_node_private_t *nodepriv = a->_private;
1554 const char *p_name = (const char *) a->name;
1555 const char *p_value = pcmk__xml_attr_value(a);
1556 char *p_copy = NULL;
1557
1558 if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1559 continue;
1560 } else if (pcmk_any_flags_set(options,
1563 && (strcmp(XML_DIFF_MARKER, p_name) == 0)) {
1564 continue;
1565
1566 } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1567 p_copy = strdup("*****");
1568
1569 } else {
1570 p_copy = crm_xml_escape(p_value);
1571 }
1572
1573 pcmk__g_strcat(buffer, " ", p_name, "=\"",
1574 pcmk__s(p_copy, "<null>"), "\"", NULL);
1575 free(p_copy);
1576 }
1577
1578 if(xml_has_children(data) == FALSE) {
1579 g_string_append(buffer, "/>");
1580
1581 } else if (pcmk_is_set(options, xml_log_option_children)) {
1582 g_string_append_c(buffer, '>');
1583
1584 } else {
1585 g_string_append(buffer, "/>");
1586 }
1587 }
1588
1589 do_crm_log_alias(log_level, file, function, line, "%s %s", prefix,
1590 (const char *) buffer->str);
1591 }
1592
1593 if(data->type == XML_COMMENT_NODE) {
1594 return;
1595
1596 } else if(xml_has_children(data) == FALSE) {
1597 return;
1598
1599 } else if (pcmk_is_set(options, xml_log_option_children)) {
1600 for (child = pcmk__xml_first_child(data); child != NULL;
1601 child = pcmk__xml_next(child)) {
1602
1603 log_xml_node_and_children(buffer, log_level, file, function, line,
1604 prefix, child, depth + 1,
1605 options|xml_log_option_open
1607 }
1608 }
1609
1610 if (pcmk_is_set(options, xml_log_option_close)) {
1611 g_string_truncate(buffer, 0);
1612
1613 insert_prefix(options, buffer, depth);
1614 pcmk__g_strcat(buffer, "</", name, ">", NULL);
1615
1616 do_crm_log_alias(log_level, file, function, line, "%s %s", prefix,
1617 (const char *) buffer->str);
1618 }
1619}
1620
1621// Log an XML element or comment (and any children) in a formatted way
1622void
1623pcmk__xml_log(int log_level, const char *file, const char *function, int line,
1624 const char *prefix, const xmlNode *data, int depth, int options)
1625{
1626 /* Allocate a buffer once, for log_xml_node_and_children() to truncate and
1627 * reuse in recursive calls
1628 */
1629 GString *buffer = g_string_sized_new(1024);
1630
1631 log_xml_node_and_children(buffer, log_level, file, function, line, prefix,
1632 data, depth, options);
1633
1634 g_string_free(buffer, TRUE);
1635}
1636
1637// Log XML portions that have been marked as changed
1638static void
1639log_xml_changes(int log_level, const char *file, const char *function, int line,
1640 const char *prefix, const xmlNode *data, int depth, int options)
1641{
1642 xml_node_private_t *nodepriv;
1643 char *prefix_m = NULL;
1644 xmlNode *child = NULL;
1645
1646 if ((data == NULL) || (log_level == LOG_NEVER)) {
1647 return;
1648 }
1649
1650 nodepriv = data->_private;
1651
1652 prefix_m = strdup(prefix);
1653 prefix_m[1] = '+';
1654
1655 if (pcmk_all_flags_set(nodepriv->flags, pcmk__xf_dirty|pcmk__xf_created)) {
1656 /* Continue and log full subtree */
1657 pcmk__xml_log(log_level, file, function, line, prefix_m, data, depth,
1660
1661 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
1662 int spaces = 0;
1663 char *prefix_del = NULL;
1664 char *prefix_moved = NULL;
1665 const char *flags = prefix;
1666
1667 if (pcmk_is_set(options, xml_log_option_formatted)) {
1668 CRM_CHECK(depth >= 0, depth = 0);
1669 spaces = 2 * depth;
1670 }
1671
1672 prefix_del = strdup(prefix);
1673 prefix_del[0] = '-';
1674 prefix_del[1] = '-';
1675 prefix_moved = strdup(prefix);
1676 prefix_moved[1] = '~';
1677
1678 if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
1679 flags = prefix_moved;
1680 } else {
1681 flags = prefix;
1682 }
1683
1684 pcmk__xml_log(log_level, file, function, line, flags, data, depth,
1685 options|xml_log_option_open);
1686
1687 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1688 const char *aname = (const char*) a->name;
1689
1690 nodepriv = a->_private;
1691 if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1692 const char *value = crm_element_value(data, aname);
1693 flags = prefix_del;
1694 do_crm_log_alias(log_level, file, function, line,
1695 "%s %*s @%s=%s", flags, spaces, "", aname,
1696 value);
1697
1698 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
1699 const char *value = crm_element_value(data, aname);
1700
1701 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1702 flags = prefix_m;
1703
1704 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_modified)) {
1705 flags = prefix;
1706
1707 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
1708 flags = prefix_moved;
1709
1710 } else {
1711 flags = prefix;
1712 }
1713 do_crm_log_alias(log_level, file, function, line,
1714 "%s %*s @%s=%s", flags, spaces, "", aname,
1715 value);
1716 }
1717 }
1718 free(prefix_moved);
1719 free(prefix_del);
1720
1721 for (child = pcmk__xml_first_child(data); child != NULL;
1722 child = pcmk__xml_next(child)) {
1723 log_xml_changes(log_level, file, function, line, prefix, child,
1724 depth + 1, options);
1725 }
1726
1727 pcmk__xml_log(log_level, file, function, line, prefix, data, depth,
1728 options|xml_log_option_close);
1729
1730 } else {
1731 for (child = pcmk__xml_first_child(data); child != NULL;
1732 child = pcmk__xml_next(child)) {
1733 log_xml_changes(log_level, file, function, line, prefix, child,
1734 depth + 1, options);
1735 }
1736 }
1737
1738 free(prefix_m);
1739
1740}
1741
1742void
1743log_data_element(int log_level, const char *file, const char *function,
1744 int line, const char *prefix, const xmlNode *data, int depth,
1745 int options)
1746{
1747 xmlNode *a_child = NULL;
1748
1749 char *prefix_m = NULL;
1750
1751 if (log_level == LOG_NEVER) {
1752 return;
1753 }
1754
1755 if (prefix == NULL) {
1756 prefix = "";
1757 }
1758
1759 /* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
1760 if (data == NULL) {
1761 do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
1762 "No data to dump as XML");
1763 return;
1764 }
1765
1766 if (pcmk_is_set(options, xml_log_option_dirty_add)) {
1767 log_xml_changes(log_level, file, function, line, prefix, data, depth,
1768 options);
1769 return;
1770 }
1771
1772 if (pcmk_is_set(options, xml_log_option_formatted)) {
1774 && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1775 options |= xml_log_option_diff_all;
1776 prefix_m = strdup(prefix);
1777 prefix_m[1] = '+';
1778 prefix = prefix_m;
1779
1780 } else if (pcmk_is_set(options, xml_log_option_diff_minus)
1781 && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1782 options |= xml_log_option_diff_all;
1783 prefix_m = strdup(prefix);
1784 prefix_m[1] = '-';
1785 prefix = prefix_m;
1786 }
1787 }
1788
1790 && !pcmk_is_set(options, xml_log_option_diff_all)) {
1791 /* Still searching for the actual change */
1792 for (a_child = pcmk__xml_first_child(data); a_child != NULL;
1793 a_child = pcmk__xml_next(a_child)) {
1794 log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
1795 }
1796 } else {
1797 pcmk__xml_log(log_level, file, function, line, prefix, data, depth,
1800 }
1801 free(prefix_m);
1802}
1803
1812static void
1813dump_filtered_xml(const xmlNode *data, int options, GString *buffer)
1814{
1815 CRM_ASSERT(buffer != NULL);
1816
1817 for (const xmlAttr *a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1818 if (!pcmk__xa_filterable((const char *) (a->name))) {
1819 dump_xml_attr(a, options, buffer);
1820 }
1821 }
1822}
1823
1833static void
1834dump_xml_element(const xmlNode *data, int options, GString *buffer, int depth)
1835{
1836 const char *name = NULL;
1837
1838 CRM_ASSERT(buffer != NULL);
1839
1840 if (data == NULL) {
1841 crm_trace("Nothing to dump");
1842 return;
1843 }
1844
1845 name = crm_element_name(data);
1846 CRM_ASSERT(name != NULL);
1847
1848 insert_prefix(options, buffer, depth);
1849 pcmk__g_strcat(buffer, "<", name, NULL);
1850
1851 if (options & xml_log_option_filtered) {
1852 dump_filtered_xml(data, options, buffer);
1853
1854 } else {
1855 for (const xmlAttr *a = pcmk__xe_first_attr(data); a != NULL;
1856 a = a->next) {
1857
1858 dump_xml_attr(a, options, buffer);
1859 }
1860 }
1861
1862 if (data->children == NULL) {
1863 g_string_append(buffer, "/>");
1864
1865 } else {
1866 g_string_append_c(buffer, '>');
1867 }
1868
1869 if (pcmk_is_set(options, xml_log_option_formatted)) {
1870 g_string_append_c(buffer, '\n');
1871 }
1872
1873 if (data->children) {
1874 xmlNode *xChild = NULL;
1875 for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1876 pcmk__xml2text(xChild, options, buffer, depth + 1);
1877 }
1878
1879 insert_prefix(options, buffer, depth);
1880 pcmk__g_strcat(buffer, "</", name, ">", NULL);
1881
1882 if (pcmk_is_set(options, xml_log_option_formatted)) {
1883 g_string_append_c(buffer, '\n');
1884 }
1885 }
1886}
1887
1897static void
1898dump_xml_text(const xmlNode *data, int options, GString *buffer, int depth)
1899{
1900 CRM_ASSERT(buffer != NULL);
1901
1902 if (data == NULL) {
1903 crm_trace("Nothing to dump");
1904 return;
1905 }
1906
1907 insert_prefix(options, buffer, depth);
1908 g_string_append(buffer, (const gchar *) data->content);
1909
1910 if (pcmk_is_set(options, xml_log_option_formatted)) {
1911 g_string_append_c(buffer, '\n');
1912 }
1913}
1914
1924static void
1925dump_xml_cdata(const xmlNode *data, int options, GString *buffer, int depth)
1926{
1927 CRM_ASSERT(buffer != NULL);
1928
1929 if (data == NULL) {
1930 crm_trace("Nothing to dump");
1931 return;
1932 }
1933
1934 insert_prefix(options, buffer, depth);
1935 pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
1936 NULL);
1937
1938 if (pcmk_is_set(options, xml_log_option_formatted)) {
1939 g_string_append_c(buffer, '\n');
1940 }
1941}
1942
1952static void
1953dump_xml_comment(const xmlNode *data, int options, GString *buffer, int depth)
1954{
1955 CRM_ASSERT(buffer != NULL);
1956
1957 if (data == NULL) {
1958 crm_trace("Nothing to dump");
1959 return;
1960 }
1961
1962 insert_prefix(options, buffer, depth);
1963 pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
1964
1965 if (pcmk_is_set(options, xml_log_option_formatted)) {
1966 g_string_append_c(buffer, '\n');
1967 }
1968}
1969
1970#define PCMK__XMLDUMP_STATS 0
1971
1981void
1982pcmk__xml2text(xmlNodePtr data, int options, GString *buffer, int depth)
1983{
1984 CRM_ASSERT(buffer != NULL);
1985
1986 if (data == NULL) {
1987 return;
1988 }
1989
1992 /* libxml's serialization reuse is a good idea, sadly we cannot
1993 apply it for the filtered cases (preceding filtering pass
1994 would preclude further reuse of such in-situ modified XML
1995 in generic context and is likely not a win performance-wise),
1996 and there's also a historically unstable throughput argument
1997 (likely stemming from memory allocation overhead, eventhough
1998 that shall be minimized with defaults preset in crm_xml_init) */
1999#if (PCMK__XMLDUMP_STATS - 0)
2000 time_t next, new = time(NULL);
2001#endif
2002 xmlDoc *doc;
2003 xmlOutputBuffer *xml_buffer;
2004
2005 doc = getDocPtr(data);
2006 /* doc will only be NULL if data is */
2007 CRM_CHECK(doc != NULL, return);
2008
2009 xml_buffer = xmlAllocOutputBuffer(NULL);
2010 CRM_ASSERT(xml_buffer != NULL);
2011
2012 /* XXX we could setup custom allocation scheme for the particular
2013 buffer, but it's subsumed with crm_xml_init that needs to
2014 be invoked prior to entering this function as such, since
2015 its other branch vitally depends on it -- what can be done
2016 about this all is to have a facade parsing functions that
2017 would 100% mark entering libxml code for us, since we don't
2018 do anything as crazy as swapping out the binary form of the
2019 parsed tree (but those would need to be strictly used as
2020 opposed to libxml's raw functions) */
2021
2022 xmlNodeDumpOutput(xml_buffer, doc, data, 0,
2023 (options & xml_log_option_formatted), NULL);
2024 /* attempt adding final NL - failing shouldn't be fatal here */
2025 (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
2026 if (xml_buffer->buffer != NULL) {
2027 g_string_append(buffer,
2028 (const gchar *) xmlBufContent(xml_buffer->buffer));
2029 }
2030
2031#if (PCMK__XMLDUMP_STATS - 0)
2032 next = time(NULL);
2033 if ((now + 1) < next) {
2034 crm_log_xml_trace(data, "Long time");
2035 crm_err("xmlNodeDumpOutput() -> %lld bytes took %ds",
2036 (long long) buffer->len, next - now);
2037 }
2038#endif
2039
2040 /* asserted allocation before so there should be something to remove */
2041 (void) xmlOutputBufferClose(xml_buffer);
2042 return;
2043 }
2044
2045 switch(data->type) {
2046 case XML_ELEMENT_NODE:
2047 /* Handle below */
2048 dump_xml_element(data, options, buffer, depth);
2049 break;
2050 case XML_TEXT_NODE:
2051 /* if option xml_log_option_text is enabled, then dump XML_TEXT_NODE */
2052 if (options & xml_log_option_text) {
2053 dump_xml_text(data, options, buffer, depth);
2054 }
2055 break;
2056 case XML_COMMENT_NODE:
2057 dump_xml_comment(data, options, buffer, depth);
2058 break;
2059 case XML_CDATA_SECTION_NODE:
2060 dump_xml_cdata(data, options, buffer, depth);
2061 break;
2062 default:
2063 crm_warn("Unhandled type: %d", data->type);
2064 break;
2065
2066 /*
2067 XML_ATTRIBUTE_NODE = 2
2068 XML_ENTITY_REF_NODE = 5
2069 XML_ENTITY_NODE = 6
2070 XML_PI_NODE = 7
2071 XML_DOCUMENT_NODE = 9
2072 XML_DOCUMENT_TYPE_NODE = 10
2073 XML_DOCUMENT_FRAG_NODE = 11
2074 XML_NOTATION_NODE = 12
2075 XML_HTML_DOCUMENT_NODE = 13
2076 XML_DTD_NODE = 14
2077 XML_ELEMENT_DECL = 15
2078 XML_ATTRIBUTE_DECL = 16
2079 XML_ENTITY_DECL = 17
2080 XML_NAMESPACE_DECL = 18
2081 XML_XINCLUDE_START = 19
2082 XML_XINCLUDE_END = 20
2083 XML_DOCB_DOCUMENT_NODE = 21
2084 */
2085 }
2086}
2087
2088char *
2089dump_xml_formatted_with_text(xmlNode * an_xml_node)
2090{
2091 char *buffer = NULL;
2092 GString *g_buffer = g_string_sized_new(1024);
2093
2094 pcmk__xml2text(an_xml_node,
2096 g_buffer, 0);
2097
2098 if (g_buffer != NULL) {
2099 buffer = strdup((const char *) g_buffer->str);
2100 g_string_free(g_buffer, TRUE);
2101 }
2102 return buffer;
2103}
2104
2105char *
2106dump_xml_formatted(xmlNode * an_xml_node)
2107{
2108 char *buffer = NULL;
2109 GString *g_buffer = g_string_sized_new(1024);
2110
2111 pcmk__xml2text(an_xml_node, xml_log_option_formatted, g_buffer, 0);
2112
2113 if (g_buffer != NULL) {
2114 buffer = strdup((const char *) g_buffer->str);
2115 g_string_free(g_buffer, TRUE);
2116 }
2117 return buffer;
2118}
2119
2120char *
2121dump_xml_unformatted(xmlNode * an_xml_node)
2122{
2123 char *buffer = NULL;
2124 GString *g_buffer = g_string_sized_new(1024);
2125
2126 pcmk__xml2text(an_xml_node, 0, g_buffer, 0);
2127
2128 if (g_buffer != NULL) {
2129 buffer = strdup((const char *) g_buffer->str);
2130 g_string_free(g_buffer, TRUE);
2131 }
2132 return buffer;
2133}
2134
2135gboolean
2136xml_has_children(const xmlNode * xml_root)
2137{
2138 if (xml_root != NULL && xml_root->children != NULL) {
2139 return TRUE;
2140 }
2141 return FALSE;
2142}
2143
2144void
2145xml_remove_prop(xmlNode * obj, const char *name)
2146{
2147 if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
2148 crm_trace("Cannot remove %s from %s", name, obj->name);
2149
2150 } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
2151 /* Leave in place (marked for removal) until after the diff is calculated */
2152 xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
2153 xml_node_private_t *nodepriv = attr->_private;
2154
2155 set_parent_flag(obj, pcmk__xf_dirty);
2157 } else {
2158 xmlUnsetProp(obj, (pcmkXmlStr) name);
2159 }
2160}
2161
2162void
2163save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
2164{
2165 char *f = NULL;
2166
2167 if (filename == NULL) {
2168 char *uuid = crm_generate_uuid();
2169
2170 f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
2171 filename = f;
2172 free(uuid);
2173 }
2174
2175 crm_info("Saving %s to %s", desc, filename);
2176 write_xml_file(xml, filename, FALSE);
2177 free(f);
2178}
2179
2187static void
2188set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
2189{
2190 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
2191 pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
2192 }
2193}
2194
2209static void
2210mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
2211 const char *old_value)
2212{
2213 xml_doc_private_t *docpriv = new_xml->doc->_private;
2214 xmlAttr *attr = NULL;
2215 xml_node_private_t *nodepriv;
2216
2217 // Prevent the dirty flag being set recursively upwards
2219
2220 // Restore the old value (and the tracking flag)
2221 attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2223
2224 // Reset flags (so the attribute doesn't appear as newly created)
2225 nodepriv = attr->_private;
2226 nodepriv->flags = 0;
2227
2228 // Check ACLs and mark restored value for later removal
2229 xml_remove_prop(new_xml, attr_name);
2230
2231 crm_trace("XML attribute %s=%s was removed from %s",
2232 attr_name, old_value, element);
2233}
2234
2235/*
2236 * \internal
2237 * \brief Check ACLs for a changed XML attribute
2238 */
2239static void
2240mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
2241 const char *old_value)
2242{
2243 char *vcopy = crm_element_value_copy(new_xml, attr_name);
2244
2245 crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
2246 attr_name, old_value, vcopy, element);
2247
2248 // Restore the original value
2249 xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2250
2251 // Change it back to the new value, to check ACLs
2252 crm_xml_add(new_xml, attr_name, vcopy);
2253 free(vcopy);
2254}
2255
2267static void
2268mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
2269 xmlAttr *new_attr, int p_old, int p_new)
2270{
2271 xml_node_private_t *nodepriv = new_attr->_private;
2272
2273 crm_trace("XML attribute %s moved from position %d to %d in %s",
2274 old_attr->name, p_old, p_new, element);
2275
2276 // Mark document, element, and all element's parents as changed
2277 mark_xml_node_dirty(new_xml);
2278
2279 // Mark attribute as changed
2281
2282 nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
2284}
2285
2293static void
2294xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2295{
2296 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
2297
2298 while (attr_iter != NULL) {
2299 xmlAttr *old_attr = attr_iter;
2300 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2301 const char *name = (const char *) attr_iter->name;
2302 const char *old_value = crm_element_value(old_xml, name);
2303
2304 attr_iter = attr_iter->next;
2305 if (new_attr == NULL) {
2306 mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
2307 old_value);
2308
2309 } else {
2310 xml_node_private_t *nodepriv = new_attr->_private;
2311 int new_pos = pcmk__xml_position((xmlNode*) new_attr,
2313 int old_pos = pcmk__xml_position((xmlNode*) old_attr,
2315 const char *new_value = crm_element_value(new_xml, name);
2316
2317 // This attribute isn't new
2319
2320 if (strcmp(new_value, old_value) != 0) {
2321 mark_attr_changed(new_xml, (const char *) old_xml->name, name,
2322 old_value);
2323
2324 } else if ((old_pos != new_pos)
2325 && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
2326 mark_attr_moved(new_xml, (const char *) old_xml->name,
2327 old_attr, new_attr, old_pos, new_pos);
2328 }
2329 }
2330 }
2331}
2332
2342static void
2343mark_created_attrs(xmlNode *new_xml)
2344{
2345 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
2346
2347 while (attr_iter != NULL) {
2348 xmlAttr *new_attr = attr_iter;
2349 xml_node_private_t *nodepriv = attr_iter->_private;
2350
2351 attr_iter = attr_iter->next;
2352 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
2353 const char *attr_name = (const char *) new_attr->name;
2354
2355 crm_trace("Created new attribute %s=%s in %s",
2356 attr_name, crm_element_value(new_xml, attr_name),
2357 new_xml->name);
2358
2359 /* Check ACLs (we can't use the remove-then-create trick because it
2360 * would modify the attribute position).
2361 */
2362 if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
2363 pcmk__mark_xml_attr_dirty(new_attr);
2364 } else {
2365 // Creation was not allowed, so remove the attribute
2366 xmlUnsetProp(new_xml, new_attr->name);
2367 }
2368 }
2369 }
2370}
2371
2379static void
2380xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2381{
2382 set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
2383 xml_diff_old_attrs(old_xml, new_xml);
2384 mark_created_attrs(new_xml);
2385}
2386
2399static void
2400mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2401{
2402 // Re-create the child element so we can check ACLs
2403 xmlNode *candidate = add_node_copy(new_parent, old_child);
2404
2405 // Clear flags on new child and its children
2406 reset_xml_node_flags(candidate);
2407
2408 // Check whether ACLs allow the deletion
2409 pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
2410
2411 // Remove the child again (which will track it in document's deleted_objs)
2412 free_xml_with_position(candidate,
2413 pcmk__xml_position(old_child, pcmk__xf_skip));
2414
2415 if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
2416 pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
2418 }
2419}
2420
2421static void
2422mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2423 int p_old, int p_new)
2424{
2425 xml_node_private_t *nodepriv = new_child->_private;
2426
2427 crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
2428 new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
2429 p_old, p_new, new_parent->name);
2430 mark_xml_node_dirty(new_parent);
2432
2433 if (p_old > p_new) {
2434 nodepriv = old_child->_private;
2435 } else {
2436 nodepriv = new_child->_private;
2437 }
2439}
2440
2441// Given original and new XML, mark new XML portions that have changed
2442static void
2443mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
2444{
2445 xmlNode *cIter = NULL;
2446 xml_node_private_t *nodepriv = NULL;
2447
2448 CRM_CHECK(new_xml != NULL, return);
2449 if (old_xml == NULL) {
2450 pcmk__mark_xml_created(new_xml);
2451 pcmk__apply_creation_acl(new_xml, check_top);
2452 return;
2453 }
2454
2455 nodepriv = new_xml->_private;
2456 CRM_CHECK(nodepriv != NULL, return);
2457
2458 if(nodepriv->flags & pcmk__xf_processed) {
2459 /* Avoid re-comparing nodes */
2460 return;
2461 }
2463
2464 xml_diff_attrs(old_xml, new_xml);
2465
2466 // Check for differences in the original children
2467 for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2468 xmlNode *old_child = cIter;
2469 xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true);
2470
2471 cIter = pcmk__xml_next(cIter);
2472 if(new_child) {
2473 mark_xml_changes(old_child, new_child, TRUE);
2474
2475 } else {
2476 mark_child_deleted(old_child, new_xml);
2477 }
2478 }
2479
2480 // Check for moved or created children
2481 for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2482 xmlNode *new_child = cIter;
2483 xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true);
2484
2485 cIter = pcmk__xml_next(cIter);
2486 if(old_child == NULL) {
2487 // This is a newly created child
2488 nodepriv = new_child->_private;
2490 mark_xml_changes(old_child, new_child, TRUE);
2491
2492 } else {
2493 /* Check for movement, we already checked for differences */
2494 int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
2495 int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
2496
2497 if(p_old != p_new) {
2498 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2499 }
2500 }
2501 }
2502}
2503
2504void
2505xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
2506{
2508 xml_calculate_changes(old_xml, new_xml);
2509}
2510
2511void
2512xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
2513{
2514 CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
2515 return);
2516 CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
2517
2518 if(xml_tracking_changes(new_xml) == FALSE) {
2519 xml_track_changes(new_xml, NULL, NULL, FALSE);
2520 }
2521
2522 mark_xml_changes(old_xml, new_xml, FALSE);
2523}
2524
2525gboolean
2526can_prune_leaf(xmlNode * xml_node)
2527{
2528 xmlNode *cIter = NULL;
2529 gboolean can_prune = TRUE;
2530 const char *name = crm_element_name(xml_node);
2531
2534 return FALSE;
2535 }
2536
2537 for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2538 const char *p_name = (const char *) a->name;
2539
2540 if (strcmp(p_name, XML_ATTR_ID) == 0) {
2541 continue;
2542 }
2543 can_prune = FALSE;
2544 }
2545
2546 cIter = pcmk__xml_first_child(xml_node);
2547 while (cIter) {
2548 xmlNode *child = cIter;
2549
2550 cIter = pcmk__xml_next(cIter);
2551 if (can_prune_leaf(child)) {
2552 free_xml(child);
2553 } else {
2554 can_prune = FALSE;
2555 }
2556 }
2557 return can_prune;
2558}
2559
2568xmlNode *
2569pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
2570{
2571 xmlNode *a_child = NULL;
2572 int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
2573
2574 CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
2575
2576 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2577 a_child = pcmk__xml_next(a_child)) {
2578 if (exact) {
2579 int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
2580 xml_node_private_t *nodepriv = a_child->_private;
2581
2582 if (offset < search_offset) {
2583 continue;
2584
2585 } else if (offset > search_offset) {
2586 return NULL;
2587 }
2588
2589 if (pcmk_is_set(nodepriv->flags, pcmk__xf_skip)) {
2590 continue;
2591 }
2592 }
2593
2594 if (a_child->type == XML_COMMENT_NODE
2595 && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
2596 return a_child;
2597
2598 } else if (exact) {
2599 return NULL;
2600 }
2601 }
2602
2603 return NULL;
2604}
2605
2617void
2618pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
2619{
2620 CRM_CHECK(update != NULL, return);
2621 CRM_CHECK(update->type == XML_COMMENT_NODE, return);
2622
2623 if (target == NULL) {
2624 target = pcmk__xc_match(parent, update, false);
2625 }
2626
2627 if (target == NULL) {
2628 add_node_copy(parent, update);
2629
2630 } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
2631 xmlFree(target->content);
2632 target->content = xmlStrdup(update->content);
2633 }
2634}
2635
2648void
2649pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
2650 bool as_diff)
2651{
2652 xmlNode *a_child = NULL;
2653 const char *object_name = NULL,
2654 *object_href = NULL,
2655 *object_href_val = NULL;
2656
2657#if XML_PARSER_DEBUG
2658 crm_log_xml_trace(update, "update:");
2659 crm_log_xml_trace(target, "target:");
2660#endif
2661
2662 CRM_CHECK(update != NULL, return);
2663
2664 if (update->type == XML_COMMENT_NODE) {
2665 pcmk__xc_update(parent, target, update);
2666 return;
2667 }
2668
2669 object_name = crm_element_name(update);
2670 object_href_val = ID(update);
2671 if (object_href_val != NULL) {
2672 object_href = XML_ATTR_ID;
2673 } else {
2674 object_href_val = crm_element_value(update, XML_ATTR_IDREF);
2675 object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
2676 }
2677
2678 CRM_CHECK(object_name != NULL, return);
2679 CRM_CHECK(target != NULL || parent != NULL, return);
2680
2681 if (target == NULL) {
2682 target = pcmk__xe_match(parent, object_name,
2683 object_href, object_href_val);
2684 }
2685
2686 if (target == NULL) {
2687 target = create_xml_node(parent, object_name);
2688 CRM_CHECK(target != NULL, return);
2689#if XML_PARSER_DEBUG
2690 crm_trace("Added <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2691 object_href ? " " : "",
2692 object_href ? object_href : "",
2693 object_href ? "=" : "",
2694 object_href ? object_href_val : "");
2695
2696 } else {
2697 crm_trace("Found node <%s%s%s%s%s/> to update",
2698 pcmk__s(object_name, "<null>"),
2699 object_href ? " " : "",
2700 object_href ? object_href : "",
2701 object_href ? "=" : "",
2702 object_href ? object_href_val : "");
2703#endif
2704 }
2705
2706 CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2708 return);
2709
2710 if (as_diff == FALSE) {
2711 /* So that expand_plus_plus() gets called */
2712 copy_in_properties(target, update);
2713
2714 } else {
2715 /* No need for expand_plus_plus(), just raw speed */
2716 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2717 a = a->next) {
2718 const char *p_value = pcmk__xml_attr_value(a);
2719
2720 /* Remove it first so the ordering of the update is preserved */
2721 xmlUnsetProp(target, a->name);
2722 xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
2723 }
2724 }
2725
2726 for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2727 a_child = pcmk__xml_next(a_child)) {
2728#if XML_PARSER_DEBUG
2729 crm_trace("Updating child <%s%s%s%s%s/>",
2730 pcmk__s(object_name, "<null>"),
2731 object_href ? " " : "",
2732 object_href ? object_href : "",
2733 object_href ? "=" : "",
2734 object_href ? object_href_val : "");
2735#endif
2736 pcmk__xml_update(target, NULL, a_child, as_diff);
2737 }
2738
2739#if XML_PARSER_DEBUG
2740 crm_trace("Finished with <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2741 object_href ? " " : "",
2742 object_href ? object_href : "",
2743 object_href ? "=" : "",
2744 object_href ? object_href_val : "");
2745#endif
2746}
2747
2748gboolean
2749update_xml_child(xmlNode * child, xmlNode * to_update)
2750{
2751 gboolean can_update = TRUE;
2752 xmlNode *child_of_child = NULL;
2753
2754 CRM_CHECK(child != NULL, return FALSE);
2755 CRM_CHECK(to_update != NULL, return FALSE);
2756
2757 if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
2758 can_update = FALSE;
2759
2760 } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
2761 can_update = FALSE;
2762
2763 } else if (can_update) {
2764#if XML_PARSER_DEBUG
2765 crm_log_xml_trace(child, "Update match found...");
2766#endif
2767 pcmk__xml_update(NULL, child, to_update, false);
2768 }
2769
2770 for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2771 child_of_child = pcmk__xml_next(child_of_child)) {
2772 /* only update the first one */
2773 if (can_update) {
2774 break;
2775 }
2776 can_update = update_xml_child(child_of_child, to_update);
2777 }
2778
2779 return can_update;
2780}
2781
2782int
2783find_xml_children(xmlNode ** children, xmlNode * root,
2784 const char *tag, const char *field, const char *value, gboolean search_matches)
2785{
2786 int match_found = 0;
2787
2788 CRM_CHECK(root != NULL, return FALSE);
2789 CRM_CHECK(children != NULL, return FALSE);
2790
2791 if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
2792
2793 } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
2794
2795 } else {
2796 if (*children == NULL) {
2797 *children = create_xml_node(NULL, __func__);
2798 }
2799 add_node_copy(*children, root);
2800 match_found = 1;
2801 }
2802
2803 if (search_matches || match_found == 0) {
2804 xmlNode *child = NULL;
2805
2806 for (child = pcmk__xml_first_child(root); child != NULL;
2807 child = pcmk__xml_next(child)) {
2808 match_found += find_xml_children(children, child, tag, field, value, search_matches);
2809 }
2810 }
2811
2812 return match_found;
2813}
2814
2815gboolean
2816replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2817{
2818 gboolean can_delete = FALSE;
2819 xmlNode *child_of_child = NULL;
2820
2821 const char *up_id = NULL;
2822 const char *child_id = NULL;
2823 const char *right_val = NULL;
2824
2825 CRM_CHECK(child != NULL, return FALSE);
2826 CRM_CHECK(update != NULL, return FALSE);
2827
2828 up_id = ID(update);
2829 child_id = ID(child);
2830
2831 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2832 can_delete = TRUE;
2833 }
2834 if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
2835 can_delete = FALSE;
2836 }
2837 if (can_delete && delete_only) {
2838 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2839 a = a->next) {
2840 const char *p_name = (const char *) a->name;
2841 const char *p_value = pcmk__xml_attr_value(a);
2842
2843 right_val = crm_element_value(child, p_name);
2844 if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) {
2845 can_delete = FALSE;
2846 }
2847 }
2848 }
2849
2850 if (can_delete && parent != NULL) {
2851 crm_log_xml_trace(child, "Delete match found...");
2852 if (delete_only || update == NULL) {
2853 free_xml(child);
2854
2855 } else {
2856 xmlNode *tmp = copy_xml(update);
2857 xmlDoc *doc = tmp->doc;
2858 xmlNode *old = NULL;
2859
2860 xml_accept_changes(tmp);
2861 old = xmlReplaceNode(child, tmp);
2862
2863 if(xml_tracking_changes(tmp)) {
2864 /* Replaced sections may have included relevant ACLs */
2865 pcmk__apply_acl(tmp);
2866 }
2867
2868 xml_calculate_changes(old, tmp);
2869 xmlDocSetRootElement(doc, old);
2870 free_xml(old);
2871 }
2872 child = NULL;
2873 return TRUE;
2874
2875 } else if (can_delete) {
2876 crm_log_xml_debug(child, "Cannot delete the search root");
2877 can_delete = FALSE;
2878 }
2879
2880 child_of_child = pcmk__xml_first_child(child);
2881 while (child_of_child) {
2882 xmlNode *next = pcmk__xml_next(child_of_child);
2883
2884 can_delete = replace_xml_child(child, child_of_child, update, delete_only);
2885
2886 /* only delete the first one */
2887 if (can_delete) {
2888 child_of_child = NULL;
2889 } else {
2890 child_of_child = next;
2891 }
2892 }
2893
2894 return can_delete;
2895}
2896
2897xmlNode *
2898sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2899{
2900 xmlNode *child = NULL;
2901 GSList *nvpairs = NULL;
2902 xmlNode *result = NULL;
2903 const char *name = NULL;
2904
2905 CRM_CHECK(input != NULL, return NULL);
2906
2907 name = crm_element_name(input);
2908 CRM_CHECK(name != NULL, return NULL);
2909
2911 nvpairs = pcmk_xml_attrs2nvpairs(input);
2912 nvpairs = pcmk_sort_nvpairs(nvpairs);
2914 pcmk_free_nvpairs(nvpairs);
2915
2916 for (child = pcmk__xml_first_child(input); child != NULL;
2917 child = pcmk__xml_next(child)) {
2918
2919 if (recursive) {
2920 sorted_xml(child, result, recursive);
2921 } else {
2922 add_node_copy(result, child);
2923 }
2924 }
2925
2926 return result;
2927}
2928
2929xmlNode *
2930first_named_child(const xmlNode *parent, const char *name)
2931{
2932 xmlNode *match = NULL;
2933
2934 for (match = pcmk__xe_first_child(parent); match != NULL;
2935 match = pcmk__xe_next(match)) {
2936 /*
2937 * name == NULL gives first child regardless of name; this is
2938 * semantically incorrect in this function, but may be necessary
2939 * due to prior use of xml_child_iter_filter
2940 */
2941 if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) {
2942 return match;
2943 }
2944 }
2945 return NULL;
2946}
2947
2955xmlNode *
2956crm_next_same_xml(const xmlNode *sibling)
2957{
2958 xmlNode *match = pcmk__xe_next(sibling);
2959 const char *name = crm_element_name(sibling);
2960
2961 while (match != NULL) {
2962 if (!strcmp(crm_element_name(match), name)) {
2963 return match;
2964 }
2965 match = pcmk__xe_next(match);
2966 }
2967 return NULL;
2968}
2969
2970void
2972{
2973 static bool init = true;
2974
2975 if(init) {
2976 init = false;
2977 /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
2978 * pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
2979 * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
2980 * less than 1 second.
2981 */
2982 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2983
2984 /* Populate and free the _private field when nodes are created and destroyed */
2985 xmlDeregisterNodeDefault(free_private_data);
2986 xmlRegisterNodeDefault(new_private_data);
2987
2989 }
2990}
2991
2992void
2994{
2996 xmlCleanupParser();
2997}
2998
2999#define XPATH_MAX 512
3000
3001xmlNode *
3002expand_idref(xmlNode * input, xmlNode * top)
3003{
3004 const char *tag = NULL;
3005 const char *ref = NULL;
3006 xmlNode *result = input;
3007
3008 if (result == NULL) {
3009 return NULL;
3010
3011 } else if (top == NULL) {
3012 top = input;
3013 }
3014
3015 tag = crm_element_name(result);
3017
3018 if (ref != NULL) {
3019 char *xpath_string = crm_strdup_printf("//%s[@id='%s']", tag, ref);
3020
3021 result = get_xpath_object(xpath_string, top, LOG_ERR);
3022 if (result == NULL) {
3023 char *nodePath = (char *)xmlGetNodePath(top);
3024
3025 crm_err("No match for %s found in %s: Invalid configuration",
3026 xpath_string, pcmk__s(nodePath, "unrecognizable path"));
3027 free(nodePath);
3028 }
3029 free(xpath_string);
3030 }
3031 return result;
3032}
3033
3034char *
3036{
3037 static const char *base = NULL;
3038 char *ret = NULL;
3039
3040 if (base == NULL) {
3041 base = getenv("PCMK_schema_directory");
3042 }
3043 if (pcmk__str_empty(base)) {
3044 base = CRM_SCHEMA_DIRECTORY;
3045 }
3046
3047 switch (ns) {
3050 ret = strdup(base);
3051 break;
3054 ret = crm_strdup_printf("%s/base", base);
3055 break;
3056 default:
3057 crm_err("XML artefact family specified as %u not recognized", ns);
3058 }
3059 return ret;
3060}
3061
3062char *
3064{
3065 char *base = pcmk__xml_artefact_root(ns), *ret = NULL;
3066
3067 switch (ns) {
3070 ret = crm_strdup_printf("%s/%s.rng", base, filespec);
3071 break;
3074 ret = crm_strdup_printf("%s/%s.xsl", base, filespec);
3075 break;
3076 default:
3077 crm_err("XML artefact family specified as %u not recognized", ns);
3078 }
3079 free(base);
3080
3081 return ret;
3082}
3083
3084void
3085pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
3086{
3087 while (true) {
3088 const char *name, *value;
3089
3090 name = va_arg(pairs, const char *);
3091 if (name == NULL) {
3092 return;
3093 }
3094
3095 value = va_arg(pairs, const char *);
3096 if (value != NULL) {
3097 crm_xml_add(node, name, value);
3098 }
3099 }
3100}
3101
3102void
3103pcmk__xe_set_props(xmlNodePtr node, ...)
3104{
3105 va_list pairs;
3106 va_start(pairs, node);
3107 pcmk__xe_set_propv(node, pairs);
3108 va_end(pairs);
3109}
3110
3111int
3112pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
3113 int (*handler)(xmlNode *xml, void *userdata),
3114 void *userdata)
3115{
3116 xmlNode *children = (xml? xml->children : NULL);
3117
3118 CRM_ASSERT(handler != NULL);
3119
3120 for (xmlNode *node = children; node != NULL; node = node->next) {
3121 if (node->type == XML_ELEMENT_NODE &&
3122 pcmk__str_eq(child_element_name, (const char *) node->name, pcmk__str_null_matches)) {
3123 int rc = handler(node, userdata);
3124
3125 if (rc != pcmk_rc_ok) {
3126 return rc;
3127 }
3128 }
3129 }
3130
3131 return pcmk_rc_ok;
3132}
3133
3134// Deprecated functions kept only for backward API compatibility
3135// LCOV_EXCL_START
3136
3137#include <crm/common/xml_compat.h>
3138
3139xmlNode *
3140find_entity(xmlNode *parent, const char *node_name, const char *id)
3141{
3142 return pcmk__xe_match(parent, node_name,
3143 ((id == NULL)? id : XML_ATTR_ID), id);
3144}
3145
3146void
3148{
3149 free_xml(data);
3150}
3151
3152// LCOV_EXCL_STOP
3153// End deprecated API
void pcmk__free_acls(GList *acls)
Definition: acl.c:44
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:571
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:224
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition: acl.c:613
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:655
const char * parent
Definition: cib.c:25
const char * name
Definition: cib.c:24
void crm_schema_cleanup(void)
Definition: schemas.c:554
void crm_schema_init(void)
Definition: schemas.c:377
uint64_t flags
Definition: remote.c:3
char * crm_generate_uuid(void)
Definition: utils.c:509
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:397
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:121
#define CRM_SCHEMA_DIRECTORY
Definition: config.h:45
char data[0]
Definition: cpg.c:10
A dumping ground.
#define INFINITY
Definition: crm.h:99
#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:237
const char * pcmk__get_tmpdir(void)
Definition: io.c:541
const char * pcmk__epoch2str(const time_t *when)
Definition: iso8601.c:1730
#define crm_info(fmt, args...)
Definition: logging.h:362
#define crm_warn(fmt, args...)
Definition: logging.h:360
#define CRM_XS
Definition: logging.h:55
#define crm_log_xml_debug(xml, text)
Definition: logging.h:372
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:211
unsigned int crm_trace_nonlog
Definition: logging.c:46
#define crm_log_xml_err(xml, text)
Definition: logging.h:368
@ 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:310
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_err(fmt, args...)
Definition: logging.h:359
#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:282
#define crm_log_xml_trace(xml, text)
Definition: logging.h:373
#define crm_log_xml_warn(xml, text)
Definition: logging.h:369
#define crm_trace(fmt, args...)
Definition: logging.h:365
#define LOG_TRACE
Definition: logging.h:37
#define pcmk__log_else(level, else_action)
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:216
#define ID(x)
Definition: msg_xml.h:468
#define XML_ACL_TAG_ROLE_REF
Definition: msg_xml.h:427
#define XML_CIB_TAG_OBJ_REF
Definition: msg_xml.h:445
#define XML_ATTR_ID
Definition: msg_xml.h:134
#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:131
#define XML_ACL_TAG_ROLE_REFv1
Definition: msg_xml.h:428
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
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:419
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:714
GSList * pcmk_xml_attrs2nvpairs(const xmlNode *xml)
Create a list of name/value pairs from an XML node's attributes.
Definition: nvpair.c:161
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:35
const char * target
Definition: pcmk_fence.c:29
const char * bz2_strerror(int rc)
Definition: results.c:823
#define CRM_ASSERT(expr)
Definition: results.h:42
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_error
Definition: results.h:144
int pcmk_rc2legacy(int rc)
Definition: results.c:521
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:928
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1214
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:2749
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:2136
bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:49
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:2816
void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
Definition: xml.c:2649
void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml.c:2618
#define attr_matches(c, n, v)
Definition: xml.c:503
xmlNode * filename2xml(const char *filename)
Definition: xml.c:1106
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:741
void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml.c:163
void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
Definition: xml.c:108
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, const xmlNode *data, int depth, int options)
Definition: xml.c:1743
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition: xml.c:727
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:3002
xmlNode * pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
Definition: xml.c:2569
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:774
xmlNode * pcmk__xe_match(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:520
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
Definition: xml.c:2089
void fix_plus_plus_recursive(xmlNode *target)
Parse integer assignment statements on this node and all its child nodes.
Definition: xml.c:578
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:682
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
Definition: xml.c:1333
void xml_accept_changes(xmlNode *xml)
Definition: xml.c:445
char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
Definition: xml.c:3035
xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
Definition: xml.c:394
void pcmk__mark_xml_created(xmlNode *xml)
Definition: xml.c:145
void crm_xml_init(void)
Initialize the CRM XML subsystem.
Definition: xml.c:2971
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml.c:1176
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml.c:3112
xmlNode * copy_xml(xmlNode *src)
Definition: xml.c:891
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Set the ID of an XML element using a format.
Definition: xml.c:1212
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2956
int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
Definition: xml.c:340
void pcmk__strip_xml_text(xmlNode *xml)
Definition: xml.c:1078
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2512
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml.c:3085
#define PCMK__XML_PARSE_OPTS
Definition: xml.c:46
char * crm_xml_escape(const char *text)
Replace special characters with their XML escape sequences.
Definition: xml.c:1406
xmlNode * string2xml(const char *input)
Definition: xml.c:930
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:711
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:316
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml.c:3103
xmlNode * stdin2xml(void)
Definition: xml.c:991
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1361
void crm_xml_cleanup(void)
Definition: xml.c:2993
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:323
xmlNode * pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id, const char *class_name, const char *text)
Definition: xml.c:786
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition: xml.c:301
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:2505
void free_xml(xmlNode *child)
Definition: xml.c:885
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:1190
void pcmk__xml2text(xmlNodePtr data, int options, GString *buffer, int depth)
Definition: xml.c:1982
void crm_destroy_xml(gpointer data)
Definition: xml.c:3147
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:3140
#define XML_NODE_PRIVATE_MAGIC
Definition: xml.c:174
void xml_log_changes(uint8_t log_level, const char *function, xmlNode *xml)
Definition: xml.c:410
void copy_in_properties(xmlNode *target, xmlNode *src)
Definition: xml.c:545
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
Definition: xml.c:470
char * dump_xml_formatted(xmlNode *an_xml_node)
Definition: xml.c:2106
#define XML_DOC_PRIVATE_MAGIC
Definition: xml.c:173
gboolean can_prune_leaf(xmlNode *xml_node)
Definition: xml.c:2526
void pcmk_free_xml_subtree(xmlNode *xml)
Definition: xml.c:808
void pcmk__xml_log(int log_level, const char *file, const char *function, int line, const char *prefix, const xmlNode *data, int depth, int options)
Definition: xml.c:1623
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2145
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Update current XML attribute value per parsed integer assignment statement.
Definition: xml.c:612
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:2163
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2898
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
Definition: xml.c:3063
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:2783
char * dump_xml_unformatted(xmlNode *an_xml_node)
Definition: xml.c:2121
Wrappers for and extensions to libxml2.
const xmlChar * pcmkXmlStr
Definition: xml.h:50
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:320
@ pcmk__xf_deleted
Definition: xml_internal.h:323
@ pcmk__xf_acl_enabled
Definition: xml_internal.h:332
@ pcmk__xf_created
Definition: xml_internal.h:324
@ pcmk__xf_dirty
Definition: xml_internal.h:322
@ pcmk__xf_modified
Definition: xml_internal.h:325
@ pcmk__xf_skip
Definition: xml_internal.h:329
@ pcmk__xf_acl_write
Definition: xml_internal.h:334
@ pcmk__xf_lazy
Definition: xml_internal.h:339
@ pcmk__xf_tracking
Definition: xml_internal.h:327
@ pcmk__xf_none
Definition: xml_internal.h:321
@ pcmk__xf_processed
Definition: xml_internal.h:328
@ pcmk__xf_moved
Definition: xml_internal.h:330
GString * pcmk__element_xpath(const xmlNode *xml)
Definition: xpath.c:281
#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