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