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