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