This source file includes following definitions.
- pcmk__tracking_xml_changes
- set_parent_flag
- pcmk__set_xml_doc_flag
- mark_xml_node_dirty
- reset_xml_node_flags
- pcmk__mark_xml_created
- pcmk__mark_xml_attr_dirty
- free_deleted_object
- reset_xml_private_data
- free_private_data
- new_private_data
- xml_track_changes
- xml_tracking_changes
- xml_document_dirty
- pcmk__xml_position
- marked_as_deleted
- accept_attr_deletions
- pcmk__xml_match
- xml_accept_changes
- find_xml_node
- pcmk__xe_match
- copy_in_properties
- fix_plus_plus_recursive
- expand_plus_plus
- pcmk__xe_remove_matching_attrs
- getDocPtr
- add_node_copy
- create_xml_node
- pcmk_create_xml_text_node
- pcmk_create_html_node
- pcmk_free_xml_subtree
- free_xml_with_position
- free_xml
- copy_xml
- string2xml
- stdin2xml
- decompress_file
- pcmk__strip_xml_text
- filename2xml
- pcmk__xe_add_last_written
- crm_xml_sanitize_id
- crm_xml_set_id
- write_xml_stream
- write_xml_fd
- write_xml_file
- replace_text
- crm_xml_escape
- dump_xml_attr
- dump_xml_element
- dump_xml_text
- dump_xml_cdata
- dump_xml_comment
- pcmk__xml2text
- dump_xml_formatted_with_text
- dump_xml_formatted
- dump_xml_unformatted
- xml_has_children
- xml_remove_prop
- save_xml_to_file
- set_attrs_flag
- mark_attr_deleted
- mark_attr_changed
- mark_attr_moved
- xml_diff_old_attrs
- mark_created_attrs
- xml_diff_attrs
- mark_child_deleted
- mark_child_moved
- mark_xml_changes
- xml_calculate_significant_changes
- xml_calculate_changes
- can_prune_leaf
- pcmk__xc_match
- pcmk__xc_update
- pcmk__xml_update
- update_xml_child
- find_xml_children
- replace_xml_child
- sorted_xml
- first_named_child
- crm_next_same_xml
- crm_xml_init
- crm_xml_cleanup
- expand_idref
- pcmk__xml_artefact_root
- pcmk__xml_artefact_path
- pcmk__xe_set_propv
- pcmk__xe_set_props
- pcmk__xe_foreach_child
- find_entity
- crm_destroy_xml
- add_node_nocopy
1
2
3
4
5
6
7
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>
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>
29 #include "crmcommon_private.h"
30
31
32 #ifndef XML_PARSER_DEBUG
33 #define XML_PARSER_DEBUG 0
34 #endif
35
36
37
38
39
40
41
42
43
44
45 #define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER)
46
47 bool
48 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
49 {
50 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
51 return FALSE;
52 } else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
53 pcmk__xf_tracking)) {
54 return FALSE;
55 } else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
56 pcmk__xf_lazy)) {
57 return FALSE;
58 }
59 return TRUE;
60 }
61
62 static inline void
63 set_parent_flag(xmlNode *xml, long flag)
64 {
65 for(; xml; xml = xml->parent) {
66 xml_node_private_t *nodepriv = xml->_private;
67
68 if (nodepriv == NULL) {
69
70 } else {
71 pcmk__set_xml_flags(nodepriv, flag);
72 }
73 }
74 }
75
76 void
77 pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
78 {
79 if(xml && xml->doc && xml->doc->_private){
80
81 xml_doc_private_t *docpriv = xml->doc->_private;
82
83 pcmk__set_xml_flags(docpriv, flag);
84 }
85 }
86
87
88 static inline void
89 mark_xml_node_dirty(xmlNode *xml)
90 {
91 pcmk__set_xml_doc_flag(xml, pcmk__xf_dirty);
92 set_parent_flag(xml, pcmk__xf_dirty);
93 }
94
95
96 static void
97 reset_xml_node_flags(xmlNode *xml)
98 {
99 xmlNode *cIter = NULL;
100 xml_node_private_t *nodepriv = xml->_private;
101
102 if (nodepriv) {
103 nodepriv->flags = 0;
104 }
105
106 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
107 cIter = pcmk__xml_next(cIter)) {
108 reset_xml_node_flags(cIter);
109 }
110 }
111
112
113 void
114 pcmk__mark_xml_created(xmlNode *xml)
115 {
116 xmlNode *cIter = NULL;
117 xml_node_private_t *nodepriv = xml->_private;
118
119 if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) {
120 if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
121 pcmk__set_xml_flags(nodepriv, pcmk__xf_created);
122 mark_xml_node_dirty(xml);
123 }
124 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
125 cIter = pcmk__xml_next(cIter)) {
126 pcmk__mark_xml_created(cIter);
127 }
128 }
129 }
130
131 void
132 pcmk__mark_xml_attr_dirty(xmlAttr *a)
133 {
134 xmlNode *parent = a->parent;
135 xml_node_private_t *nodepriv = a->_private;
136
137 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_modified);
138 pcmk__clear_xml_flags(nodepriv, pcmk__xf_deleted);
139 mark_xml_node_dirty(parent);
140 }
141
142 #define XML_DOC_PRIVATE_MAGIC 0x81726354UL
143 #define XML_NODE_PRIVATE_MAGIC 0x54637281UL
144
145
146 static void
147 free_deleted_object(void *data)
148 {
149 if(data) {
150 pcmk__deleted_xml_t *deleted_obj = data;
151
152 free(deleted_obj->path);
153 free(deleted_obj);
154 }
155 }
156
157
158 static void
159 reset_xml_private_data(xml_doc_private_t *docpriv)
160 {
161 if (docpriv != NULL) {
162 CRM_ASSERT(docpriv->check == XML_DOC_PRIVATE_MAGIC);
163
164 free(docpriv->user);
165 docpriv->user = NULL;
166
167 if (docpriv->acls != NULL) {
168 pcmk__free_acls(docpriv->acls);
169 docpriv->acls = NULL;
170 }
171
172 if(docpriv->deleted_objs) {
173 g_list_free_full(docpriv->deleted_objs, free_deleted_object);
174 docpriv->deleted_objs = NULL;
175 }
176 }
177 }
178
179
180 static void
181 free_private_data(xmlNode *node)
182 {
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 if (node->name == NULL || node->name[0] != ' ') {
210 if (node->_private) {
211 if (node->type == XML_DOCUMENT_NODE) {
212 reset_xml_private_data(node->_private);
213 } else {
214 CRM_ASSERT(((xml_node_private_t *) node->_private)->check
215 == XML_NODE_PRIVATE_MAGIC);
216
217 }
218 free(node->_private);
219 node->_private = NULL;
220 }
221 }
222 }
223
224
225 static void
226 new_private_data(xmlNode *node)
227 {
228 switch (node->type) {
229 case XML_DOCUMENT_NODE: {
230 xml_doc_private_t *docpriv = NULL;
231 docpriv = calloc(1, sizeof(xml_doc_private_t));
232 CRM_ASSERT(docpriv != NULL);
233 docpriv->check = XML_DOC_PRIVATE_MAGIC;
234
235 pcmk__set_xml_flags(docpriv, pcmk__xf_dirty|pcmk__xf_created);
236 node->_private = docpriv;
237 break;
238 }
239 case XML_ELEMENT_NODE:
240 case XML_ATTRIBUTE_NODE:
241 case XML_COMMENT_NODE: {
242 xml_node_private_t *nodepriv = NULL;
243 nodepriv = calloc(1, sizeof(xml_node_private_t));
244 CRM_ASSERT(nodepriv != NULL);
245 nodepriv->check = XML_NODE_PRIVATE_MAGIC;
246
247 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created);
248 node->_private = nodepriv;
249 if (pcmk__tracking_xml_changes(node, FALSE)) {
250
251
252
253 mark_xml_node_dirty(node);
254 }
255 break;
256 }
257 case XML_TEXT_NODE:
258 case XML_DTD_NODE:
259 case XML_CDATA_SECTION_NODE:
260 break;
261 default:
262
263 crm_trace("Ignoring %p %d", node, node->type);
264 CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
265 break;
266 }
267 }
268
269 void
270 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
271 {
272 xml_accept_changes(xml);
273 crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
274 pcmk__set_xml_doc_flag(xml, pcmk__xf_tracking);
275 if(enforce_acls) {
276 if(acl_source == NULL) {
277 acl_source = xml;
278 }
279 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_enabled);
280 pcmk__unpack_acl(acl_source, xml, user);
281 pcmk__apply_acl(xml);
282 }
283 }
284
285 bool xml_tracking_changes(xmlNode * xml)
286 {
287 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
288 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
289 pcmk__xf_tracking);
290 }
291
292 bool xml_document_dirty(xmlNode *xml)
293 {
294 return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
295 && pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
296 pcmk__xf_dirty);
297 }
298
299
300
301
302
303
304
305
306
307
308 int
309 pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
310 {
311 int position = 0;
312
313 for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
314 xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
315
316 if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
317 position++;
318 }
319 }
320
321 return position;
322 }
323
324
325 static bool
326 marked_as_deleted(xmlAttrPtr a, void *user_data)
327 {
328 xml_node_private_t *nodepriv = a->_private;
329
330 if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
331 return true;
332 }
333 nodepriv->flags = pcmk__xf_none;
334 return false;
335 }
336
337
338 static void
339 accept_attr_deletions(xmlNode *xml)
340 {
341
342 ((xml_node_private_t *) xml->_private)->flags = pcmk__xf_none;
343
344
345 pcmk__xe_remove_matching_attrs(xml, marked_as_deleted, NULL);
346
347
348 for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
349 cIter = pcmk__xml_next(cIter)) {
350 accept_attr_deletions(cIter);
351 }
352 }
353
354
355
356
357
358
359
360
361
362 xmlNode *
363 pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
364 {
365 CRM_CHECK(needle != NULL, return NULL);
366
367 if (needle->type == XML_COMMENT_NODE) {
368 return pcmk__xc_match(haystack, needle, exact);
369
370 } else {
371 const char *id = ID(needle);
372 const char *attr = (id == NULL)? NULL : XML_ATTR_ID;
373
374 return pcmk__xe_match(haystack, crm_element_name(needle), attr, id);
375 }
376 }
377
378 void
379 xml_accept_changes(xmlNode * xml)
380 {
381 xmlNode *top = NULL;
382 xml_doc_private_t *docpriv = NULL;
383
384 if(xml == NULL) {
385 return;
386 }
387
388 crm_trace("Accepting changes to %p", xml);
389 docpriv = xml->doc->_private;
390 top = xmlDocGetRootElement(xml->doc);
391
392 reset_xml_private_data(xml->doc->_private);
393
394 if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
395 docpriv->flags = pcmk__xf_none;
396 return;
397 }
398
399 docpriv->flags = pcmk__xf_none;
400 accept_attr_deletions(top);
401 }
402
403 xmlNode *
404 find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
405 {
406 xmlNode *a_child = NULL;
407 const char *name = "NULL";
408
409 if (root != NULL) {
410 name = crm_element_name(root);
411 }
412
413 if (search_path == NULL) {
414 crm_warn("Will never find <NULL>");
415 return NULL;
416 }
417
418 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
419 a_child = pcmk__xml_next(a_child)) {
420 if (strcmp((const char *)a_child->name, search_path) == 0) {
421
422 return a_child;
423 }
424 }
425
426 if (must_find) {
427 crm_warn("Could not find %s in %s.", search_path, name);
428 } else if (root != NULL) {
429 crm_trace("Could not find %s in %s.", search_path, name);
430 } else {
431 crm_trace("Could not find %s in <NULL>.", search_path);
432 }
433
434 return NULL;
435 }
436
437 #define attr_matches(c, n, v) pcmk__str_eq(crm_element_value((c), (n)), \
438 (v), pcmk__str_none)
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453 xmlNode *
454 pcmk__xe_match(const xmlNode *parent, const char *node_name,
455 const char *attr_n, const char *attr_v)
456 {
457 CRM_CHECK(parent != NULL, return NULL);
458 CRM_CHECK(attr_v == NULL || attr_n != NULL, return NULL);
459
460 for (xmlNode *child = pcmk__xml_first_child(parent); child != NULL;
461 child = pcmk__xml_next(child)) {
462 if (pcmk__str_eq(node_name, (const char *) (child->name),
463 pcmk__str_null_matches)
464 && ((attr_n == NULL) ||
465 (attr_v == NULL && xmlHasProp(child, (pcmkXmlStr) attr_n)) ||
466 (attr_v != NULL && attr_matches(child, attr_n, attr_v)))) {
467 return child;
468 }
469 }
470 crm_trace("XML child node <%s%s%s%s%s> not found in %s",
471 (node_name? node_name : "(any)"),
472 (attr_n? " " : ""),
473 (attr_n? attr_n : ""),
474 (attr_n? "=" : ""),
475 (attr_n? attr_v : ""),
476 crm_element_name(parent));
477 return NULL;
478 }
479
480 void
481 copy_in_properties(xmlNode *target, const xmlNode *src)
482 {
483 if (src == NULL) {
484 crm_warn("No node to copy properties from");
485
486 } else if (target == NULL) {
487 crm_err("No node to copy properties into");
488
489 } else {
490 for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
491 const char *p_name = (const char *) a->name;
492 const char *p_value = pcmk__xml_attr_value(a);
493
494 expand_plus_plus(target, p_name, p_value);
495 if (xml_acl_denied(target)) {
496 crm_trace("Cannot copy %s=%s to %s", p_name, p_value, target->name);
497 return;
498 }
499 }
500 }
501
502 return;
503 }
504
505
506
507
508
509
510
511
512
513 void
514 fix_plus_plus_recursive(xmlNode *target)
515 {
516
517 xmlNode *child = NULL;
518
519 for (xmlAttrPtr a = pcmk__xe_first_attr(target); a != NULL; a = a->next) {
520 const char *p_name = (const char *) a->name;
521 const char *p_value = pcmk__xml_attr_value(a);
522
523 expand_plus_plus(target, p_name, p_value);
524 }
525 for (child = pcmk__xml_first_child(target); child != NULL;
526 child = pcmk__xml_next(child)) {
527 fix_plus_plus_recursive(child);
528 }
529 }
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547 void
548 expand_plus_plus(xmlNode * target, const char *name, const char *value)
549 {
550 int offset = 1;
551 int name_len = 0;
552 int int_value = 0;
553 int value_len = 0;
554
555 const char *old_value = NULL;
556
557 if (target == NULL || value == NULL || name == NULL) {
558 return;
559 }
560
561 old_value = crm_element_value(target, name);
562
563 if (old_value == NULL) {
564
565 goto set_unexpanded;
566
567 } else if (strstr(value, name) != value) {
568 goto set_unexpanded;
569 }
570
571 name_len = strlen(name);
572 value_len = strlen(value);
573 if (value_len < (name_len + 2)
574 || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
575 goto set_unexpanded;
576 }
577
578
579
580
581 if (old_value != value) {
582 int_value = char2score(old_value);
583 }
584
585 if (value[name_len + 1] != '+') {
586 const char *offset_s = value + (name_len + 2);
587
588 offset = char2score(offset_s);
589 }
590 int_value += offset;
591
592 if (int_value > INFINITY) {
593 int_value = (int)INFINITY;
594 }
595
596 crm_xml_add_int(target, name, int_value);
597 return;
598
599 set_unexpanded:
600 if (old_value == value) {
601
602 return;
603 }
604 crm_xml_add(target, name, value);
605 return;
606 }
607
608
609
610
611
612
613
614
615
616
617 void
618 pcmk__xe_remove_matching_attrs(xmlNode *element,
619 bool (*match)(xmlAttrPtr, void *),
620 void *user_data)
621 {
622 xmlAttrPtr next = NULL;
623
624 for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
625 next = a->next;
626 if ((match == NULL) || match(a, user_data)) {
627 if (!pcmk__check_acl(element, NULL, pcmk__xf_acl_write)) {
628 crm_trace("ACLs prevent removal of attributes (%s and "
629 "possibly others) from %s element",
630 (const char *) a->name, (const char *) element->name);
631 return;
632 }
633
634 if (pcmk__tracking_xml_changes(element, false)) {
635
636 set_parent_flag(element, pcmk__xf_dirty);
637 pcmk__set_xml_flags((xml_node_private_t *) a->_private,
638 pcmk__xf_deleted);
639 } else {
640 xmlRemoveProp(a);
641 }
642 }
643 }
644 }
645
646 xmlDoc *
647 getDocPtr(xmlNode * node)
648 {
649 xmlDoc *doc = NULL;
650
651 CRM_CHECK(node != NULL, return NULL);
652
653 doc = node->doc;
654 if (doc == NULL) {
655 doc = xmlNewDoc((pcmkXmlStr) "1.0");
656 xmlDocSetRootElement(doc, node);
657 xmlSetTreeDoc(node, doc);
658 }
659 return doc;
660 }
661
662 xmlNode *
663 add_node_copy(xmlNode * parent, xmlNode * src_node)
664 {
665 xmlNode *child = NULL;
666 xmlDoc *doc = getDocPtr(parent);
667
668 CRM_CHECK(src_node != NULL, return NULL);
669
670 child = xmlDocCopyNode(src_node, doc, 1);
671 xmlAddChild(parent, child);
672 pcmk__mark_xml_created(child);
673 return child;
674 }
675
676 xmlNode *
677 create_xml_node(xmlNode * parent, const char *name)
678 {
679 xmlDoc *doc = NULL;
680 xmlNode *node = NULL;
681
682 if (pcmk__str_empty(name)) {
683 CRM_CHECK(name != NULL && name[0] == 0, return NULL);
684 return NULL;
685 }
686
687 if (parent == NULL) {
688 doc = xmlNewDoc((pcmkXmlStr) "1.0");
689 node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
690 xmlDocSetRootElement(doc, node);
691
692 } else {
693 doc = getDocPtr(parent);
694 node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
695 xmlAddChild(parent, node);
696 }
697 pcmk__mark_xml_created(node);
698 return node;
699 }
700
701 xmlNode *
702 pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content)
703 {
704 xmlNode *node = create_xml_node(parent, name);
705
706 if (node != NULL) {
707 xmlNodeSetContent(node, (pcmkXmlStr) content);
708 }
709
710 return node;
711 }
712
713 xmlNode *
714 pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id,
715 const char *class_name, const char *text)
716 {
717 xmlNode *node = pcmk_create_xml_text_node(parent, element_name, text);
718
719 if (class_name != NULL) {
720 crm_xml_add(node, "class", class_name);
721 }
722
723 if (id != NULL) {
724 crm_xml_add(node, "id", id);
725 }
726
727 return node;
728 }
729
730
731
732
733
734
735 void
736 pcmk_free_xml_subtree(xmlNode *xml)
737 {
738 xmlUnlinkNode(xml);
739 xmlFreeNode(xml);
740 }
741
742 static void
743 free_xml_with_position(xmlNode * child, int position)
744 {
745 if (child != NULL) {
746 xmlNode *top = NULL;
747 xmlDoc *doc = child->doc;
748 xml_node_private_t *nodepriv = child->_private;
749 xml_doc_private_t *docpriv = NULL;
750
751 if (doc != NULL) {
752 top = xmlDocGetRootElement(doc);
753 }
754
755 if (doc != NULL && top == child) {
756
757 xmlFreeDoc(doc);
758
759 } else if (pcmk__check_acl(child, NULL, pcmk__xf_acl_write) == FALSE) {
760 GString *xpath = NULL;
761
762 pcmk__if_tracing({}, return);
763 xpath = pcmk__element_xpath(child);
764 qb_log_from_external_source(__func__, __FILE__,
765 "Cannot remove %s %x", LOG_TRACE,
766 __LINE__, 0, (const char *) xpath->str,
767 nodepriv->flags);
768 g_string_free(xpath, TRUE);
769 return;
770
771 } else {
772 if (doc && pcmk__tracking_xml_changes(child, FALSE)
773 && !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
774
775 GString *xpath = pcmk__element_xpath(child);
776
777 if (xpath != NULL) {
778 pcmk__deleted_xml_t *deleted_obj = NULL;
779
780 crm_trace("Deleting %s %p from %p",
781 (const char *) xpath->str, child, doc);
782
783 deleted_obj = calloc(1, sizeof(pcmk__deleted_xml_t));
784 deleted_obj->path = strdup((const char *) xpath->str);
785
786 CRM_ASSERT(deleted_obj->path != NULL);
787 g_string_free(xpath, TRUE);
788
789 deleted_obj->position = -1;
790
791 if (child->type == XML_COMMENT_NODE) {
792 if (position >= 0) {
793 deleted_obj->position = position;
794
795 } else {
796 deleted_obj->position = pcmk__xml_position(child,
797 pcmk__xf_skip);
798 }
799 }
800
801 docpriv = doc->_private;
802 docpriv->deleted_objs = g_list_append(docpriv->deleted_objs, deleted_obj);
803 pcmk__set_xml_doc_flag(child, pcmk__xf_dirty);
804 }
805 }
806 pcmk_free_xml_subtree(child);
807 }
808 }
809 }
810
811
812 void
813 free_xml(xmlNode * child)
814 {
815 free_xml_with_position(child, -1);
816 }
817
818 xmlNode *
819 copy_xml(xmlNode * src)
820 {
821 xmlDoc *doc = xmlNewDoc((pcmkXmlStr) "1.0");
822 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
823
824 CRM_ASSERT(copy != NULL);
825 xmlDocSetRootElement(doc, copy);
826 xmlSetTreeDoc(copy, doc);
827 return copy;
828 }
829
830 xmlNode *
831 string2xml(const char *input)
832 {
833 xmlNode *xml = NULL;
834 xmlDocPtr output = NULL;
835 xmlParserCtxtPtr ctxt = NULL;
836 xmlErrorPtr last_error = NULL;
837
838 if (input == NULL) {
839 crm_err("Can't parse NULL input");
840 return NULL;
841 }
842
843
844 ctxt = xmlNewParserCtxt();
845 CRM_CHECK(ctxt != NULL, return NULL);
846
847 xmlCtxtResetLastError(ctxt);
848 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
849 output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
850 PCMK__XML_PARSE_OPTS);
851 if (output) {
852 xml = xmlDocGetRootElement(output);
853 }
854 last_error = xmlCtxtGetLastError(ctxt);
855 if (last_error && last_error->code != XML_ERR_OK) {
856
857
858
859
860
861 crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
862 last_error->domain, last_error->level, last_error->code, last_error->message);
863
864 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
865 CRM_LOG_ASSERT("Cannot parse an empty string");
866
867 } else if (last_error->code != XML_ERR_DOCUMENT_END) {
868 crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
869 input);
870 if (xml != NULL) {
871 crm_log_xml_err(xml, "Partial");
872 }
873
874 } else {
875 int len = strlen(input);
876 int lpc = 0;
877
878 while(lpc < len) {
879 crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
880 lpc += 80;
881 }
882
883 CRM_LOG_ASSERT("String parsing error");
884 }
885 }
886
887 xmlFreeParserCtxt(ctxt);
888 return xml;
889 }
890
891 xmlNode *
892 stdin2xml(void)
893 {
894 size_t data_length = 0;
895 size_t read_chars = 0;
896
897 char *xml_buffer = NULL;
898 xmlNode *xml_obj = NULL;
899
900 do {
901 xml_buffer = pcmk__realloc(xml_buffer, data_length + PCMK__BUFFER_SIZE);
902 read_chars = fread(xml_buffer + data_length, 1, PCMK__BUFFER_SIZE,
903 stdin);
904 data_length += read_chars;
905 } while (read_chars == PCMK__BUFFER_SIZE);
906
907 if (data_length == 0) {
908 crm_warn("No XML supplied on stdin");
909 free(xml_buffer);
910 return NULL;
911 }
912
913 xml_buffer[data_length] = '\0';
914 xml_obj = string2xml(xml_buffer);
915 free(xml_buffer);
916
917 crm_log_xml_trace(xml_obj, "Created fragment");
918 return xml_obj;
919 }
920
921 static char *
922 decompress_file(const char *filename)
923 {
924 char *buffer = NULL;
925 int rc = 0;
926 size_t length = 0, read_len = 0;
927 BZFILE *bz_file = NULL;
928 FILE *input = fopen(filename, "r");
929
930 if (input == NULL) {
931 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
932 return NULL;
933 }
934
935 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
936 if (rc != BZ_OK) {
937 crm_err("Could not prepare to read compressed %s: %s "
938 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
939 BZ2_bzReadClose(&rc, bz_file);
940 fclose(input);
941 return NULL;
942 }
943
944 rc = BZ_OK;
945
946
947 while (rc == BZ_OK) {
948 buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1);
949 read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
950
951 crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
952
953 if (rc == BZ_OK || rc == BZ_STREAM_END) {
954 length += read_len;
955 }
956 }
957
958 buffer[length] = '\0';
959
960 if (rc != BZ_STREAM_END) {
961 crm_err("Could not read compressed %s: %s "
962 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
963 free(buffer);
964 buffer = NULL;
965 }
966
967 BZ2_bzReadClose(&rc, bz_file);
968 fclose(input);
969 return buffer;
970 }
971
972
973
974
975
976
977
978 void
979 pcmk__strip_xml_text(xmlNode *xml)
980 {
981 xmlNode *iter = xml->children;
982
983 while (iter) {
984 xmlNode *next = iter->next;
985
986 switch (iter->type) {
987 case XML_TEXT_NODE:
988
989 pcmk_free_xml_subtree(iter);
990 break;
991
992 case XML_ELEMENT_NODE:
993
994 pcmk__strip_xml_text(iter);
995 break;
996
997 default:
998
999 break;
1000 }
1001
1002 iter = next;
1003 }
1004 }
1005
1006 xmlNode *
1007 filename2xml(const char *filename)
1008 {
1009 xmlNode *xml = NULL;
1010 xmlDocPtr output = NULL;
1011 bool uncompressed = true;
1012 xmlParserCtxtPtr ctxt = NULL;
1013 xmlErrorPtr last_error = NULL;
1014
1015
1016 ctxt = xmlNewParserCtxt();
1017 CRM_CHECK(ctxt != NULL, return NULL);
1018
1019 xmlCtxtResetLastError(ctxt);
1020 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
1021
1022 if (filename) {
1023 uncompressed = !pcmk__ends_with_ext(filename, ".bz2");
1024 }
1025
1026 if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
1027
1028 output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
1029 PCMK__XML_PARSE_OPTS);
1030
1031 } else if (uncompressed) {
1032 output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
1033
1034 } else {
1035 char *input = decompress_file(filename);
1036
1037 output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
1038 PCMK__XML_PARSE_OPTS);
1039 free(input);
1040 }
1041
1042 if (output && (xml = xmlDocGetRootElement(output))) {
1043 pcmk__strip_xml_text(xml);
1044 }
1045
1046 last_error = xmlCtxtGetLastError(ctxt);
1047 if (last_error && last_error->code != XML_ERR_OK) {
1048
1049
1050
1051
1052
1053 crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
1054 last_error->domain, last_error->level, last_error->code, last_error->message);
1055
1056 if (last_error && last_error->code != XML_ERR_OK) {
1057 crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
1058 if (xml != NULL) {
1059 crm_log_xml_err(xml, "Partial");
1060 }
1061 }
1062 }
1063
1064 xmlFreeParserCtxt(ctxt);
1065 return xml;
1066 }
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076 const char *
1077 pcmk__xe_add_last_written(xmlNode *xe)
1078 {
1079 char *now_s = pcmk__epoch2str(NULL, 0);
1080 const char *result = NULL;
1081
1082 result = crm_xml_add(xe, XML_CIB_ATTR_WRITTEN,
1083 pcmk__s(now_s, "Could not determine current time"));
1084 free(now_s);
1085 return result;
1086 }
1087
1088
1089
1090
1091
1092
1093 void
1094 crm_xml_sanitize_id(char *id)
1095 {
1096 char *c;
1097
1098 for (c = id; *c; ++c) {
1099
1100 switch (*c) {
1101 case ':':
1102 case '#':
1103 *c = '.';
1104 }
1105 }
1106 }
1107
1108
1109
1110
1111
1112
1113
1114
1115 void
1116 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1117 {
1118 va_list ap;
1119 int len = 0;
1120 char *id = NULL;
1121
1122
1123 va_start(ap, format);
1124 len = vasprintf(&id, format, ap);
1125 va_end(ap);
1126 CRM_ASSERT(len > 0);
1127
1128 crm_xml_sanitize_id(id);
1129 crm_xml_add(xml, XML_ATTR_ID, id);
1130 free(id);
1131 }
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145 static int
1146 write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
1147 bool compress, unsigned int *nbytes)
1148 {
1149 int rc = pcmk_rc_ok;
1150 char *buffer = NULL;
1151
1152 *nbytes = 0;
1153 crm_log_xml_trace(xml_node, "writing");
1154
1155 buffer = dump_xml_formatted(xml_node);
1156 CRM_CHECK(buffer && strlen(buffer),
1157 crm_log_xml_warn(xml_node, "formatting failed");
1158 rc = pcmk_rc_error;
1159 goto bail);
1160
1161 if (compress) {
1162 unsigned int in = 0;
1163 BZFILE *bz_file = NULL;
1164
1165 rc = BZ_OK;
1166 bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1167 if (rc != BZ_OK) {
1168 crm_warn("Not compressing %s: could not prepare file stream: %s "
1169 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1170 } else {
1171 BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1172 if (rc != BZ_OK) {
1173 crm_warn("Not compressing %s: could not compress data: %s "
1174 CRM_XS " bzerror=%d errno=%d",
1175 filename, bz2_strerror(rc), rc, errno);
1176 }
1177 }
1178
1179 if (rc == BZ_OK) {
1180 BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1181 if (rc != BZ_OK) {
1182 crm_warn("Not compressing %s: could not write compressed data: %s "
1183 CRM_XS " bzerror=%d errno=%d",
1184 filename, bz2_strerror(rc), rc, errno);
1185 *nbytes = 0;
1186 } else {
1187 crm_trace("Compressed XML for %s from %u bytes to %u",
1188 filename, in, *nbytes);
1189 }
1190 }
1191 rc = pcmk_rc_ok;
1192 }
1193
1194 if (*nbytes == 0) {
1195 rc = fprintf(stream, "%s", buffer);
1196 if (rc < 0) {
1197 rc = errno;
1198 crm_perror(LOG_ERR, "writing %s", filename);
1199 } else {
1200 *nbytes = (unsigned int) rc;
1201 rc = pcmk_rc_ok;
1202 }
1203 }
1204
1205 bail:
1206
1207 if (fflush(stream) != 0) {
1208 rc = errno;
1209 crm_perror(LOG_ERR, "flushing %s", filename);
1210 }
1211
1212
1213 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1214 rc = errno;
1215 crm_perror(LOG_ERR, "synchronizing %s", filename);
1216 }
1217
1218 fclose(stream);
1219
1220 crm_trace("Saved %d bytes to %s as XML", *nbytes, filename);
1221 free(buffer);
1222
1223 return rc;
1224 }
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236 int
1237 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
1238 {
1239 FILE *stream = NULL;
1240 unsigned int nbytes = 0;
1241 int rc = pcmk_rc_ok;
1242
1243 CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
1244 stream = fdopen(fd, "w");
1245 if (stream == NULL) {
1246 return -errno;
1247 }
1248 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1249 if (rc != pcmk_rc_ok) {
1250 return pcmk_rc2legacy(rc);
1251 }
1252 return (int) nbytes;
1253 }
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264 int
1265 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
1266 {
1267 FILE *stream = NULL;
1268 unsigned int nbytes = 0;
1269 int rc = pcmk_rc_ok;
1270
1271 CRM_CHECK(xml_node && filename, return -EINVAL);
1272 stream = fopen(filename, "w");
1273 if (stream == NULL) {
1274 return -errno;
1275 }
1276 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1277 if (rc != pcmk_rc_ok) {
1278 return pcmk_rc2legacy(rc);
1279 }
1280 return (int) nbytes;
1281 }
1282
1283
1284 static char *
1285 replace_text(char *text, int start, size_t *length, const char *replace)
1286 {
1287 size_t offset = strlen(replace) - 1;
1288
1289 *length += offset;
1290 text = pcmk__realloc(text, *length);
1291
1292 for (size_t lpc = (*length) - 1; lpc > (start + offset); lpc--) {
1293 text[lpc] = text[lpc - offset];
1294 }
1295
1296 memcpy(text + start, replace, offset + 1);
1297 return text;
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309 char *
1310 crm_xml_escape(const char *text)
1311 {
1312 size_t length;
1313 char *copy;
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330 if (text == NULL) {
1331 return NULL;
1332 }
1333
1334 length = 1 + strlen(text);
1335 copy = strdup(text);
1336 CRM_ASSERT(copy != NULL);
1337 for (size_t index = 0; index < length; index++) {
1338 if(copy[index] & 0x80 && copy[index+1] & 0x80){
1339 index++;
1340 break;
1341 }
1342 switch (copy[index]) {
1343 case 0:
1344 break;
1345 case '<':
1346 copy = replace_text(copy, index, &length, "<");
1347 break;
1348 case '>':
1349 copy = replace_text(copy, index, &length, ">");
1350 break;
1351 case '"':
1352 copy = replace_text(copy, index, &length, """);
1353 break;
1354 case '\'':
1355 copy = replace_text(copy, index, &length, "'");
1356 break;
1357 case '&':
1358 copy = replace_text(copy, index, &length, "&");
1359 break;
1360 case '\t':
1361
1362 copy = replace_text(copy, index, &length, " ");
1363 break;
1364 case '\n':
1365 copy = replace_text(copy, index, &length, "\\n");
1366 break;
1367 case '\r':
1368 copy = replace_text(copy, index, &length, "\\r");
1369 break;
1370 default:
1371
1372 if(copy[index] < ' ' || copy[index] > '~') {
1373 char *replace = crm_strdup_printf("\\%.3o", copy[index]);
1374
1375 copy = replace_text(copy, index, &length, replace);
1376 free(replace);
1377 }
1378 }
1379 }
1380 return copy;
1381 }
1382
1383
1384
1385
1386
1387
1388
1389
1390 static void
1391 dump_xml_attr(const xmlAttr *attr, GString *buffer)
1392 {
1393 char *p_value = NULL;
1394 const char *p_name = NULL;
1395 xml_node_private_t *nodepriv = NULL;
1396
1397 if (attr == NULL || attr->children == NULL) {
1398 return;
1399 }
1400
1401 nodepriv = attr->_private;
1402 if (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
1403 return;
1404 }
1405
1406 p_name = (const char *) attr->name;
1407 p_value = crm_xml_escape((const char *)attr->children->content);
1408 pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"",
1409 NULL);
1410
1411 free(p_value);
1412 }
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423 static void
1424 dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
1425 int depth)
1426 {
1427 const char *name = crm_element_name(data);
1428 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1429 bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
1430 int spaces = pretty? (2 * depth) : 0;
1431
1432 CRM_ASSERT(name != NULL);
1433
1434 for (int lpc = 0; lpc < spaces; lpc++) {
1435 g_string_append_c(buffer, ' ');
1436 }
1437
1438 pcmk__g_strcat(buffer, "<", name, NULL);
1439
1440 for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
1441 attr = attr->next) {
1442
1443 if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
1444 dump_xml_attr(attr, buffer);
1445 }
1446 }
1447
1448 if (data->children == NULL) {
1449 g_string_append(buffer, "/>");
1450
1451 } else {
1452 g_string_append_c(buffer, '>');
1453 }
1454
1455 if (pretty) {
1456 g_string_append_c(buffer, '\n');
1457 }
1458
1459 if (data->children) {
1460 xmlNode *xChild = NULL;
1461 for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1462 pcmk__xml2text(xChild, options, buffer, depth + 1);
1463 }
1464
1465 for (int lpc = 0; lpc < spaces; lpc++) {
1466 g_string_append_c(buffer, ' ');
1467 }
1468
1469 pcmk__g_strcat(buffer, "</", name, ">", NULL);
1470
1471 if (pretty) {
1472 g_string_append_c(buffer, '\n');
1473 }
1474 }
1475 }
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486 static void
1487 dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer,
1488 int depth)
1489 {
1490
1491
1492
1493 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1494 int spaces = pretty? (2 * depth) : 0;
1495
1496 for (int lpc = 0; lpc < spaces; lpc++) {
1497 g_string_append_c(buffer, ' ');
1498 }
1499
1500 g_string_append(buffer, (const gchar *) data->content);
1501
1502 if (pretty) {
1503 g_string_append_c(buffer, '\n');
1504 }
1505 }
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516 static void
1517 dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer,
1518 int depth)
1519 {
1520 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1521 int spaces = pretty? (2 * depth) : 0;
1522
1523 for (int lpc = 0; lpc < spaces; lpc++) {
1524 g_string_append_c(buffer, ' ');
1525 }
1526
1527 pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
1528 NULL);
1529
1530 if (pretty) {
1531 g_string_append_c(buffer, '\n');
1532 }
1533 }
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544 static void
1545 dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
1546 int depth)
1547 {
1548 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
1549 int spaces = pretty? (2 * depth) : 0;
1550
1551 for (int lpc = 0; lpc < spaces; lpc++) {
1552 g_string_append_c(buffer, ' ');
1553 }
1554
1555 pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
1556
1557 if (pretty) {
1558 g_string_append_c(buffer, '\n');
1559 }
1560 }
1561
1562 #define PCMK__XMLDUMP_STATS 0
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573 void
1574 pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
1575 {
1576 if (data == NULL) {
1577 crm_trace("Nothing to dump");
1578 return;
1579 }
1580
1581 CRM_ASSERT(buffer != NULL);
1582 CRM_CHECK(depth >= 0, depth = 0);
1583
1584 if (pcmk_is_set(options, pcmk__xml_fmt_full)) {
1585
1586
1587
1588
1589
1590
1591
1592 #if (PCMK__XMLDUMP_STATS - 0)
1593 time_t next, new = time(NULL);
1594 #endif
1595 xmlDoc *doc;
1596 xmlOutputBuffer *xml_buffer;
1597
1598 doc = getDocPtr(data);
1599
1600 CRM_CHECK(doc != NULL, return);
1601
1602 xml_buffer = xmlAllocOutputBuffer(NULL);
1603 CRM_ASSERT(xml_buffer != NULL);
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615 xmlNodeDumpOutput(xml_buffer, doc, data, 0,
1616 pcmk_is_set(options, pcmk__xml_fmt_pretty), NULL);
1617
1618 (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
1619 if (xml_buffer->buffer != NULL) {
1620 g_string_append(buffer,
1621 (const gchar *) xmlBufContent(xml_buffer->buffer));
1622 }
1623
1624 #if (PCMK__XMLDUMP_STATS - 0)
1625 next = time(NULL);
1626 if ((now + 1) < next) {
1627 crm_log_xml_trace(data, "Long time");
1628 crm_err("xmlNodeDumpOutput() -> %lld bytes took %ds",
1629 (long long) buffer->len, next - now);
1630 }
1631 #endif
1632
1633
1634 (void) xmlOutputBufferClose(xml_buffer);
1635 return;
1636 }
1637
1638 switch(data->type) {
1639 case XML_ELEMENT_NODE:
1640
1641 dump_xml_element(data, options, buffer, depth);
1642 break;
1643 case XML_TEXT_NODE:
1644 if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
1645
1646
1647
1648
1649
1650 dump_xml_text(data, options, buffer, depth);
1651 }
1652 break;
1653 case XML_COMMENT_NODE:
1654 dump_xml_comment(data, options, buffer, depth);
1655 break;
1656 case XML_CDATA_SECTION_NODE:
1657 dump_xml_cdata(data, options, buffer, depth);
1658 break;
1659 default:
1660 crm_warn("Unhandled type: %d", data->type);
1661 break;
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682 }
1683 }
1684
1685 char *
1686 dump_xml_formatted_with_text(xmlNode * an_xml_node)
1687 {
1688 char *buffer = NULL;
1689 GString *g_buffer = g_string_sized_new(1024);
1690
1691 pcmk__xml2text(an_xml_node, pcmk__xml_fmt_pretty|pcmk__xml_fmt_full,
1692 g_buffer, 0);
1693
1694 pcmk__str_update(&buffer, g_buffer->str);
1695 g_string_free(g_buffer, TRUE);
1696 return buffer;
1697 }
1698
1699 char *
1700 dump_xml_formatted(xmlNode * an_xml_node)
1701 {
1702 char *buffer = NULL;
1703 GString *g_buffer = g_string_sized_new(1024);
1704
1705 pcmk__xml2text(an_xml_node, pcmk__xml_fmt_pretty, g_buffer, 0);
1706
1707 pcmk__str_update(&buffer, g_buffer->str);
1708 g_string_free(g_buffer, TRUE);
1709 return buffer;
1710 }
1711
1712 char *
1713 dump_xml_unformatted(xmlNode * an_xml_node)
1714 {
1715 char *buffer = NULL;
1716 GString *g_buffer = g_string_sized_new(1024);
1717
1718 pcmk__xml2text(an_xml_node, 0, g_buffer, 0);
1719
1720 pcmk__str_update(&buffer, g_buffer->str);
1721 g_string_free(g_buffer, TRUE);
1722 return buffer;
1723 }
1724
1725 gboolean
1726 xml_has_children(const xmlNode * xml_root)
1727 {
1728 if (xml_root != NULL && xml_root->children != NULL) {
1729 return TRUE;
1730 }
1731 return FALSE;
1732 }
1733
1734 void
1735 xml_remove_prop(xmlNode * obj, const char *name)
1736 {
1737 if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
1738 crm_trace("Cannot remove %s from %s", name, obj->name);
1739
1740 } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
1741
1742 xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
1743 xml_node_private_t *nodepriv = attr->_private;
1744
1745 set_parent_flag(obj, pcmk__xf_dirty);
1746 pcmk__set_xml_flags(nodepriv, pcmk__xf_deleted);
1747 } else {
1748 xmlUnsetProp(obj, (pcmkXmlStr) name);
1749 }
1750 }
1751
1752 void
1753 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
1754 {
1755 char *f = NULL;
1756
1757 if (filename == NULL) {
1758 char *uuid = crm_generate_uuid();
1759
1760 f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
1761 filename = f;
1762 free(uuid);
1763 }
1764
1765 crm_info("Saving %s to %s", desc, filename);
1766 write_xml_file(xml, filename, FALSE);
1767 free(f);
1768 }
1769
1770
1771
1772
1773
1774
1775
1776
1777 static void
1778 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
1779 {
1780 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
1781 pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
1782 }
1783 }
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799 static void
1800 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
1801 const char *old_value)
1802 {
1803 xml_doc_private_t *docpriv = new_xml->doc->_private;
1804 xmlAttr *attr = NULL;
1805 xml_node_private_t *nodepriv;
1806
1807
1808 pcmk__clear_xml_flags(docpriv, pcmk__xf_tracking);
1809
1810
1811 attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1812 pcmk__set_xml_flags(docpriv, pcmk__xf_tracking);
1813
1814
1815 nodepriv = attr->_private;
1816 nodepriv->flags = 0;
1817
1818
1819 xml_remove_prop(new_xml, attr_name);
1820
1821 crm_trace("XML attribute %s=%s was removed from %s",
1822 attr_name, old_value, element);
1823 }
1824
1825
1826
1827
1828
1829 static void
1830 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
1831 const char *old_value)
1832 {
1833 char *vcopy = crm_element_value_copy(new_xml, attr_name);
1834
1835 crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
1836 attr_name, old_value, vcopy, element);
1837
1838
1839 xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
1840
1841
1842 crm_xml_add(new_xml, attr_name, vcopy);
1843 free(vcopy);
1844 }
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857 static void
1858 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
1859 xmlAttr *new_attr, int p_old, int p_new)
1860 {
1861 xml_node_private_t *nodepriv = new_attr->_private;
1862
1863 crm_trace("XML attribute %s moved from position %d to %d in %s",
1864 old_attr->name, p_old, p_new, element);
1865
1866
1867 mark_xml_node_dirty(new_xml);
1868
1869
1870 pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_moved);
1871
1872 nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
1873 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
1874 }
1875
1876
1877
1878
1879
1880
1881
1882
1883 static void
1884 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
1885 {
1886 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
1887
1888 while (attr_iter != NULL) {
1889 xmlAttr *old_attr = attr_iter;
1890 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
1891 const char *name = (const char *) attr_iter->name;
1892 const char *old_value = crm_element_value(old_xml, name);
1893
1894 attr_iter = attr_iter->next;
1895 if (new_attr == NULL) {
1896 mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
1897 old_value);
1898
1899 } else {
1900 xml_node_private_t *nodepriv = new_attr->_private;
1901 int new_pos = pcmk__xml_position((xmlNode*) new_attr,
1902 pcmk__xf_skip);
1903 int old_pos = pcmk__xml_position((xmlNode*) old_attr,
1904 pcmk__xf_skip);
1905 const char *new_value = crm_element_value(new_xml, name);
1906
1907
1908 pcmk__clear_xml_flags(nodepriv, pcmk__xf_created);
1909
1910 if (strcmp(new_value, old_value) != 0) {
1911 mark_attr_changed(new_xml, (const char *) old_xml->name, name,
1912 old_value);
1913
1914 } else if ((old_pos != new_pos)
1915 && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
1916 mark_attr_moved(new_xml, (const char *) old_xml->name,
1917 old_attr, new_attr, old_pos, new_pos);
1918 }
1919 }
1920 }
1921 }
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932 static void
1933 mark_created_attrs(xmlNode *new_xml)
1934 {
1935 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
1936
1937 while (attr_iter != NULL) {
1938 xmlAttr *new_attr = attr_iter;
1939 xml_node_private_t *nodepriv = attr_iter->_private;
1940
1941 attr_iter = attr_iter->next;
1942 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
1943 const char *attr_name = (const char *) new_attr->name;
1944
1945 crm_trace("Created new attribute %s=%s in %s",
1946 attr_name, crm_element_value(new_xml, attr_name),
1947 new_xml->name);
1948
1949
1950
1951
1952 if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
1953 pcmk__mark_xml_attr_dirty(new_attr);
1954 } else {
1955
1956 xmlUnsetProp(new_xml, new_attr->name);
1957 }
1958 }
1959 }
1960 }
1961
1962
1963
1964
1965
1966
1967
1968
1969 static void
1970 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
1971 {
1972 set_attrs_flag(new_xml, pcmk__xf_created);
1973 xml_diff_old_attrs(old_xml, new_xml);
1974 mark_created_attrs(new_xml);
1975 }
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989 static void
1990 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
1991 {
1992
1993 xmlNode *candidate = add_node_copy(new_parent, old_child);
1994
1995
1996 reset_xml_node_flags(candidate);
1997
1998
1999 pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
2000
2001
2002 free_xml_with_position(candidate,
2003 pcmk__xml_position(old_child, pcmk__xf_skip));
2004
2005 if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
2006 pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
2007 pcmk__xf_skip);
2008 }
2009 }
2010
2011 static void
2012 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2013 int p_old, int p_new)
2014 {
2015 xml_node_private_t *nodepriv = new_child->_private;
2016
2017 crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
2018 new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
2019 p_old, p_new, new_parent->name);
2020 mark_xml_node_dirty(new_parent);
2021 pcmk__set_xml_flags(nodepriv, pcmk__xf_moved);
2022
2023 if (p_old > p_new) {
2024 nodepriv = old_child->_private;
2025 } else {
2026 nodepriv = new_child->_private;
2027 }
2028 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
2029 }
2030
2031
2032 static void
2033 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
2034 {
2035 xmlNode *cIter = NULL;
2036 xml_node_private_t *nodepriv = NULL;
2037
2038 CRM_CHECK(new_xml != NULL, return);
2039 if (old_xml == NULL) {
2040 pcmk__mark_xml_created(new_xml);
2041 pcmk__apply_creation_acl(new_xml, check_top);
2042 return;
2043 }
2044
2045 nodepriv = new_xml->_private;
2046 CRM_CHECK(nodepriv != NULL, return);
2047
2048 if(nodepriv->flags & pcmk__xf_processed) {
2049
2050 return;
2051 }
2052 pcmk__set_xml_flags(nodepriv, pcmk__xf_processed);
2053
2054 xml_diff_attrs(old_xml, new_xml);
2055
2056
2057 for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2058 xmlNode *old_child = cIter;
2059 xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true);
2060
2061 cIter = pcmk__xml_next(cIter);
2062 if(new_child) {
2063 mark_xml_changes(old_child, new_child, TRUE);
2064
2065 } else {
2066 mark_child_deleted(old_child, new_xml);
2067 }
2068 }
2069
2070
2071 for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2072 xmlNode *new_child = cIter;
2073 xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true);
2074
2075 cIter = pcmk__xml_next(cIter);
2076 if(old_child == NULL) {
2077
2078 nodepriv = new_child->_private;
2079 pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
2080 mark_xml_changes(old_child, new_child, TRUE);
2081
2082 } else {
2083
2084 int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
2085 int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
2086
2087 if(p_old != p_new) {
2088 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2089 }
2090 }
2091 }
2092 }
2093
2094 void
2095 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
2096 {
2097 pcmk__set_xml_doc_flag(new_xml, pcmk__xf_lazy);
2098 xml_calculate_changes(old_xml, new_xml);
2099 }
2100
2101
2102 void
2103 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
2104 {
2105 CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
2106 return);
2107 CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
2108
2109 if(xml_tracking_changes(new_xml) == FALSE) {
2110 xml_track_changes(new_xml, NULL, NULL, FALSE);
2111 }
2112
2113 mark_xml_changes(old_xml, new_xml, FALSE);
2114 }
2115
2116 gboolean
2117 can_prune_leaf(xmlNode * xml_node)
2118 {
2119 xmlNode *cIter = NULL;
2120 gboolean can_prune = TRUE;
2121 const char *name = crm_element_name(xml_node);
2122
2123 if (pcmk__strcase_any_of(name, XML_TAG_RESOURCE_REF, XML_CIB_TAG_OBJ_REF,
2124 XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1, NULL)) {
2125 return FALSE;
2126 }
2127
2128 for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2129 const char *p_name = (const char *) a->name;
2130
2131 if (strcmp(p_name, XML_ATTR_ID) == 0) {
2132 continue;
2133 }
2134 can_prune = FALSE;
2135 }
2136
2137 cIter = pcmk__xml_first_child(xml_node);
2138 while (cIter) {
2139 xmlNode *child = cIter;
2140
2141 cIter = pcmk__xml_next(cIter);
2142 if (can_prune_leaf(child)) {
2143 free_xml(child);
2144 } else {
2145 can_prune = FALSE;
2146 }
2147 }
2148 return can_prune;
2149 }
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159 xmlNode *
2160 pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
2161 {
2162 xmlNode *a_child = NULL;
2163 int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
2164
2165 CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
2166
2167 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2168 a_child = pcmk__xml_next(a_child)) {
2169 if (exact) {
2170 int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
2171 xml_node_private_t *nodepriv = a_child->_private;
2172
2173 if (offset < search_offset) {
2174 continue;
2175
2176 } else if (offset > search_offset) {
2177 return NULL;
2178 }
2179
2180 if (pcmk_is_set(nodepriv->flags, pcmk__xf_skip)) {
2181 continue;
2182 }
2183 }
2184
2185 if (a_child->type == XML_COMMENT_NODE
2186 && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
2187 return a_child;
2188
2189 } else if (exact) {
2190 return NULL;
2191 }
2192 }
2193
2194 return NULL;
2195 }
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208 void
2209 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
2210 {
2211 CRM_CHECK(update != NULL, return);
2212 CRM_CHECK(update->type == XML_COMMENT_NODE, return);
2213
2214 if (target == NULL) {
2215 target = pcmk__xc_match(parent, update, false);
2216 }
2217
2218 if (target == NULL) {
2219 add_node_copy(parent, update);
2220
2221 } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
2222 xmlFree(target->content);
2223 target->content = xmlStrdup(update->content);
2224 }
2225 }
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239 void
2240 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
2241 bool as_diff)
2242 {
2243 xmlNode *a_child = NULL;
2244 const char *object_name = NULL,
2245 *object_href = NULL,
2246 *object_href_val = NULL;
2247
2248 #if XML_PARSER_DEBUG
2249 crm_log_xml_trace(update, "update:");
2250 crm_log_xml_trace(target, "target:");
2251 #endif
2252
2253 CRM_CHECK(update != NULL, return);
2254
2255 if (update->type == XML_COMMENT_NODE) {
2256 pcmk__xc_update(parent, target, update);
2257 return;
2258 }
2259
2260 object_name = crm_element_name(update);
2261 object_href_val = ID(update);
2262 if (object_href_val != NULL) {
2263 object_href = XML_ATTR_ID;
2264 } else {
2265 object_href_val = crm_element_value(update, XML_ATTR_IDREF);
2266 object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
2267 }
2268
2269 CRM_CHECK(object_name != NULL, return);
2270 CRM_CHECK(target != NULL || parent != NULL, return);
2271
2272 if (target == NULL) {
2273 target = pcmk__xe_match(parent, object_name,
2274 object_href, object_href_val);
2275 }
2276
2277 if (target == NULL) {
2278 target = create_xml_node(parent, object_name);
2279 CRM_CHECK(target != NULL, return);
2280 #if XML_PARSER_DEBUG
2281 crm_trace("Added <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2282 object_href ? " " : "",
2283 object_href ? object_href : "",
2284 object_href ? "=" : "",
2285 object_href ? object_href_val : "");
2286
2287 } else {
2288 crm_trace("Found node <%s%s%s%s%s/> to update",
2289 pcmk__s(object_name, "<null>"),
2290 object_href ? " " : "",
2291 object_href ? object_href : "",
2292 object_href ? "=" : "",
2293 object_href ? object_href_val : "");
2294 #endif
2295 }
2296
2297 CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2298 pcmk__str_casei),
2299 return);
2300
2301 if (as_diff == FALSE) {
2302
2303 copy_in_properties(target, update);
2304
2305 } else {
2306
2307 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2308 a = a->next) {
2309 const char *p_value = pcmk__xml_attr_value(a);
2310
2311
2312 xmlUnsetProp(target, a->name);
2313 xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
2314 }
2315 }
2316
2317 for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2318 a_child = pcmk__xml_next(a_child)) {
2319 #if XML_PARSER_DEBUG
2320 crm_trace("Updating child <%s%s%s%s%s/>",
2321 pcmk__s(object_name, "<null>"),
2322 object_href ? " " : "",
2323 object_href ? object_href : "",
2324 object_href ? "=" : "",
2325 object_href ? object_href_val : "");
2326 #endif
2327 pcmk__xml_update(target, NULL, a_child, as_diff);
2328 }
2329
2330 #if XML_PARSER_DEBUG
2331 crm_trace("Finished with <%s%s%s%s%s/>", pcmk__s(object_name, "<null>"),
2332 object_href ? " " : "",
2333 object_href ? object_href : "",
2334 object_href ? "=" : "",
2335 object_href ? object_href_val : "");
2336 #endif
2337 }
2338
2339 gboolean
2340 update_xml_child(xmlNode * child, xmlNode * to_update)
2341 {
2342 gboolean can_update = TRUE;
2343 xmlNode *child_of_child = NULL;
2344
2345 CRM_CHECK(child != NULL, return FALSE);
2346 CRM_CHECK(to_update != NULL, return FALSE);
2347
2348 if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
2349 can_update = FALSE;
2350
2351 } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
2352 can_update = FALSE;
2353
2354 } else if (can_update) {
2355 #if XML_PARSER_DEBUG
2356 crm_log_xml_trace(child, "Update match found...");
2357 #endif
2358 pcmk__xml_update(NULL, child, to_update, false);
2359 }
2360
2361 for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2362 child_of_child = pcmk__xml_next(child_of_child)) {
2363
2364 if (can_update) {
2365 break;
2366 }
2367 can_update = update_xml_child(child_of_child, to_update);
2368 }
2369
2370 return can_update;
2371 }
2372
2373 int
2374 find_xml_children(xmlNode ** children, xmlNode * root,
2375 const char *tag, const char *field, const char *value, gboolean search_matches)
2376 {
2377 int match_found = 0;
2378
2379 CRM_CHECK(root != NULL, return FALSE);
2380 CRM_CHECK(children != NULL, return FALSE);
2381
2382 if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
2383
2384 } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
2385
2386 } else {
2387 if (*children == NULL) {
2388 *children = create_xml_node(NULL, __func__);
2389 }
2390 add_node_copy(*children, root);
2391 match_found = 1;
2392 }
2393
2394 if (search_matches || match_found == 0) {
2395 xmlNode *child = NULL;
2396
2397 for (child = pcmk__xml_first_child(root); child != NULL;
2398 child = pcmk__xml_next(child)) {
2399 match_found += find_xml_children(children, child, tag, field, value, search_matches);
2400 }
2401 }
2402
2403 return match_found;
2404 }
2405
2406 gboolean
2407 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2408 {
2409 gboolean can_delete = FALSE;
2410 xmlNode *child_of_child = NULL;
2411
2412 const char *up_id = NULL;
2413 const char *child_id = NULL;
2414 const char *right_val = NULL;
2415
2416 CRM_CHECK(child != NULL, return FALSE);
2417 CRM_CHECK(update != NULL, return FALSE);
2418
2419 up_id = ID(update);
2420 child_id = ID(child);
2421
2422 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2423 can_delete = TRUE;
2424 }
2425 if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
2426 can_delete = FALSE;
2427 }
2428 if (can_delete && delete_only) {
2429 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2430 a = a->next) {
2431 const char *p_name = (const char *) a->name;
2432 const char *p_value = pcmk__xml_attr_value(a);
2433
2434 right_val = crm_element_value(child, p_name);
2435 if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) {
2436 can_delete = FALSE;
2437 }
2438 }
2439 }
2440
2441 if (can_delete && parent != NULL) {
2442 crm_log_xml_trace(child, "Delete match found...");
2443 if (delete_only || update == NULL) {
2444 free_xml(child);
2445
2446 } else {
2447 xmlNode *tmp = copy_xml(update);
2448 xmlDoc *doc = tmp->doc;
2449 xmlNode *old = NULL;
2450
2451 xml_accept_changes(tmp);
2452 old = xmlReplaceNode(child, tmp);
2453
2454 if(xml_tracking_changes(tmp)) {
2455
2456 pcmk__apply_acl(tmp);
2457 }
2458
2459 xml_calculate_changes(old, tmp);
2460 xmlDocSetRootElement(doc, old);
2461 free_xml(old);
2462 }
2463 child = NULL;
2464 return TRUE;
2465
2466 } else if (can_delete) {
2467 crm_log_xml_debug(child, "Cannot delete the search root");
2468 can_delete = FALSE;
2469 }
2470
2471 child_of_child = pcmk__xml_first_child(child);
2472 while (child_of_child) {
2473 xmlNode *next = pcmk__xml_next(child_of_child);
2474
2475 can_delete = replace_xml_child(child, child_of_child, update, delete_only);
2476
2477
2478 if (can_delete) {
2479 child_of_child = NULL;
2480 } else {
2481 child_of_child = next;
2482 }
2483 }
2484
2485 return can_delete;
2486 }
2487
2488 xmlNode *
2489 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2490 {
2491 xmlNode *child = NULL;
2492 GSList *nvpairs = NULL;
2493 xmlNode *result = NULL;
2494 const char *name = NULL;
2495
2496 CRM_CHECK(input != NULL, return NULL);
2497
2498 name = crm_element_name(input);
2499 CRM_CHECK(name != NULL, return NULL);
2500
2501 result = create_xml_node(parent, name);
2502 nvpairs = pcmk_xml_attrs2nvpairs(input);
2503 nvpairs = pcmk_sort_nvpairs(nvpairs);
2504 pcmk_nvpairs2xml_attrs(nvpairs, result);
2505 pcmk_free_nvpairs(nvpairs);
2506
2507 for (child = pcmk__xml_first_child(input); child != NULL;
2508 child = pcmk__xml_next(child)) {
2509
2510 if (recursive) {
2511 sorted_xml(child, result, recursive);
2512 } else {
2513 add_node_copy(result, child);
2514 }
2515 }
2516
2517 return result;
2518 }
2519
2520 xmlNode *
2521 first_named_child(const xmlNode *parent, const char *name)
2522 {
2523 xmlNode *match = NULL;
2524
2525 for (match = pcmk__xe_first_child(parent); match != NULL;
2526 match = pcmk__xe_next(match)) {
2527
2528
2529
2530
2531
2532 if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) {
2533 return match;
2534 }
2535 }
2536 return NULL;
2537 }
2538
2539
2540
2541
2542
2543
2544
2545
2546 xmlNode *
2547 crm_next_same_xml(const xmlNode *sibling)
2548 {
2549 xmlNode *match = pcmk__xe_next(sibling);
2550 const char *name = crm_element_name(sibling);
2551
2552 while (match != NULL) {
2553 if (!strcmp(crm_element_name(match), name)) {
2554 return match;
2555 }
2556 match = pcmk__xe_next(match);
2557 }
2558 return NULL;
2559 }
2560
2561 void
2562 crm_xml_init(void)
2563 {
2564 static bool init = true;
2565
2566 if(init) {
2567 init = false;
2568
2569
2570
2571
2572
2573 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2574
2575
2576 xmlDeregisterNodeDefault(free_private_data);
2577 xmlRegisterNodeDefault(new_private_data);
2578
2579 crm_schema_init();
2580 }
2581 }
2582
2583 void
2584 crm_xml_cleanup(void)
2585 {
2586 crm_schema_cleanup();
2587 xmlCleanupParser();
2588 }
2589
2590 #define XPATH_MAX 512
2591
2592 xmlNode *
2593 expand_idref(xmlNode * input, xmlNode * top)
2594 {
2595 const char *tag = NULL;
2596 const char *ref = NULL;
2597 xmlNode *result = input;
2598
2599 if (result == NULL) {
2600 return NULL;
2601
2602 } else if (top == NULL) {
2603 top = input;
2604 }
2605
2606 tag = crm_element_name(result);
2607 ref = crm_element_value(result, XML_ATTR_IDREF);
2608
2609 if (ref != NULL) {
2610 char *xpath_string = crm_strdup_printf("//%s[@" XML_ATTR_ID "='%s']",
2611 tag, ref);
2612
2613 result = get_xpath_object(xpath_string, top, LOG_ERR);
2614 if (result == NULL) {
2615 char *nodePath = (char *)xmlGetNodePath(top);
2616
2617 crm_err("No match for %s found in %s: Invalid configuration",
2618 xpath_string, pcmk__s(nodePath, "unrecognizable path"));
2619 free(nodePath);
2620 }
2621 free(xpath_string);
2622 }
2623 return result;
2624 }
2625
2626 char *
2627 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
2628 {
2629 static const char *base = NULL;
2630 char *ret = NULL;
2631
2632 if (base == NULL) {
2633 base = getenv("PCMK_schema_directory");
2634 }
2635 if (pcmk__str_empty(base)) {
2636 base = CRM_SCHEMA_DIRECTORY;
2637 }
2638
2639 switch (ns) {
2640 case pcmk__xml_artefact_ns_legacy_rng:
2641 case pcmk__xml_artefact_ns_legacy_xslt:
2642 ret = strdup(base);
2643 break;
2644 case pcmk__xml_artefact_ns_base_rng:
2645 case pcmk__xml_artefact_ns_base_xslt:
2646 ret = crm_strdup_printf("%s/base", base);
2647 break;
2648 default:
2649 crm_err("XML artefact family specified as %u not recognized", ns);
2650 }
2651 return ret;
2652 }
2653
2654 char *
2655 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2656 {
2657 char *base = pcmk__xml_artefact_root(ns), *ret = NULL;
2658
2659 switch (ns) {
2660 case pcmk__xml_artefact_ns_legacy_rng:
2661 case pcmk__xml_artefact_ns_base_rng:
2662 ret = crm_strdup_printf("%s/%s.rng", base, filespec);
2663 break;
2664 case pcmk__xml_artefact_ns_legacy_xslt:
2665 case pcmk__xml_artefact_ns_base_xslt:
2666 ret = crm_strdup_printf("%s/%s.xsl", base, filespec);
2667 break;
2668 default:
2669 crm_err("XML artefact family specified as %u not recognized", ns);
2670 }
2671 free(base);
2672
2673 return ret;
2674 }
2675
2676 void
2677 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2678 {
2679 while (true) {
2680 const char *name, *value;
2681
2682 name = va_arg(pairs, const char *);
2683 if (name == NULL) {
2684 return;
2685 }
2686
2687 value = va_arg(pairs, const char *);
2688 if (value != NULL) {
2689 crm_xml_add(node, name, value);
2690 }
2691 }
2692 }
2693
2694 void
2695 pcmk__xe_set_props(xmlNodePtr node, ...)
2696 {
2697 va_list pairs;
2698 va_start(pairs, node);
2699 pcmk__xe_set_propv(node, pairs);
2700 va_end(pairs);
2701 }
2702
2703 int
2704 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
2705 int (*handler)(xmlNode *xml, void *userdata),
2706 void *userdata)
2707 {
2708 xmlNode *children = (xml? xml->children : NULL);
2709
2710 CRM_ASSERT(handler != NULL);
2711
2712 for (xmlNode *node = children; node != NULL; node = node->next) {
2713 if (node->type == XML_ELEMENT_NODE &&
2714 pcmk__str_eq(child_element_name, (const char *) node->name, pcmk__str_null_matches)) {
2715 int rc = handler(node, userdata);
2716
2717 if (rc != pcmk_rc_ok) {
2718 return rc;
2719 }
2720 }
2721 }
2722
2723 return pcmk_rc_ok;
2724 }
2725
2726
2727
2728
2729 #include <crm/common/xml_compat.h>
2730
2731 xmlNode *
2732 find_entity(xmlNode *parent, const char *node_name, const char *id)
2733 {
2734 return pcmk__xe_match(parent, node_name,
2735 ((id == NULL)? id : XML_ATTR_ID), id);
2736 }
2737
2738 void
2739 crm_destroy_xml(gpointer data)
2740 {
2741 free_xml(data);
2742 }
2743
2744 int
2745 add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
2746 {
2747 add_node_copy(parent, child);
2748 free_xml(child);
2749 return 1;
2750 }
2751
2752
2753