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