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