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 return NULL;
979 }
980
981 rc = BZ_OK;
982
983
984 while (rc == BZ_OK) {
985 buffer = pcmk__realloc(buffer, PCMK__BUFFER_SIZE + length + 1);
986 read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
987
988 crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
989
990 if (rc == BZ_OK || rc == BZ_STREAM_END) {
991 length += read_len;
992 }
993 }
994
995 buffer[length] = '\0';
996
997 if (rc != BZ_STREAM_END) {
998 crm_err("Could not read compressed %s: %s "
999 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1000 free(buffer);
1001 buffer = NULL;
1002 }
1003
1004 BZ2_bzReadClose(&rc, bz_file);
1005 fclose(input);
1006 return buffer;
1007 }
1008
1009
1010
1011
1012
1013
1014
1015 void
1016 pcmk__strip_xml_text(xmlNode *xml)
1017 {
1018 xmlNode *iter = xml->children;
1019
1020 while (iter) {
1021 xmlNode *next = iter->next;
1022
1023 switch (iter->type) {
1024 case XML_TEXT_NODE:
1025
1026 pcmk_free_xml_subtree(iter);
1027 break;
1028
1029 case XML_ELEMENT_NODE:
1030
1031 pcmk__strip_xml_text(iter);
1032 break;
1033
1034 default:
1035
1036 break;
1037 }
1038
1039 iter = next;
1040 }
1041 }
1042
1043 xmlNode *
1044 filename2xml(const char *filename)
1045 {
1046 xmlNode *xml = NULL;
1047 xmlDocPtr output = NULL;
1048 bool uncompressed = true;
1049 xmlParserCtxtPtr ctxt = NULL;
1050 xmlErrorPtr last_error = NULL;
1051
1052
1053 ctxt = xmlNewParserCtxt();
1054 CRM_CHECK(ctxt != NULL, return NULL);
1055
1056 xmlCtxtResetLastError(ctxt);
1057 xmlSetGenericErrorFunc(ctxt, log_xmllib_err);
1058
1059 if (filename) {
1060 uncompressed = !pcmk__ends_with_ext(filename, ".bz2");
1061 }
1062
1063 if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
1064
1065 output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
1066 PCMK__XML_PARSE_OPTS);
1067
1068 } else if (uncompressed) {
1069 output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
1070
1071 } else {
1072 char *input = decompress_file(filename);
1073
1074 output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
1075 PCMK__XML_PARSE_OPTS);
1076 free(input);
1077 }
1078
1079 if (output && (xml = xmlDocGetRootElement(output))) {
1080 pcmk__strip_xml_text(xml);
1081 }
1082
1083 last_error = xmlCtxtGetLastError(ctxt);
1084 if (last_error && last_error->code != XML_ERR_OK) {
1085
1086
1087
1088
1089
1090 crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
1091 last_error->domain, last_error->level, last_error->code, last_error->message);
1092
1093 if (last_error && last_error->code != XML_ERR_OK) {
1094 crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
1095 if (xml != NULL) {
1096 crm_log_xml_err(xml, "Partial");
1097 }
1098 }
1099 }
1100
1101 xmlFreeParserCtxt(ctxt);
1102 return xml;
1103 }
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113 const char *
1114 pcmk__xe_add_last_written(xmlNode *xe)
1115 {
1116 const char *now_str = pcmk__epoch2str(NULL);
1117
1118 return crm_xml_add(xe, XML_CIB_ATTR_WRITTEN,
1119 now_str ? now_str : "Could not determine current time");
1120 }
1121
1122
1123
1124
1125
1126
1127 void
1128 crm_xml_sanitize_id(char *id)
1129 {
1130 char *c;
1131
1132 for (c = id; *c; ++c) {
1133
1134 switch (*c) {
1135 case ':':
1136 case '#':
1137 *c = '.';
1138 }
1139 }
1140 }
1141
1142
1143
1144
1145
1146
1147
1148
1149 void
1150 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1151 {
1152 va_list ap;
1153 int len = 0;
1154 char *id = NULL;
1155
1156
1157 va_start(ap, format);
1158 len = vasprintf(&id, format, ap);
1159 va_end(ap);
1160 CRM_ASSERT(len > 0);
1161
1162 crm_xml_sanitize_id(id);
1163 crm_xml_add(xml, XML_ATTR_ID, id);
1164 free(id);
1165 }
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179 static int
1180 write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
1181 bool compress, unsigned int *nbytes)
1182 {
1183 int rc = pcmk_rc_ok;
1184 char *buffer = NULL;
1185
1186 *nbytes = 0;
1187 crm_log_xml_trace(xml_node, "writing");
1188
1189 buffer = dump_xml_formatted(xml_node);
1190 CRM_CHECK(buffer && strlen(buffer),
1191 crm_log_xml_warn(xml_node, "formatting failed");
1192 rc = pcmk_rc_error;
1193 goto bail);
1194
1195 if (compress) {
1196 unsigned int in = 0;
1197 BZFILE *bz_file = NULL;
1198
1199 rc = BZ_OK;
1200 bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
1201 if (rc != BZ_OK) {
1202 crm_warn("Not compressing %s: could not prepare file stream: %s "
1203 CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
1204 } else {
1205 BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
1206 if (rc != BZ_OK) {
1207 crm_warn("Not compressing %s: could not compress data: %s "
1208 CRM_XS " bzerror=%d errno=%d",
1209 filename, bz2_strerror(rc), rc, errno);
1210 }
1211 }
1212
1213 if (rc == BZ_OK) {
1214 BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
1215 if (rc != BZ_OK) {
1216 crm_warn("Not compressing %s: could not write compressed data: %s "
1217 CRM_XS " bzerror=%d errno=%d",
1218 filename, bz2_strerror(rc), rc, errno);
1219 *nbytes = 0;
1220 } else {
1221 crm_trace("Compressed XML for %s from %u bytes to %u",
1222 filename, in, *nbytes);
1223 }
1224 }
1225 rc = pcmk_rc_ok;
1226 }
1227
1228 if (*nbytes == 0) {
1229 rc = fprintf(stream, "%s", buffer);
1230 if (rc < 0) {
1231 rc = errno;
1232 crm_perror(LOG_ERR, "writing %s", filename);
1233 } else {
1234 *nbytes = (unsigned int) rc;
1235 rc = pcmk_rc_ok;
1236 }
1237 }
1238
1239 bail:
1240
1241 if (fflush(stream) != 0) {
1242 rc = errno;
1243 crm_perror(LOG_ERR, "flushing %s", filename);
1244 }
1245
1246
1247 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
1248 rc = errno;
1249 crm_perror(LOG_ERR, "synchronizing %s", filename);
1250 }
1251
1252 fclose(stream);
1253
1254 crm_trace("Saved %d bytes to %s as XML", *nbytes, filename);
1255 free(buffer);
1256
1257 return rc;
1258 }
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270 int
1271 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
1272 {
1273 FILE *stream = NULL;
1274 unsigned int nbytes = 0;
1275 int rc = pcmk_rc_ok;
1276
1277 CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
1278 stream = fdopen(fd, "w");
1279 if (stream == NULL) {
1280 return -errno;
1281 }
1282 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1283 if (rc != pcmk_rc_ok) {
1284 return pcmk_rc2legacy(rc);
1285 }
1286 return (int) nbytes;
1287 }
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298 int
1299 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
1300 {
1301 FILE *stream = NULL;
1302 unsigned int nbytes = 0;
1303 int rc = pcmk_rc_ok;
1304
1305 CRM_CHECK(xml_node && filename, return -EINVAL);
1306 stream = fopen(filename, "w");
1307 if (stream == NULL) {
1308 return -errno;
1309 }
1310 rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
1311 if (rc != pcmk_rc_ok) {
1312 return pcmk_rc2legacy(rc);
1313 }
1314 return (int) nbytes;
1315 }
1316
1317
1318 static char *
1319 replace_text(char *text, int start, size_t *length, const char *replace)
1320 {
1321 size_t offset = strlen(replace) - 1;
1322
1323 *length += offset;
1324 text = pcmk__realloc(text, *length);
1325
1326 for (size_t 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
1335
1336
1337
1338
1339
1340
1341
1342
1343 char *
1344 crm_xml_escape(const char *text)
1345 {
1346 size_t length;
1347 char *copy;
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364 if (text == NULL) {
1365 return NULL;
1366 }
1367
1368 length = 1 + strlen(text);
1369 copy = strdup(text);
1370 CRM_ASSERT(copy != NULL);
1371 for (size_t index = 0; index < length; index++) {
1372 switch (copy[index]) {
1373 case 0:
1374 break;
1375 case '<':
1376 copy = replace_text(copy, index, &length, "<");
1377 break;
1378 case '>':
1379 copy = replace_text(copy, index, &length, ">");
1380 break;
1381 case '"':
1382 copy = replace_text(copy, index, &length, """);
1383 break;
1384 case '\'':
1385 copy = replace_text(copy, index, &length, "'");
1386 break;
1387 case '&':
1388 copy = replace_text(copy, index, &length, "&");
1389 break;
1390 case '\t':
1391
1392 copy = replace_text(copy, index, &length, " ");
1393 break;
1394 case '\n':
1395 copy = replace_text(copy, index, &length, "\\n");
1396 break;
1397 case '\r':
1398 copy = replace_text(copy, index, &length, "\\r");
1399 break;
1400 default:
1401
1402 if(copy[index] < ' ' || copy[index] > '~') {
1403 char *replace = crm_strdup_printf("\\%.3o", copy[index]);
1404
1405 copy = replace_text(copy, index, &length, replace);
1406 free(replace);
1407 }
1408 }
1409 }
1410 return copy;
1411 }
1412
1413 static inline void
1414 dump_xml_attr(xmlAttrPtr attr, int options, char **buffer, int *offset, int *max)
1415 {
1416 char *p_value = NULL;
1417 const char *p_name = NULL;
1418 xml_private_t *p = NULL;
1419
1420 CRM_ASSERT(buffer != NULL);
1421 if (attr == NULL || attr->children == NULL) {
1422 return;
1423 }
1424
1425 p = attr->_private;
1426 if (p && pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1427 return;
1428 }
1429
1430 p_name = (const char *)attr->name;
1431 p_value = crm_xml_escape((const char *)attr->children->content);
1432 buffer_print(*buffer, *max, *offset, " %s=\"%s\"",
1433 p_name, crm_str(p_value));
1434 free(p_value);
1435 }
1436
1437
1438 void
1439 pcmk__xe_log(int log_level, const char *file, const char *function, int line,
1440 const char *prefix, xmlNode *data, int depth, int options)
1441 {
1442 int max = 0;
1443 int offset = 0;
1444 const char *name = NULL;
1445 const char *hidden = NULL;
1446
1447 xmlNode *child = NULL;
1448
1449 if ((data == NULL) || (log_level == LOG_NEVER)) {
1450 return;
1451 }
1452
1453 name = crm_element_name(data);
1454
1455 if (pcmk_is_set(options, xml_log_option_open)) {
1456 char *buffer = NULL;
1457
1458 insert_prefix(options, &buffer, &offset, &max, depth);
1459
1460 if (data->type == XML_COMMENT_NODE) {
1461 buffer_print(buffer, max, offset, "<!--%s-->", data->content);
1462
1463 } else {
1464 buffer_print(buffer, max, offset, "<%s", name);
1465
1466 hidden = crm_element_value(data, "hidden");
1467 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL;
1468 a = a->next) {
1469
1470 xml_private_t *p = a->_private;
1471 const char *p_name = (const char *) a->name;
1472 const char *p_value = pcmk__xml_attr_value(a);
1473 char *p_copy = NULL;
1474
1475 if (pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1476 continue;
1477 } else if (pcmk_any_flags_set(options,
1478 xml_log_option_diff_plus
1479 |xml_log_option_diff_minus)
1480 && (strcmp(XML_DIFF_MARKER, p_name) == 0)) {
1481 continue;
1482
1483 } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
1484 p_copy = strdup("*****");
1485
1486 } else {
1487 p_copy = crm_xml_escape(p_value);
1488 }
1489
1490 buffer_print(buffer, max, offset, " %s=\"%s\"",
1491 p_name, crm_str(p_copy));
1492 free(p_copy);
1493 }
1494
1495 if(xml_has_children(data) == FALSE) {
1496 buffer_print(buffer, max, offset, "/>");
1497
1498 } else if (pcmk_is_set(options, xml_log_option_children)) {
1499 buffer_print(buffer, max, offset, ">");
1500
1501 } else {
1502 buffer_print(buffer, max, offset, "/>");
1503 }
1504 }
1505
1506 do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
1507 free(buffer);
1508 }
1509
1510 if(data->type == XML_COMMENT_NODE) {
1511 return;
1512
1513 } else if(xml_has_children(data) == FALSE) {
1514 return;
1515
1516 } else if (pcmk_is_set(options, xml_log_option_children)) {
1517 offset = 0;
1518 max = 0;
1519
1520 for (child = pcmk__xml_first_child(data); child != NULL;
1521 child = pcmk__xml_next(child)) {
1522 pcmk__xe_log(log_level, file, function, line, prefix, child,
1523 depth + 1,
1524 options|xml_log_option_open|xml_log_option_close);
1525 }
1526 }
1527
1528 if (pcmk_is_set(options, xml_log_option_close)) {
1529 char *buffer = NULL;
1530
1531 insert_prefix(options, &buffer, &offset, &max, depth);
1532 buffer_print(buffer, max, offset, "</%s>", name);
1533
1534 do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
1535 free(buffer);
1536 }
1537 }
1538
1539
1540 static void
1541 log_xml_changes(int log_level, const char *file, const char *function, int line,
1542 const char *prefix, xmlNode *data, int depth, int options)
1543 {
1544 xml_private_t *p;
1545 char *prefix_m = NULL;
1546 xmlNode *child = NULL;
1547
1548 if ((data == NULL) || (log_level == LOG_NEVER)) {
1549 return;
1550 }
1551
1552 p = data->_private;
1553
1554 prefix_m = strdup(prefix);
1555 prefix_m[1] = '+';
1556
1557 if (pcmk_all_flags_set(p->flags, pcmk__xf_dirty|pcmk__xf_created)) {
1558
1559 pcmk__xe_log(log_level, file, function, line, prefix_m, data, depth,
1560 options|xml_log_option_open|xml_log_option_close
1561 |xml_log_option_children);
1562
1563 } else if (pcmk_is_set(p->flags, pcmk__xf_dirty)) {
1564 char *spaces = calloc(80, 1);
1565 int s_count = 0, s_max = 80;
1566 char *prefix_del = NULL;
1567 char *prefix_moved = NULL;
1568 const char *flags = prefix;
1569
1570 insert_prefix(options, &spaces, &s_count, &s_max, depth);
1571 prefix_del = strdup(prefix);
1572 prefix_del[0] = '-';
1573 prefix_del[1] = '-';
1574 prefix_moved = strdup(prefix);
1575 prefix_moved[1] = '~';
1576
1577 if (pcmk_is_set(p->flags, pcmk__xf_moved)) {
1578 flags = prefix_moved;
1579 } else {
1580 flags = prefix;
1581 }
1582
1583 pcmk__xe_log(log_level, file, function, line, flags, data, depth,
1584 options|xml_log_option_open);
1585
1586 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1587 const char *aname = (const char*) a->name;
1588
1589 p = a->_private;
1590 if (pcmk_is_set(p->flags, pcmk__xf_deleted)) {
1591 const char *value = crm_element_value(data, aname);
1592 flags = prefix_del;
1593 do_crm_log_alias(log_level, file, function, line,
1594 "%s %s @%s=%s", flags, spaces, aname, value);
1595
1596 } else if (pcmk_is_set(p->flags, pcmk__xf_dirty)) {
1597 const char *value = crm_element_value(data, aname);
1598
1599 if (pcmk_is_set(p->flags, pcmk__xf_created)) {
1600 flags = prefix_m;
1601
1602 } else if (pcmk_is_set(p->flags, pcmk__xf_modified)) {
1603 flags = prefix;
1604
1605 } else if (pcmk_is_set(p->flags, pcmk__xf_moved)) {
1606 flags = prefix_moved;
1607
1608 } else {
1609 flags = prefix;
1610 }
1611 do_crm_log_alias(log_level, file, function, line,
1612 "%s %s @%s=%s", flags, spaces, aname, value);
1613 }
1614 }
1615 free(prefix_moved);
1616 free(prefix_del);
1617 free(spaces);
1618
1619 for (child = pcmk__xml_first_child(data); child != NULL;
1620 child = pcmk__xml_next(child)) {
1621 log_xml_changes(log_level, file, function, line, prefix, child,
1622 depth + 1, options);
1623 }
1624
1625 pcmk__xe_log(log_level, file, function, line, prefix, data, depth,
1626 options|xml_log_option_close);
1627
1628 } else {
1629 for (child = pcmk__xml_first_child(data); child != NULL;
1630 child = pcmk__xml_next(child)) {
1631 log_xml_changes(log_level, file, function, line, prefix, child,
1632 depth + 1, options);
1633 }
1634 }
1635
1636 free(prefix_m);
1637
1638 }
1639
1640 void
1641 log_data_element(int log_level, const char *file, const char *function, int line,
1642 const char *prefix, xmlNode * data, int depth, int options)
1643 {
1644 xmlNode *a_child = NULL;
1645
1646 char *prefix_m = NULL;
1647
1648 if (log_level == LOG_NEVER) {
1649 return;
1650 }
1651
1652 if (prefix == NULL) {
1653 prefix = "";
1654 }
1655
1656
1657 if (data == NULL) {
1658 do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
1659 "No data to dump as XML");
1660 return;
1661 }
1662
1663 if (pcmk_is_set(options, xml_log_option_dirty_add)) {
1664 log_xml_changes(log_level, file, function, line, prefix, data, depth,
1665 options);
1666 return;
1667 }
1668
1669 if (pcmk_is_set(options, xml_log_option_formatted)) {
1670 if (pcmk_is_set(options, xml_log_option_diff_plus)
1671 && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1672 options |= xml_log_option_diff_all;
1673 prefix_m = strdup(prefix);
1674 prefix_m[1] = '+';
1675 prefix = prefix_m;
1676
1677 } else if (pcmk_is_set(options, xml_log_option_diff_minus)
1678 && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
1679 options |= xml_log_option_diff_all;
1680 prefix_m = strdup(prefix);
1681 prefix_m[1] = '-';
1682 prefix = prefix_m;
1683 }
1684 }
1685
1686 if (pcmk_is_set(options, xml_log_option_diff_short)
1687 && !pcmk_is_set(options, xml_log_option_diff_all)) {
1688
1689 for (a_child = pcmk__xml_first_child(data); a_child != NULL;
1690 a_child = pcmk__xml_next(a_child)) {
1691 log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
1692 }
1693 } else {
1694 pcmk__xe_log(log_level, file, function, line, prefix, data, depth,
1695 options|xml_log_option_open|xml_log_option_close
1696 |xml_log_option_children);
1697 }
1698 free(prefix_m);
1699 }
1700
1701 static void
1702 dump_filtered_xml(xmlNode * data, int options, char **buffer, int *offset, int *max)
1703 {
1704 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1705 if (!pcmk__xa_filterable((const char *) (a->name))) {
1706 dump_xml_attr(a, options, buffer, offset, max);
1707 }
1708 }
1709 }
1710
1711 static void
1712 dump_xml_element(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1713 {
1714 const char *name = NULL;
1715
1716 CRM_ASSERT(max != NULL);
1717 CRM_ASSERT(offset != NULL);
1718 CRM_ASSERT(buffer != NULL);
1719
1720 if (data == NULL) {
1721 crm_trace("Nothing to dump");
1722 return;
1723 }
1724
1725 if (*buffer == NULL) {
1726 *offset = 0;
1727 *max = 0;
1728 }
1729
1730 name = crm_element_name(data);
1731 CRM_ASSERT(name != NULL);
1732
1733 insert_prefix(options, buffer, offset, max, depth);
1734 buffer_print(*buffer, *max, *offset, "<%s", name);
1735
1736 if (options & xml_log_option_filtered) {
1737 dump_filtered_xml(data, options, buffer, offset, max);
1738
1739 } else {
1740 for (xmlAttrPtr a = pcmk__xe_first_attr(data); a != NULL; a = a->next) {
1741 dump_xml_attr(a, options, buffer, offset, max);
1742 }
1743 }
1744
1745 if (data->children == NULL) {
1746 buffer_print(*buffer, *max, *offset, "/>");
1747
1748 } else {
1749 buffer_print(*buffer, *max, *offset, ">");
1750 }
1751
1752 if (options & xml_log_option_formatted) {
1753 buffer_print(*buffer, *max, *offset, "\n");
1754 }
1755
1756 if (data->children) {
1757 xmlNode *xChild = NULL;
1758 for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
1759 pcmk__xml2text(xChild, options, buffer, offset, max, depth + 1);
1760 }
1761
1762 insert_prefix(options, buffer, offset, max, depth);
1763 buffer_print(*buffer, *max, *offset, "</%s>", name);
1764
1765 if (options & xml_log_option_formatted) {
1766 buffer_print(*buffer, *max, *offset, "\n");
1767 }
1768 }
1769 }
1770
1771 static void
1772 dump_xml_text(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1773 {
1774 CRM_ASSERT(max != NULL);
1775 CRM_ASSERT(offset != NULL);
1776 CRM_ASSERT(buffer != NULL);
1777
1778 if (data == NULL) {
1779 crm_trace("Nothing to dump");
1780 return;
1781 }
1782
1783 if (*buffer == NULL) {
1784 *offset = 0;
1785 *max = 0;
1786 }
1787
1788 insert_prefix(options, buffer, offset, max, depth);
1789
1790 buffer_print(*buffer, *max, *offset, "%s", data->content);
1791
1792 if (options & xml_log_option_formatted) {
1793 buffer_print(*buffer, *max, *offset, "\n");
1794 }
1795 }
1796
1797 static void
1798 dump_xml_cdata(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1799 {
1800 CRM_ASSERT(max != NULL);
1801 CRM_ASSERT(offset != NULL);
1802 CRM_ASSERT(buffer != NULL);
1803
1804 if (data == NULL) {
1805 crm_trace("Nothing to dump");
1806 return;
1807 }
1808
1809 if (*buffer == NULL) {
1810 *offset = 0;
1811 *max = 0;
1812 }
1813
1814 insert_prefix(options, buffer, offset, max, depth);
1815
1816 buffer_print(*buffer, *max, *offset, "<![CDATA[");
1817 buffer_print(*buffer, *max, *offset, "%s", data->content);
1818 buffer_print(*buffer, *max, *offset, "]]>");
1819
1820 if (options & xml_log_option_formatted) {
1821 buffer_print(*buffer, *max, *offset, "\n");
1822 }
1823 }
1824
1825 static void
1826 dump_xml_comment(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
1827 {
1828 CRM_ASSERT(max != NULL);
1829 CRM_ASSERT(offset != NULL);
1830 CRM_ASSERT(buffer != NULL);
1831
1832 if (data == NULL) {
1833 crm_trace("Nothing to dump");
1834 return;
1835 }
1836
1837 if (*buffer == NULL) {
1838 *offset = 0;
1839 *max = 0;
1840 }
1841
1842 insert_prefix(options, buffer, offset, max, depth);
1843
1844 buffer_print(*buffer, *max, *offset, "<!--");
1845 buffer_print(*buffer, *max, *offset, "%s", data->content);
1846 buffer_print(*buffer, *max, *offset, "-->");
1847
1848 if (options & xml_log_option_formatted) {
1849 buffer_print(*buffer, *max, *offset, "\n");
1850 }
1851 }
1852
1853 #define PCMK__XMLDUMP_STATS 0
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866 void
1867 pcmk__xml2text(xmlNode *data, int options, char **buffer, int *offset,
1868 int *max, int depth)
1869 {
1870 if(data == NULL) {
1871 *offset = 0;
1872 *max = 0;
1873 return;
1874 }
1875
1876 if (!pcmk_is_set(options, xml_log_option_filtered)
1877 && pcmk_is_set(options, xml_log_option_full_fledged)) {
1878
1879
1880
1881
1882
1883
1884
1885 #if (PCMK__XMLDUMP_STATS - 0)
1886 time_t next, new = time(NULL);
1887 #endif
1888 xmlDoc *doc;
1889 xmlOutputBuffer *xml_buffer;
1890
1891 doc = getDocPtr(data);
1892
1893 CRM_CHECK(doc != NULL, return);
1894
1895 xml_buffer = xmlAllocOutputBuffer(NULL);
1896 CRM_ASSERT(xml_buffer != NULL);
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908 xmlNodeDumpOutput(xml_buffer, doc, data, 0,
1909 (options & xml_log_option_formatted), NULL);
1910
1911 (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
1912 if (xml_buffer->buffer != NULL) {
1913 buffer_print(*buffer, *max, *offset, "%s",
1914 (char *) xmlBufContent(xml_buffer->buffer));
1915 }
1916
1917 #if (PCMK__XMLDUMP_STATS - 0)
1918 next = time(NULL);
1919 if ((now + 1) < next) {
1920 crm_log_xml_trace(data, "Long time");
1921 crm_err("xmlNodeDump() -> %dbytes took %ds", *max, next - now);
1922 }
1923 #endif
1924
1925
1926 (void) xmlOutputBufferClose(xml_buffer);
1927 return;
1928 }
1929
1930 switch(data->type) {
1931 case XML_ELEMENT_NODE:
1932
1933 dump_xml_element(data, options, buffer, offset, max, depth);
1934 break;
1935 case XML_TEXT_NODE:
1936
1937 if (options & xml_log_option_text) {
1938 dump_xml_text(data, options, buffer, offset, max, depth);
1939 }
1940 return;
1941 case XML_COMMENT_NODE:
1942 dump_xml_comment(data, options, buffer, offset, max, depth);
1943 break;
1944 case XML_CDATA_SECTION_NODE:
1945 dump_xml_cdata(data, options, buffer, offset, max, depth);
1946 break;
1947 default:
1948 crm_warn("Unhandled type: %d", data->type);
1949 return;
1950
1951
1952
1953
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 void
1984 pcmk__buffer_add_char(char **buffer, int *offset, int *max, char c)
1985 {
1986 buffer_print(*buffer, *max, *offset, "%c", c);
1987 }
1988
1989 char *
1990 dump_xml_formatted_with_text(xmlNode * an_xml_node)
1991 {
1992 char *buffer = NULL;
1993 int offset = 0, max = 0;
1994
1995 pcmk__xml2text(an_xml_node,
1996 xml_log_option_formatted|xml_log_option_full_fledged,
1997 &buffer, &offset, &max, 0);
1998 return buffer;
1999 }
2000
2001 char *
2002 dump_xml_formatted(xmlNode * an_xml_node)
2003 {
2004 char *buffer = NULL;
2005 int offset = 0, max = 0;
2006
2007 pcmk__xml2text(an_xml_node, xml_log_option_formatted, &buffer, &offset,
2008 &max, 0);
2009 return buffer;
2010 }
2011
2012 char *
2013 dump_xml_unformatted(xmlNode * an_xml_node)
2014 {
2015 char *buffer = NULL;
2016 int offset = 0, max = 0;
2017
2018 pcmk__xml2text(an_xml_node, 0, &buffer, &offset, &max, 0);
2019 return buffer;
2020 }
2021
2022 gboolean
2023 xml_has_children(const xmlNode * xml_root)
2024 {
2025 if (xml_root != NULL && xml_root->children != NULL) {
2026 return TRUE;
2027 }
2028 return FALSE;
2029 }
2030
2031 void
2032 xml_remove_prop(xmlNode * obj, const char *name)
2033 {
2034 if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
2035 crm_trace("Cannot remove %s from %s", name, obj->name);
2036
2037 } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
2038
2039 xml_private_t *p = NULL;
2040 xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
2041
2042 p = attr->_private;
2043 set_parent_flag(obj, pcmk__xf_dirty);
2044 pcmk__set_xml_flags(p, pcmk__xf_deleted);
2045 } else {
2046 xmlUnsetProp(obj, (pcmkXmlStr) name);
2047 }
2048 }
2049
2050 void
2051 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
2052 {
2053 char *f = NULL;
2054
2055 if (filename == NULL) {
2056 char *uuid = crm_generate_uuid();
2057
2058 f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
2059 filename = f;
2060 free(uuid);
2061 }
2062
2063 crm_info("Saving %s to %s", desc, filename);
2064 write_xml_file(xml, filename, FALSE);
2065 free(f);
2066 }
2067
2068
2069
2070
2071
2072
2073
2074
2075 static void
2076 set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
2077 {
2078 for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
2079 pcmk__set_xml_flags((xml_private_t *) (attr->_private), flag);
2080 }
2081 }
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092 static void
2093 mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
2094 const char *old_value)
2095 {
2096 xml_private_t *p = new_xml->doc->_private;
2097 xmlAttr *attr = NULL;
2098
2099
2100 pcmk__clear_xml_flags(p, pcmk__xf_tracking);
2101
2102
2103 attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2104 pcmk__set_xml_flags(p, pcmk__xf_tracking);
2105
2106
2107 p = attr->_private;
2108 p->flags = 0;
2109
2110
2111 xml_remove_prop(new_xml, attr_name);
2112
2113 crm_trace("XML attribute %s=%s was removed from %s",
2114 attr_name, old_value, element);
2115 }
2116
2117
2118
2119
2120
2121 static void
2122 mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
2123 const char *old_value)
2124 {
2125 char *vcopy = crm_element_value_copy(new_xml, attr_name);
2126
2127 crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
2128 attr_name, old_value, vcopy, element);
2129
2130
2131 xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
2132
2133
2134 crm_xml_add(new_xml, attr_name, vcopy);
2135 free(vcopy);
2136 }
2137
2138
2139
2140
2141
2142 static void
2143 mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
2144 xmlAttr *new_attr, int p_old, int p_new)
2145 {
2146 xml_private_t *p = new_attr->_private;
2147
2148 crm_trace("XML attribute %s moved from position %d to %d in %s",
2149 old_attr->name, p_old, p_new, element);
2150
2151
2152 mark_xml_node_dirty(new_xml);
2153
2154
2155 pcmk__set_xml_flags(p, pcmk__xf_dirty|pcmk__xf_moved);
2156
2157 p = (p_old > p_new)? old_attr->_private : new_attr->_private;
2158 pcmk__set_xml_flags(p, pcmk__xf_skip);
2159 }
2160
2161
2162
2163
2164
2165 static void
2166 xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
2167 {
2168 xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
2169
2170 while (attr_iter != NULL) {
2171 xmlAttr *old_attr = attr_iter;
2172 xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
2173 const char *name = (const char *) attr_iter->name;
2174 const char *old_value = crm_element_value(old_xml, name);
2175
2176 attr_iter = attr_iter->next;
2177 if (new_attr == NULL) {
2178 mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
2179 old_value);
2180
2181 } else {
2182 xml_private_t *p = new_attr->_private;
2183 int new_pos = pcmk__xml_position((xmlNode*) new_attr,
2184 pcmk__xf_skip);
2185 int old_pos = pcmk__xml_position((xmlNode*) old_attr,
2186 pcmk__xf_skip);
2187 const char *new_value = crm_element_value(new_xml, name);
2188
2189
2190 pcmk__clear_xml_flags(p, pcmk__xf_created);
2191
2192 if (strcmp(new_value, old_value) != 0) {
2193 mark_attr_changed(new_xml, (const char *) old_xml->name, name,
2194 old_value);
2195
2196 } else if ((old_pos != new_pos)
2197 && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
2198 mark_attr_moved(new_xml, (const char *) old_xml->name,
2199 old_attr, new_attr, old_pos, new_pos);
2200 }
2201 }
2202 }
2203 }
2204
2205
2206
2207
2208
2209 static void
2210 mark_created_attrs(xmlNode *new_xml)
2211 {
2212 xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
2213
2214 while (attr_iter != NULL) {
2215 xmlAttr *new_attr = attr_iter;
2216 xml_private_t *p = attr_iter->_private;
2217
2218 attr_iter = attr_iter->next;
2219 if (pcmk_is_set(p->flags, pcmk__xf_created)) {
2220 const char *attr_name = (const char *) new_attr->name;
2221
2222 crm_trace("Created new attribute %s=%s in %s",
2223 attr_name, crm_element_value(new_xml, attr_name),
2224 new_xml->name);
2225
2226
2227
2228
2229 if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
2230 pcmk__mark_xml_attr_dirty(new_attr);
2231 } else {
2232
2233 xmlUnsetProp(new_xml, new_attr->name);
2234 }
2235 }
2236 }
2237 }
2238
2239
2240
2241
2242
2243 static void
2244 xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
2245 {
2246 set_attrs_flag(new_xml, pcmk__xf_created);
2247 xml_diff_old_attrs(old_xml, new_xml);
2248 mark_created_attrs(new_xml);
2249 }
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260 static void
2261 mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
2262 {
2263
2264 xmlNode *candidate = add_node_copy(new_parent, old_child);
2265
2266
2267 reset_xml_node_flags(candidate);
2268
2269
2270 pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
2271
2272
2273 free_xml_with_position(candidate,
2274 pcmk__xml_position(old_child, pcmk__xf_skip));
2275
2276 if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
2277 pcmk__set_xml_flags((xml_private_t *) (old_child->_private),
2278 pcmk__xf_skip);
2279 }
2280 }
2281
2282 static void
2283 mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
2284 int p_old, int p_new)
2285 {
2286 xml_private_t *p = new_child->_private;
2287
2288 crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
2289 new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
2290 p_old, p_new, new_parent->name);
2291 mark_xml_node_dirty(new_parent);
2292 pcmk__set_xml_flags(p, pcmk__xf_moved);
2293
2294 if (p_old > p_new) {
2295 p = old_child->_private;
2296 } else {
2297 p = new_child->_private;
2298 }
2299 pcmk__set_xml_flags(p, pcmk__xf_skip);
2300 }
2301
2302
2303 static void
2304 mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
2305 {
2306 xmlNode *cIter = NULL;
2307 xml_private_t *p = NULL;
2308
2309 CRM_CHECK(new_xml != NULL, return);
2310 if (old_xml == NULL) {
2311 pcmk__mark_xml_created(new_xml);
2312 pcmk__apply_creation_acl(new_xml, check_top);
2313 return;
2314 }
2315
2316 p = new_xml->_private;
2317 CRM_CHECK(p != NULL, return);
2318
2319 if(p->flags & pcmk__xf_processed) {
2320
2321 return;
2322 }
2323 pcmk__set_xml_flags(p, pcmk__xf_processed);
2324
2325 xml_diff_attrs(old_xml, new_xml);
2326
2327
2328 for (cIter = pcmk__xml_first_child(old_xml); cIter != NULL; ) {
2329 xmlNode *old_child = cIter;
2330 xmlNode *new_child = pcmk__xml_match(new_xml, cIter, true);
2331
2332 cIter = pcmk__xml_next(cIter);
2333 if(new_child) {
2334 mark_xml_changes(old_child, new_child, TRUE);
2335
2336 } else {
2337 mark_child_deleted(old_child, new_xml);
2338 }
2339 }
2340
2341
2342 for (cIter = pcmk__xml_first_child(new_xml); cIter != NULL; ) {
2343 xmlNode *new_child = cIter;
2344 xmlNode *old_child = pcmk__xml_match(old_xml, cIter, true);
2345
2346 cIter = pcmk__xml_next(cIter);
2347 if(old_child == NULL) {
2348
2349 p = new_child->_private;
2350 pcmk__set_xml_flags(p, pcmk__xf_skip);
2351 mark_xml_changes(old_child, new_child, TRUE);
2352
2353 } else {
2354
2355 int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
2356 int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
2357
2358 if(p_old != p_new) {
2359 mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
2360 }
2361 }
2362 }
2363 }
2364
2365 void
2366 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
2367 {
2368 pcmk__set_xml_doc_flag(new_xml, pcmk__xf_lazy);
2369 xml_calculate_changes(old_xml, new_xml);
2370 }
2371
2372 void
2373 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
2374 {
2375 CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
2376 return);
2377 CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
2378
2379 if(xml_tracking_changes(new_xml) == FALSE) {
2380 xml_track_changes(new_xml, NULL, NULL, FALSE);
2381 }
2382
2383 mark_xml_changes(old_xml, new_xml, FALSE);
2384 }
2385
2386 gboolean
2387 can_prune_leaf(xmlNode * xml_node)
2388 {
2389 xmlNode *cIter = NULL;
2390 gboolean can_prune = TRUE;
2391 const char *name = crm_element_name(xml_node);
2392
2393 if (pcmk__strcase_any_of(name, XML_TAG_RESOURCE_REF, XML_CIB_TAG_OBJ_REF,
2394 XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1, NULL)) {
2395 return FALSE;
2396 }
2397
2398 for (xmlAttrPtr a = pcmk__xe_first_attr(xml_node); a != NULL; a = a->next) {
2399 const char *p_name = (const char *) a->name;
2400
2401 if (strcmp(p_name, XML_ATTR_ID) == 0) {
2402 continue;
2403 }
2404 can_prune = FALSE;
2405 }
2406
2407 cIter = pcmk__xml_first_child(xml_node);
2408 while (cIter) {
2409 xmlNode *child = cIter;
2410
2411 cIter = pcmk__xml_next(cIter);
2412 if (can_prune_leaf(child)) {
2413 free_xml(child);
2414 } else {
2415 can_prune = FALSE;
2416 }
2417 }
2418 return can_prune;
2419 }
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429 xmlNode *
2430 pcmk__xc_match(xmlNode *root, xmlNode *search_comment, bool exact)
2431 {
2432 xmlNode *a_child = NULL;
2433 int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
2434
2435 CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
2436
2437 for (a_child = pcmk__xml_first_child(root); a_child != NULL;
2438 a_child = pcmk__xml_next(a_child)) {
2439 if (exact) {
2440 int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
2441 xml_private_t *p = a_child->_private;
2442
2443 if (offset < search_offset) {
2444 continue;
2445
2446 } else if (offset > search_offset) {
2447 return NULL;
2448 }
2449
2450 if (pcmk_is_set(p->flags, pcmk__xf_skip)) {
2451 continue;
2452 }
2453 }
2454
2455 if (a_child->type == XML_COMMENT_NODE
2456 && pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
2457 return a_child;
2458
2459 } else if (exact) {
2460 return NULL;
2461 }
2462 }
2463
2464 return NULL;
2465 }
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478 void
2479 pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
2480 {
2481 CRM_CHECK(update != NULL, return);
2482 CRM_CHECK(update->type == XML_COMMENT_NODE, return);
2483
2484 if (target == NULL) {
2485 target = pcmk__xc_match(parent, update, false);
2486 }
2487
2488 if (target == NULL) {
2489 add_node_copy(parent, update);
2490
2491 } else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
2492 xmlFree(target->content);
2493 target->content = xmlStrdup(update->content);
2494 }
2495 }
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509 void
2510 pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
2511 bool as_diff)
2512 {
2513 xmlNode *a_child = NULL;
2514 const char *object_name = NULL,
2515 *object_href = NULL,
2516 *object_href_val = NULL;
2517
2518 #if XML_PARSER_DEBUG
2519 crm_log_xml_trace("update:", update);
2520 crm_log_xml_trace("target:", target);
2521 #endif
2522
2523 CRM_CHECK(update != NULL, return);
2524
2525 if (update->type == XML_COMMENT_NODE) {
2526 pcmk__xc_update(parent, target, update);
2527 return;
2528 }
2529
2530 object_name = crm_element_name(update);
2531 object_href_val = ID(update);
2532 if (object_href_val != NULL) {
2533 object_href = XML_ATTR_ID;
2534 } else {
2535 object_href_val = crm_element_value(update, XML_ATTR_IDREF);
2536 object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
2537 }
2538
2539 CRM_CHECK(object_name != NULL, return);
2540 CRM_CHECK(target != NULL || parent != NULL, return);
2541
2542 if (target == NULL) {
2543 target = pcmk__xe_match(parent, object_name,
2544 object_href, object_href_val);
2545 }
2546
2547 if (target == NULL) {
2548 target = create_xml_node(parent, object_name);
2549 CRM_CHECK(target != NULL, return);
2550 #if XML_PARSER_DEBUG
2551 crm_trace("Added <%s%s%s%s%s/>", crm_str(object_name),
2552 object_href ? " " : "",
2553 object_href ? object_href : "",
2554 object_href ? "=" : "",
2555 object_href ? object_href_val : "");
2556
2557 } else {
2558 crm_trace("Found node <%s%s%s%s%s/> to update", crm_str(object_name),
2559 object_href ? " " : "",
2560 object_href ? object_href : "",
2561 object_href ? "=" : "",
2562 object_href ? object_href_val : "");
2563 #endif
2564 }
2565
2566 CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
2567 pcmk__str_casei),
2568 return);
2569
2570 if (as_diff == FALSE) {
2571
2572 copy_in_properties(target, update);
2573
2574 } else {
2575
2576 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2577 a = a->next) {
2578 const char *p_value = pcmk__xml_attr_value(a);
2579
2580
2581 xmlUnsetProp(target, a->name);
2582 xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
2583 }
2584 }
2585
2586 for (a_child = pcmk__xml_first_child(update); a_child != NULL;
2587 a_child = pcmk__xml_next(a_child)) {
2588 #if XML_PARSER_DEBUG
2589 crm_trace("Updating child <%s%s%s%s%s/>", crm_str(object_name),
2590 object_href ? " " : "",
2591 object_href ? object_href : "",
2592 object_href ? "=" : "",
2593 object_href ? object_href_val : "");
2594 #endif
2595 pcmk__xml_update(target, NULL, a_child, as_diff);
2596 }
2597
2598 #if XML_PARSER_DEBUG
2599 crm_trace("Finished with <%s%s%s%s%s/>", crm_str(object_name),
2600 object_href ? " " : "",
2601 object_href ? object_href : "",
2602 object_href ? "=" : "",
2603 object_href ? object_href_val : "");
2604 #endif
2605 }
2606
2607 gboolean
2608 update_xml_child(xmlNode * child, xmlNode * to_update)
2609 {
2610 gboolean can_update = TRUE;
2611 xmlNode *child_of_child = NULL;
2612
2613 CRM_CHECK(child != NULL, return FALSE);
2614 CRM_CHECK(to_update != NULL, return FALSE);
2615
2616 if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
2617 can_update = FALSE;
2618
2619 } else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
2620 can_update = FALSE;
2621
2622 } else if (can_update) {
2623 #if XML_PARSER_DEBUG
2624 crm_log_xml_trace(child, "Update match found...");
2625 #endif
2626 pcmk__xml_update(NULL, child, to_update, false);
2627 }
2628
2629 for (child_of_child = pcmk__xml_first_child(child); child_of_child != NULL;
2630 child_of_child = pcmk__xml_next(child_of_child)) {
2631
2632 if (can_update) {
2633 break;
2634 }
2635 can_update = update_xml_child(child_of_child, to_update);
2636 }
2637
2638 return can_update;
2639 }
2640
2641 int
2642 find_xml_children(xmlNode ** children, xmlNode * root,
2643 const char *tag, const char *field, const char *value, gboolean search_matches)
2644 {
2645 int match_found = 0;
2646
2647 CRM_CHECK(root != NULL, return FALSE);
2648 CRM_CHECK(children != NULL, return FALSE);
2649
2650 if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
2651
2652 } else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
2653
2654 } else {
2655 if (*children == NULL) {
2656 *children = create_xml_node(NULL, __func__);
2657 }
2658 add_node_copy(*children, root);
2659 match_found = 1;
2660 }
2661
2662 if (search_matches || match_found == 0) {
2663 xmlNode *child = NULL;
2664
2665 for (child = pcmk__xml_first_child(root); child != NULL;
2666 child = pcmk__xml_next(child)) {
2667 match_found += find_xml_children(children, child, tag, field, value, search_matches);
2668 }
2669 }
2670
2671 return match_found;
2672 }
2673
2674 gboolean
2675 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
2676 {
2677 gboolean can_delete = FALSE;
2678 xmlNode *child_of_child = NULL;
2679
2680 const char *up_id = NULL;
2681 const char *child_id = NULL;
2682 const char *right_val = NULL;
2683
2684 CRM_CHECK(child != NULL, return FALSE);
2685 CRM_CHECK(update != NULL, return FALSE);
2686
2687 up_id = ID(update);
2688 child_id = ID(child);
2689
2690 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
2691 can_delete = TRUE;
2692 }
2693 if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
2694 can_delete = FALSE;
2695 }
2696 if (can_delete && delete_only) {
2697 for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
2698 a = a->next) {
2699 const char *p_name = (const char *) a->name;
2700 const char *p_value = pcmk__xml_attr_value(a);
2701
2702 right_val = crm_element_value(child, p_name);
2703 if (!pcmk__str_eq(p_value, right_val, pcmk__str_casei)) {
2704 can_delete = FALSE;
2705 }
2706 }
2707 }
2708
2709 if (can_delete && parent != NULL) {
2710 crm_log_xml_trace(child, "Delete match found...");
2711 if (delete_only || update == NULL) {
2712 free_xml(child);
2713
2714 } else {
2715 xmlNode *tmp = copy_xml(update);
2716 xmlDoc *doc = tmp->doc;
2717 xmlNode *old = NULL;
2718
2719 xml_accept_changes(tmp);
2720 old = xmlReplaceNode(child, tmp);
2721
2722 if(xml_tracking_changes(tmp)) {
2723
2724 pcmk__apply_acl(tmp);
2725 }
2726
2727 xml_calculate_changes(old, tmp);
2728 xmlDocSetRootElement(doc, old);
2729 free_xml(old);
2730 }
2731 child = NULL;
2732 return TRUE;
2733
2734 } else if (can_delete) {
2735 crm_log_xml_debug(child, "Cannot delete the search root");
2736 can_delete = FALSE;
2737 }
2738
2739 child_of_child = pcmk__xml_first_child(child);
2740 while (child_of_child) {
2741 xmlNode *next = pcmk__xml_next(child_of_child);
2742
2743 can_delete = replace_xml_child(child, child_of_child, update, delete_only);
2744
2745
2746 if (can_delete) {
2747 child_of_child = NULL;
2748 } else {
2749 child_of_child = next;
2750 }
2751 }
2752
2753 return can_delete;
2754 }
2755
2756 xmlNode *
2757 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
2758 {
2759 xmlNode *child = NULL;
2760 GSList *nvpairs = NULL;
2761 xmlNode *result = NULL;
2762 const char *name = NULL;
2763
2764 CRM_CHECK(input != NULL, return NULL);
2765
2766 name = crm_element_name(input);
2767 CRM_CHECK(name != NULL, return NULL);
2768
2769 result = create_xml_node(parent, name);
2770 nvpairs = pcmk_xml_attrs2nvpairs(input);
2771 nvpairs = pcmk_sort_nvpairs(nvpairs);
2772 pcmk_nvpairs2xml_attrs(nvpairs, result);
2773 pcmk_free_nvpairs(nvpairs);
2774
2775 for (child = pcmk__xml_first_child(input); child != NULL;
2776 child = pcmk__xml_next(child)) {
2777
2778 if (recursive) {
2779 sorted_xml(child, result, recursive);
2780 } else {
2781 add_node_copy(result, child);
2782 }
2783 }
2784
2785 return result;
2786 }
2787
2788 xmlNode *
2789 first_named_child(const xmlNode *parent, const char *name)
2790 {
2791 xmlNode *match = NULL;
2792
2793 for (match = pcmk__xe_first_child(parent); match != NULL;
2794 match = pcmk__xe_next(match)) {
2795
2796
2797
2798
2799
2800 if (pcmk__str_eq(name, (const char *)match->name, pcmk__str_null_matches)) {
2801 return match;
2802 }
2803 }
2804 return NULL;
2805 }
2806
2807
2808
2809
2810
2811
2812
2813
2814 xmlNode *
2815 crm_next_same_xml(const xmlNode *sibling)
2816 {
2817 xmlNode *match = pcmk__xe_next(sibling);
2818 const char *name = crm_element_name(sibling);
2819
2820 while (match != NULL) {
2821 if (!strcmp(crm_element_name(match), name)) {
2822 return match;
2823 }
2824 match = pcmk__xe_next(match);
2825 }
2826 return NULL;
2827 }
2828
2829 void
2830 crm_xml_init(void)
2831 {
2832 static bool init = true;
2833
2834 if(init) {
2835 init = false;
2836
2837
2838
2839
2840
2841 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
2842
2843
2844 xmlDeregisterNodeDefault(free_private_data);
2845 xmlRegisterNodeDefault(new_private_data);
2846
2847 crm_schema_init();
2848 }
2849 }
2850
2851 void
2852 crm_xml_cleanup(void)
2853 {
2854 crm_info("Cleaning up memory from libxml2");
2855 crm_schema_cleanup();
2856 xmlCleanupParser();
2857 }
2858
2859 #define XPATH_MAX 512
2860
2861 xmlNode *
2862 expand_idref(xmlNode * input, xmlNode * top)
2863 {
2864 const char *tag = NULL;
2865 const char *ref = NULL;
2866 xmlNode *result = input;
2867
2868 if (result == NULL) {
2869 return NULL;
2870
2871 } else if (top == NULL) {
2872 top = input;
2873 }
2874
2875 tag = crm_element_name(result);
2876 ref = crm_element_value(result, XML_ATTR_IDREF);
2877
2878 if (ref != NULL) {
2879 char *xpath_string = crm_strdup_printf("//%s[@id='%s']", tag, ref);
2880
2881 result = get_xpath_object(xpath_string, top, LOG_ERR);
2882 if (result == NULL) {
2883 char *nodePath = (char *)xmlGetNodePath(top);
2884
2885 crm_err("No match for %s found in %s: Invalid configuration", xpath_string,
2886 crm_str(nodePath));
2887 free(nodePath);
2888 }
2889 free(xpath_string);
2890 }
2891 return result;
2892 }
2893
2894 void
2895 crm_destroy_xml(gpointer data)
2896 {
2897 free_xml(data);
2898 }
2899
2900 char *
2901 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
2902 {
2903 static const char *base = NULL;
2904 char *ret = NULL;
2905
2906 if (base == NULL) {
2907 base = getenv("PCMK_schema_directory");
2908 }
2909 if (pcmk__str_empty(base)) {
2910 base = CRM_SCHEMA_DIRECTORY;
2911 }
2912
2913 switch (ns) {
2914 case pcmk__xml_artefact_ns_legacy_rng:
2915 case pcmk__xml_artefact_ns_legacy_xslt:
2916 ret = strdup(base);
2917 break;
2918 case pcmk__xml_artefact_ns_base_rng:
2919 case pcmk__xml_artefact_ns_base_xslt:
2920 ret = crm_strdup_printf("%s/base", base);
2921 break;
2922 default:
2923 crm_err("XML artefact family specified as %u not recognized", ns);
2924 }
2925 return ret;
2926 }
2927
2928 char *
2929 pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
2930 {
2931 char *base = pcmk__xml_artefact_root(ns), *ret = NULL;
2932
2933 switch (ns) {
2934 case pcmk__xml_artefact_ns_legacy_rng:
2935 case pcmk__xml_artefact_ns_base_rng:
2936 ret = crm_strdup_printf("%s/%s.rng", base, filespec);
2937 break;
2938 case pcmk__xml_artefact_ns_legacy_xslt:
2939 case pcmk__xml_artefact_ns_base_xslt:
2940 ret = crm_strdup_printf("%s/%s.xsl", base, filespec);
2941 break;
2942 default:
2943 crm_err("XML artefact family specified as %u not recognized", ns);
2944 }
2945 free(base);
2946
2947 return ret;
2948 }
2949
2950 void
2951 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
2952 {
2953 while (true) {
2954 const char *name, *value;
2955
2956 name = va_arg(pairs, const char *);
2957 if (name == NULL) {
2958 return;
2959 }
2960
2961 value = va_arg(pairs, const char *);
2962 if (value != NULL) {
2963 crm_xml_add(node, name, value);
2964 }
2965 }
2966 }
2967
2968 void
2969 pcmk__xe_set_props(xmlNodePtr node, ...)
2970 {
2971 va_list pairs;
2972 va_start(pairs, node);
2973 pcmk__xe_set_propv(node, pairs);
2974 va_end(pairs);
2975 }
2976
2977
2978
2979
2980 #include <crm/common/xml_compat.h>
2981
2982 xmlNode *
2983 find_entity(xmlNode *parent, const char *node_name, const char *id)
2984 {
2985 return pcmk__xe_match(parent, node_name,
2986 ((id == NULL)? id : XML_ATTR_ID), id);
2987 }
2988
2989
2990