This source file includes following definitions.
- TRACKING_CHANGES
- insert_prefix
- set_parent_flag
- set_doc_flag
- __xml_node_dirty
- __xml_node_clean
- crm_node_created
- crm_attr_dirty
- crm_attr_value
- crm_first_attr
- __xml_acl_free
- __xml_deleted_obj_free
- __xml_private_clean
- __xml_private_free
- pcmkDeregisterNode
- pcmkRegisterNode
- __xml_acl_create
- __xml_acl_parse_entry
- __xml_acl_to_text
- __xml_acl_apply
- __xml_acl_unpack
- __xml_acl_mode_test
- __xml_purge_attributes
- xml_acl_filtered_copy
- __xml_acl_post_process
- xml_acl_denied
- xml_acl_disable
- xml_acl_enabled
- xml_track_changes
- xml_tracking_changes
- xml_document_dirty
- __xml_offset
- __xml_offset_no_deletions
- __xml_build_changes
- __xml_accept_changes
- is_config_change
- xml_repair_v1_diff
- xml_create_patchset_v1
- xml_create_patchset_v2
- patch_legacy_mode
- xml_create_patchset
- patchset_process_digest
- xml_log_patchset
- xml_log_changes
- xml_accept_changes
- find_element
- __subtract_xml_object
- __add_xml_object
- find_patch_xml_node
- xml_patch_versions
- xml_patch_version_check
- xml_apply_patchset_v1
- __first_xml_child_match
- __xml_find_path
- xml_apply_patchset_v2
- xml_apply_patchset
- find_xml_node
- find_entity
- copy_in_properties
- fix_plus_plus_recursive
- expand_plus_plus
- getDocPtr
- add_node_copy
- add_node_nocopy
- __xml_acl_check
- crm_xml_add
- crm_xml_replace
- crm_xml_add_int
- create_xml_node
- __get_prefix
- xml_get_path
- free_xml_with_position
- free_xml
- copy_xml
- crm_xml_err
- string2xml
- stdin2xml
- decompress_file
- strip_text_nodes
- filename2xml
- crm_xml_add_last_written
- crm_xml_sanitize_id
- crm_xml_set_id
- write_xml_stream
- write_xml_fd
- write_xml_file
- get_message_xml
- add_message_xml
- crm_xml_escape_shuffle
- crm_xml_escape
- dump_xml_attr
- __xml_log_element
- __xml_log_change_element
- log_data_element
- dump_filtered_xml
- dump_xml_element
- dump_xml_text
- dump_xml_comment
- crm_xml_dump
- crm_buffer_add_char
- dump_xml_formatted_with_text
- dump_xml_formatted
- dump_xml_unformatted
- xml_has_children
- crm_element_value_int
- crm_element_value_const_int
- crm_element_value_const
- crm_element_value_copy
- xml_remove_prop
- purge_diff_markers
- save_xml_to_file
- apply_xml_diff
- __xml_diff_object
- xml_calculate_changes
- diff_xml_object
- can_prune_leaf
- diff_filter_context
- in_upper_context
- find_xml_comment
- subtract_xml_comment
- subtract_xml_object
- add_xml_comment
- add_xml_object
- update_xml_child
- find_xml_children
- replace_xml_child
- crm_create_nvpair_xml
- hash2nvpair
- hash2smartfield
- hash2field
- hash2metafield
- xml2list
- sort_pairs
- dump_pair
- sorted_xml
- first_named_child
- crm_next_same_xml
- crm_xml_init
- crm_xml_cleanup
- expand_idref
- crm_element_value
- crm_destroy_xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 #include <crm_internal.h>
20
21 #include <sys/param.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <time.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <stdarg.h>
30
31 #include <crm/crm.h>
32 #include <crm/msg_xml.h>
33 #include <crm/common/xml.h>
34 #include <crm/common/xml_internal.h>
35
36 #if HAVE_BZLIB_H
37 # include <bzlib.h>
38 #endif
39
40 #if HAVE_LIBXML2
41 # include <libxml/parser.h>
42 # include <libxml/tree.h>
43 #endif
44
45 #define XML_BUFFER_SIZE 4096
46 #define XML_PARSER_DEBUG 0
47
48 static inline int
49 __get_prefix(const char *prefix, xmlNode *xml, char *buffer, int offset);
50
51 typedef struct {
52 int found;
53 const char *string;
54 } filter_t;
55
56 enum xml_private_flags {
57 xpf_none = 0x0000,
58 xpf_dirty = 0x0001,
59 xpf_deleted = 0x0002,
60 xpf_created = 0x0004,
61 xpf_modified = 0x0008,
62
63 xpf_tracking = 0x0010,
64 xpf_processed = 0x0020,
65 xpf_skip = 0x0040,
66 xpf_moved = 0x0080,
67
68 xpf_acl_enabled = 0x0100,
69 xpf_acl_read = 0x0200,
70 xpf_acl_write = 0x0400,
71 xpf_acl_deny = 0x0800,
72
73 xpf_acl_create = 0x1000,
74 xpf_acl_denied = 0x2000,
75 };
76
77 typedef struct xml_private_s
78 {
79 long check;
80 uint32_t flags;
81 char *user;
82 GListPtr acls;
83 GListPtr deleted_objs;
84 } xml_private_t;
85
86 typedef struct xml_acl_s {
87 enum xml_private_flags mode;
88 char *xpath;
89 } xml_acl_t;
90
91 typedef struct xml_deleted_obj_s {
92 char *path;
93 int position;
94 } xml_deleted_obj_t;
95
96
97
98 static filter_t filter[] = {
99 { 0, XML_ATTR_ORIGIN },
100 { 0, XML_CIB_ATTR_WRITTEN },
101 { 0, XML_ATTR_UPDATE_ORIG },
102 { 0, XML_ATTR_UPDATE_CLIENT },
103 { 0, XML_ATTR_UPDATE_USER },
104 };
105
106
107 static xmlNode *subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right, gboolean * changed);
108 static xmlNode *find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact);
109 static int add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update);
110 static bool __xml_acl_check(xmlNode *xml, const char *name, enum xml_private_flags mode);
111 const char *__xml_acl_to_text(enum xml_private_flags flags);
112
113 #define CHUNK_SIZE 1024
114 static inline bool TRACKING_CHANGES(xmlNode *xml)
115 {
116 if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
117 return FALSE;
118 } else if(is_set(((xml_private_t *)xml->doc->_private)->flags, xpf_tracking)) {
119 return TRUE;
120 }
121 return FALSE;
122 }
123
124 #define buffer_print(buffer, max, offset, fmt, args...) do { \
125 int rc = (max); \
126 if(buffer) { \
127 rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \
128 } \
129 if(buffer && rc < 0) { \
130 crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \
131 (buffer)[(offset)] = 0; \
132 break; \
133 } else if(rc >= ((max) - (offset))) { \
134 char *tmp = NULL; \
135 (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \
136 tmp = realloc_safe((buffer), (max)); \
137 CRM_ASSERT(tmp); \
138 (buffer) = tmp; \
139 } else { \
140 offset += rc; \
141 break; \
142 } \
143 } while(1);
144
145 static void
146 insert_prefix(int options, char **buffer, int *offset, int *max, int depth)
147 {
148 if (options & xml_log_option_formatted) {
149 size_t spaces = 2 * depth;
150
151 if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
152 (*max) = QB_MAX(CHUNK_SIZE, (*max) * 2);
153 (*buffer) = realloc_safe((*buffer), (*max));
154 }
155 memset((*buffer) + (*offset), ' ', spaces);
156 (*offset) += spaces;
157 }
158 }
159
160 static void
161 set_parent_flag(xmlNode *xml, long flag)
162 {
163
164 for(; xml; xml = xml->parent) {
165 xml_private_t *p = xml->_private;
166
167 if(p == NULL) {
168
169 } else {
170 p->flags |= flag;
171
172 }
173 }
174 }
175
176 static void
177 set_doc_flag(xmlNode *xml, long flag)
178 {
179
180 if(xml && xml->doc && xml->doc->_private){
181
182 xml_private_t *p = xml->doc->_private;
183
184 p->flags |= flag;
185
186 }
187 }
188
189 static void
190 __xml_node_dirty(xmlNode *xml)
191 {
192 set_doc_flag(xml, xpf_dirty);
193 set_parent_flag(xml, xpf_dirty);
194 }
195
196 static void
197 __xml_node_clean(xmlNode *xml)
198 {
199 xmlNode *cIter = NULL;
200 xml_private_t *p = xml->_private;
201
202 if(p) {
203 p->flags = 0;
204 }
205
206 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
207 __xml_node_clean(cIter);
208 }
209 }
210
211 static void
212 crm_node_created(xmlNode *xml)
213 {
214 xmlNode *cIter = NULL;
215 xml_private_t *p = xml->_private;
216
217 if(p && TRACKING_CHANGES(xml)) {
218 if(is_not_set(p->flags, xpf_created)) {
219 p->flags |= xpf_created;
220 __xml_node_dirty(xml);
221 }
222
223 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
224 crm_node_created(cIter);
225 }
226 }
227 }
228
229 static void
230 crm_attr_dirty(xmlAttr *a)
231 {
232 xmlNode *parent = a->parent;
233 xml_private_t *p = NULL;
234
235 p = a->_private;
236 p->flags |= (xpf_dirty|xpf_modified);
237 p->flags = (p->flags & ~xpf_deleted);
238
239
240
241 __xml_node_dirty(parent);
242 }
243
244 int get_tag_name(const char *input, size_t offset, size_t max);
245 int get_attr_name(const char *input, size_t offset, size_t max);
246 int get_attr_value(const char *input, size_t offset, size_t max);
247 gboolean can_prune_leaf(xmlNode * xml_node);
248
249 void diff_filter_context(int context, int upper_bound, int lower_bound,
250 xmlNode * xml_node, xmlNode * parent);
251 int in_upper_context(int depth, int context, xmlNode * xml_node);
252 int add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff);
253
254 static inline const char *
255 crm_attr_value(xmlAttr * attr)
256 {
257 if (attr == NULL || attr->children == NULL) {
258 return NULL;
259 }
260 return (const char *)attr->children->content;
261 }
262
263 static inline xmlAttr *
264 crm_first_attr(xmlNode * xml)
265 {
266 if (xml == NULL) {
267 return NULL;
268 }
269 return xml->properties;
270 }
271
272 #define XML_PRIVATE_MAGIC (long) 0x81726354
273
274 static void
275 __xml_acl_free(void *data)
276 {
277 if(data) {
278 xml_acl_t *acl = data;
279
280 free(acl->xpath);
281 free(acl);
282 }
283 }
284
285 static void
286 __xml_deleted_obj_free(void *data)
287 {
288 if(data) {
289 xml_deleted_obj_t *deleted_obj = data;
290
291 free(deleted_obj->path);
292 free(deleted_obj);
293 }
294 }
295
296 static void
297 __xml_private_clean(xml_private_t *p)
298 {
299 if(p) {
300 CRM_ASSERT(p->check == XML_PRIVATE_MAGIC);
301
302 free(p->user);
303 p->user = NULL;
304
305 if(p->acls) {
306 g_list_free_full(p->acls, __xml_acl_free);
307 p->acls = NULL;
308 }
309
310 if(p->deleted_objs) {
311 g_list_free_full(p->deleted_objs, __xml_deleted_obj_free);
312 p->deleted_objs = NULL;
313 }
314 }
315 }
316
317
318 static void
319 __xml_private_free(xml_private_t *p)
320 {
321 __xml_private_clean(p);
322 free(p);
323 }
324
325 static void
326 pcmkDeregisterNode(xmlNodePtr node)
327 {
328 __xml_private_free(node->_private);
329 }
330
331 static void
332 pcmkRegisterNode(xmlNodePtr node)
333 {
334 xml_private_t *p = NULL;
335
336 switch(node->type) {
337 case XML_ELEMENT_NODE:
338 case XML_DOCUMENT_NODE:
339 case XML_ATTRIBUTE_NODE:
340 case XML_COMMENT_NODE:
341 p = calloc(1, sizeof(xml_private_t));
342 p->check = XML_PRIVATE_MAGIC;
343
344 p->flags |= (xpf_dirty|xpf_created);
345 node->_private = p;
346 break;
347 case XML_TEXT_NODE:
348 case XML_DTD_NODE:
349 case XML_CDATA_SECTION_NODE:
350 break;
351 default:
352
353 crm_trace("Ignoring %p %d", node, node->type);
354 CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
355 break;
356 }
357
358 if(p && TRACKING_CHANGES(node)) {
359
360
361
362 set_doc_flag(node, xpf_dirty);
363 __xml_node_dirty(node);
364 }
365 }
366
367 static xml_acl_t *
368 __xml_acl_create(xmlNode * xml, xmlNode *target, enum xml_private_flags mode)
369 {
370 xml_acl_t *acl = NULL;
371
372 xml_private_t *p = NULL;
373 const char *tag = crm_element_value(xml, XML_ACL_ATTR_TAG);
374 const char *ref = crm_element_value(xml, XML_ACL_ATTR_REF);
375 const char *xpath = crm_element_value(xml, XML_ACL_ATTR_XPATH);
376
377 if(tag == NULL) {
378
379 tag = crm_element_value(xml, XML_ACL_ATTR_TAGv1);
380 }
381 if(ref == NULL) {
382
383 ref = crm_element_value(xml, XML_ACL_ATTR_REFv1);
384 }
385
386 if(target == NULL || target->doc == NULL || target->doc->_private == NULL){
387 CRM_ASSERT(target);
388 CRM_ASSERT(target->doc);
389 CRM_ASSERT(target->doc->_private);
390 return NULL;
391
392 } else if (tag == NULL && ref == NULL && xpath == NULL) {
393 crm_trace("No criteria %p", xml);
394 return NULL;
395 }
396
397 p = target->doc->_private;
398 acl = calloc(1, sizeof(xml_acl_t));
399 if (acl) {
400 const char *attr = crm_element_value(xml, XML_ACL_ATTR_ATTRIBUTE);
401
402 acl->mode = mode;
403 if(xpath) {
404 acl->xpath = strdup(xpath);
405 crm_trace("Using xpath: %s", acl->xpath);
406
407 } else {
408 int offset = 0;
409 char buffer[XML_BUFFER_SIZE];
410
411 if(tag) {
412 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "//%s", tag);
413 } else {
414 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "//*");
415 }
416
417 if(ref || attr) {
418 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "[");
419 }
420
421 if(ref) {
422 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "@id='%s'", ref);
423 }
424
425 if(ref && attr) {
426 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, " and ");
427 }
428
429 if(attr) {
430 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "@%s", attr);
431 }
432
433 if(ref || attr) {
434 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "]");
435 }
436
437 CRM_LOG_ASSERT(offset > 0);
438 acl->xpath = strdup(buffer);
439 crm_trace("Built xpath: %s", acl->xpath);
440 }
441
442 p->acls = g_list_append(p->acls, acl);
443 }
444 return acl;
445 }
446
447 static gboolean
448 __xml_acl_parse_entry(xmlNode * acl_top, xmlNode * acl_entry, xmlNode *target)
449 {
450 xmlNode *child = NULL;
451
452 for (child = __xml_first_child(acl_entry); child; child = __xml_next(child)) {
453 const char *tag = crm_element_name(child);
454 const char *kind = crm_element_value(child, XML_ACL_ATTR_KIND);
455
456 if (strcmp(XML_ACL_TAG_PERMISSION, tag) == 0){
457 tag = kind;
458 }
459
460 crm_trace("Processing %s %p", tag, child);
461 if(tag == NULL) {
462 CRM_ASSERT(tag != NULL);
463
464 } else if (strcmp(XML_ACL_TAG_ROLE_REF, tag) == 0
465 || strcmp(XML_ACL_TAG_ROLE_REFv1, tag) == 0) {
466 const char *ref_role = crm_element_value(child, XML_ATTR_ID);
467
468 if (ref_role) {
469 xmlNode *role = NULL;
470
471 for (role = __xml_first_child(acl_top); role; role = __xml_next(role)) {
472 if (strcmp(XML_ACL_TAG_ROLE, (const char *)role->name) == 0) {
473 const char *role_id = crm_element_value(role, XML_ATTR_ID);
474
475 if (role_id && strcmp(ref_role, role_id) == 0) {
476 crm_debug("Unpacking referenced role: %s", role_id);
477 __xml_acl_parse_entry(acl_top, role, target);
478 break;
479 }
480 }
481 }
482 }
483
484 } else if (strcmp(XML_ACL_TAG_READ, tag) == 0) {
485 __xml_acl_create(child, target, xpf_acl_read);
486
487 } else if (strcmp(XML_ACL_TAG_WRITE, tag) == 0) {
488 __xml_acl_create(child, target, xpf_acl_write);
489
490 } else if (strcmp(XML_ACL_TAG_DENY, tag) == 0) {
491 __xml_acl_create(child, target, xpf_acl_deny);
492
493 } else {
494 crm_warn("Unknown ACL entry: %s/%s", tag, kind);
495 }
496 }
497
498 return TRUE;
499 }
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523 const char *
524 __xml_acl_to_text(enum xml_private_flags flags)
525 {
526 if(is_set(flags, xpf_acl_deny)) {
527 return "deny";
528 }
529 if(is_set(flags, xpf_acl_write)) {
530 return "read/write";
531 }
532 if(is_set(flags, xpf_acl_read)) {
533 return "read";
534 }
535
536 return "none";
537 }
538
539 static void
540 __xml_acl_apply(xmlNode *xml)
541 {
542 GListPtr aIter = NULL;
543 xml_private_t *p = NULL;
544 xmlXPathObjectPtr xpathObj = NULL;
545
546 if(xml_acl_enabled(xml) == FALSE) {
547 p = xml->doc->_private;
548 crm_trace("Not applying ACLs for %s", p->user);
549 return;
550 }
551
552 p = xml->doc->_private;
553 for(aIter = p->acls; aIter != NULL; aIter = aIter->next) {
554 int max = 0, lpc = 0;
555 xml_acl_t *acl = aIter->data;
556
557 xpathObj = xpath_search(xml, acl->xpath);
558 max = numXpathResults(xpathObj);
559
560 for(lpc = 0; lpc < max; lpc++) {
561 xmlNode *match = getXpathResult(xpathObj, lpc);
562 char *path = xml_get_path(match);
563
564 p = match->_private;
565 crm_trace("Applying %x to %s for %s", acl->mode, path, acl->xpath);
566
567 #ifdef SUSE_ACL_COMPAT
568 if(is_not_set(p->flags, acl->mode)) {
569 if(is_set(p->flags, xpf_acl_read)
570 || is_set(p->flags, xpf_acl_write)
571 || is_set(p->flags, xpf_acl_deny)) {
572 crm_config_warn("Configuration element %s is matched by multiple ACL rules, only the first applies ('%s' wins over '%s')",
573 path, __xml_acl_to_text(p->flags), __xml_acl_to_text(acl->mode));
574 free(path);
575 continue;
576 }
577 }
578 #endif
579
580 p->flags |= acl->mode;
581 free(path);
582 }
583 crm_trace("Now enforcing ACL: %s (%d matches)", acl->xpath, max);
584 freeXpathObject(xpathObj);
585 }
586
587 p = xml->_private;
588 if(is_not_set(p->flags, xpf_acl_read) && is_not_set(p->flags, xpf_acl_write)) {
589 p->flags |= xpf_acl_deny;
590 p = xml->doc->_private;
591 crm_info("Enforcing default ACL for %s to %s", p->user, crm_element_name(xml));
592 }
593
594 }
595
596 static void
597 __xml_acl_unpack(xmlNode *source, xmlNode *target, const char *user)
598 {
599 #if ENABLE_ACL
600 xml_private_t *p = NULL;
601
602 if(target == NULL || target->doc == NULL || target->doc->_private == NULL) {
603 return;
604 }
605
606 p = target->doc->_private;
607 if(pcmk_acl_required(user) == FALSE) {
608 crm_trace("no acls needed for '%s'", user);
609
610 } else if(p->acls == NULL) {
611 xmlNode *acls = get_xpath_object("//"XML_CIB_TAG_ACLS, source, LOG_TRACE);
612
613 free(p->user);
614 p->user = strdup(user);
615
616 if(acls) {
617 xmlNode *child = NULL;
618
619 for (child = __xml_first_child(acls); child; child = __xml_next(child)) {
620 const char *tag = crm_element_name(child);
621
622 if (strcmp(tag, XML_ACL_TAG_USER) == 0 || strcmp(tag, XML_ACL_TAG_USERv1) == 0) {
623 const char *id = crm_element_value(child, XML_ATTR_ID);
624
625 if(id && strcmp(id, user) == 0) {
626 crm_debug("Unpacking ACLs for %s", id);
627 __xml_acl_parse_entry(acls, child, target);
628 }
629 }
630 }
631 }
632 }
633 #endif
634 }
635
636 static inline bool
637 __xml_acl_mode_test(enum xml_private_flags allowed, enum xml_private_flags requested)
638 {
639 if(is_set(allowed, xpf_acl_deny)) {
640 return FALSE;
641
642 } else if(is_set(allowed, requested)) {
643 return TRUE;
644
645 } else if(is_set(requested, xpf_acl_read) && is_set(allowed, xpf_acl_write)) {
646 return TRUE;
647
648 } else if(is_set(requested, xpf_acl_create) && is_set(allowed, xpf_acl_write)) {
649 return TRUE;
650
651 } else if(is_set(requested, xpf_acl_create) && is_set(allowed, xpf_created)) {
652 return TRUE;
653 }
654 return FALSE;
655 }
656
657
658
659
660 static bool
661 __xml_purge_attributes(xmlNode *xml)
662 {
663 xmlNode *child = NULL;
664 xmlAttr *xIter = NULL;
665 bool readable_children = FALSE;
666 xml_private_t *p = xml->_private;
667
668 if(__xml_acl_mode_test(p->flags, xpf_acl_read)) {
669 crm_trace("%s[@id=%s] is readable", crm_element_name(xml), ID(xml));
670 return TRUE;
671 }
672
673 xIter = crm_first_attr(xml);
674 while(xIter != NULL) {
675 xmlAttr *tmp = xIter;
676 const char *prop_name = (const char *)xIter->name;
677
678 xIter = xIter->next;
679 if (strcmp(prop_name, XML_ATTR_ID) == 0) {
680 continue;
681 }
682
683 xmlUnsetProp(xml, tmp->name);
684 }
685
686 child = __xml_first_child(xml);
687 while ( child != NULL ) {
688 xmlNode *tmp = child;
689
690 child = __xml_next(child);
691 readable_children |= __xml_purge_attributes(tmp);
692 }
693
694 if(readable_children == FALSE) {
695 free_xml(xml);
696 }
697 return readable_children;
698 }
699
700 bool
701 xml_acl_filtered_copy(const char *user, xmlNode* acl_source, xmlNode *xml, xmlNode ** result)
702 {
703 GListPtr aIter = NULL;
704 xmlNode *target = NULL;
705 xml_private_t *p = NULL;
706 xml_private_t *doc = NULL;
707
708 *result = NULL;
709 if(xml == NULL || pcmk_acl_required(user) == FALSE) {
710 crm_trace("no acls needed for '%s'", user);
711 return FALSE;
712 }
713
714 crm_trace("filtering copy of %p for '%s'", xml, user);
715 target = copy_xml(xml);
716 if(target == NULL) {
717 return TRUE;
718 }
719
720 __xml_acl_unpack(acl_source, target, user);
721 set_doc_flag(target, xpf_acl_enabled);
722 __xml_acl_apply(target);
723
724 doc = target->doc->_private;
725 for(aIter = doc->acls; aIter != NULL && target; aIter = aIter->next) {
726 int max = 0;
727 xml_acl_t *acl = aIter->data;
728
729 if(acl->mode != xpf_acl_deny) {
730
731
732 } else if(acl->xpath) {
733 int lpc = 0;
734 xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
735
736 max = numXpathResults(xpathObj);
737 for(lpc = 0; lpc < max; lpc++) {
738 xmlNode *match = getXpathResult(xpathObj, lpc);
739
740 crm_trace("Purging attributes from %s", acl->xpath);
741 if(__xml_purge_attributes(match) == FALSE && match == target) {
742 crm_trace("No access to the entire document for %s", user);
743 freeXpathObject(xpathObj);
744 return TRUE;
745 }
746 }
747 crm_trace("Enforced ACL %s (%d matches)", acl->xpath, max);
748 freeXpathObject(xpathObj);
749 }
750 }
751
752 p = target->_private;
753 if(is_set(p->flags, xpf_acl_deny) && __xml_purge_attributes(target) == FALSE) {
754 crm_trace("No access to the entire document for %s", user);
755 return TRUE;
756 }
757
758 if(doc->acls) {
759 g_list_free_full(doc->acls, __xml_acl_free);
760 doc->acls = NULL;
761
762 } else {
763 crm_trace("Ordinary user '%s' cannot access the CIB without any defined ACLs", doc->user);
764 free_xml(target);
765 target = NULL;
766 }
767
768 if(target) {
769 *result = target;
770 }
771
772 return TRUE;
773 }
774
775 static void
776 __xml_acl_post_process(xmlNode * xml)
777 {
778 xmlNode *cIter = __xml_first_child(xml);
779 xml_private_t *p = xml->_private;
780
781 if(is_set(p->flags, xpf_created)) {
782 xmlAttr *xIter = NULL;
783 char *path = xml_get_path(xml);
784
785
786
787
788
789 for (xIter = crm_first_attr(xml); xIter != NULL; xIter = xIter->next) {
790 const char *prop_name = (const char *)xIter->name;
791
792 if (strcmp(prop_name, XML_ATTR_ID) == 0 && strstr(path, "/"XML_CIB_TAG_ACLS"/") == NULL) {
793
794 continue;
795
796 } else if(__xml_acl_check(xml, NULL, xpf_acl_write)) {
797 crm_trace("Creation of %s=%s is allowed", crm_element_name(xml), ID(xml));
798 break;
799
800 } else {
801 crm_trace("Cannot add new node %s at %s", crm_element_name(xml), path);
802
803 if(xml != xmlDocGetRootElement(xml->doc)) {
804 xmlUnlinkNode(xml);
805 xmlFreeNode(xml);
806 }
807 free(path);
808 return;
809 }
810 }
811 free(path);
812 }
813
814 while (cIter != NULL) {
815 xmlNode *child = cIter;
816 cIter = __xml_next(cIter);
817 __xml_acl_post_process(child);
818 }
819 }
820
821 bool
822 xml_acl_denied(xmlNode *xml)
823 {
824 if(xml && xml->doc && xml->doc->_private){
825 xml_private_t *p = xml->doc->_private;
826
827 return is_set(p->flags, xpf_acl_denied);
828 }
829 return FALSE;
830 }
831
832 void
833 xml_acl_disable(xmlNode *xml)
834 {
835 if(xml_acl_enabled(xml)) {
836 xml_private_t *p = xml->doc->_private;
837
838
839 __xml_acl_apply(xml);
840 __xml_acl_post_process(xml);
841 clear_bit(p->flags, xpf_acl_enabled);
842 }
843 }
844
845 bool
846 xml_acl_enabled(xmlNode *xml)
847 {
848 if(xml && xml->doc && xml->doc->_private){
849 xml_private_t *p = xml->doc->_private;
850
851 return is_set(p->flags, xpf_acl_enabled);
852 }
853 return FALSE;
854 }
855
856 void
857 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
858 {
859 xml_accept_changes(xml);
860 crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
861 set_doc_flag(xml, xpf_tracking);
862 if(enforce_acls) {
863 if(acl_source == NULL) {
864 acl_source = xml;
865 }
866 set_doc_flag(xml, xpf_acl_enabled);
867 __xml_acl_unpack(acl_source, xml, user);
868 __xml_acl_apply(xml);
869 }
870 }
871
872 bool xml_tracking_changes(xmlNode * xml)
873 {
874 if(xml == NULL) {
875 return FALSE;
876
877 } else if(is_set(((xml_private_t *)xml->doc->_private)->flags, xpf_tracking)) {
878 return TRUE;
879 }
880 return FALSE;
881 }
882
883 bool xml_document_dirty(xmlNode *xml)
884 {
885 if(xml != NULL && xml->doc && xml->doc->_private) {
886 xml_private_t *doc = xml->doc->_private;
887
888 return is_set(doc->flags, xpf_dirty);
889 }
890 return FALSE;
891 }
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941 static int __xml_offset(xmlNode *xml)
942 {
943 int position = 0;
944 xmlNode *cIter = NULL;
945
946 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
947 xml_private_t *p = ((xmlNode*)cIter->prev)->_private;
948
949 if(is_not_set(p->flags, xpf_skip)) {
950 position++;
951 }
952 }
953
954 return position;
955 }
956
957 static int __xml_offset_no_deletions(xmlNode *xml)
958 {
959 int position = 0;
960 xmlNode *cIter = NULL;
961
962 for(cIter = xml; cIter->prev; cIter = cIter->prev) {
963 xml_private_t *p = ((xmlNode*)cIter->prev)->_private;
964
965 if(is_not_set(p->flags, xpf_deleted)) {
966 position++;
967 }
968 }
969
970 return position;
971 }
972
973 static void
974 __xml_build_changes(xmlNode * xml, xmlNode *patchset)
975 {
976 xmlNode *cIter = NULL;
977 xmlAttr *pIter = NULL;
978 xmlNode *change = NULL;
979 xml_private_t *p = xml->_private;
980
981 if(patchset && is_set(p->flags, xpf_created)) {
982 int offset = 0;
983 char buffer[XML_BUFFER_SIZE];
984
985 if(__get_prefix(NULL, xml->parent, buffer, offset) > 0) {
986 int position = __xml_offset_no_deletions(xml);
987
988 change = create_xml_node(patchset, XML_DIFF_CHANGE);
989
990 crm_xml_add(change, XML_DIFF_OP, "create");
991 crm_xml_add(change, XML_DIFF_PATH, buffer);
992 crm_xml_add_int(change, XML_DIFF_POSITION, position);
993 add_node_copy(change, xml);
994 }
995
996 return;
997 }
998
999 for (pIter = crm_first_attr(xml); pIter != NULL; pIter = pIter->next) {
1000 xmlNode *attr = NULL;
1001
1002 p = pIter->_private;
1003 if(is_not_set(p->flags, xpf_deleted) && is_not_set(p->flags, xpf_dirty)) {
1004 continue;
1005 }
1006
1007 if(change == NULL) {
1008 int offset = 0;
1009 char buffer[XML_BUFFER_SIZE];
1010
1011 if(__get_prefix(NULL, xml, buffer, offset) > 0) {
1012 change = create_xml_node(patchset, XML_DIFF_CHANGE);
1013
1014 crm_xml_add(change, XML_DIFF_OP, "modify");
1015 crm_xml_add(change, XML_DIFF_PATH, buffer);
1016
1017 change = create_xml_node(change, XML_DIFF_LIST);
1018 }
1019 }
1020
1021 attr = create_xml_node(change, XML_DIFF_ATTR);
1022
1023 crm_xml_add(attr, XML_NVPAIR_ATTR_NAME, (const char *)pIter->name);
1024 if(p->flags & xpf_deleted) {
1025 crm_xml_add(attr, XML_DIFF_OP, "unset");
1026
1027 } else {
1028 const char *value = crm_element_value(xml, (const char *)pIter->name);
1029
1030 crm_xml_add(attr, XML_DIFF_OP, "set");
1031 crm_xml_add(attr, XML_NVPAIR_ATTR_VALUE, value);
1032 }
1033 }
1034
1035 if(change) {
1036 xmlNode *result = NULL;
1037
1038 change = create_xml_node(change->parent, XML_DIFF_RESULT);
1039 result = create_xml_node(change, (const char *)xml->name);
1040
1041 for (pIter = crm_first_attr(xml); pIter != NULL; pIter = pIter->next) {
1042 const char *value = crm_element_value(xml, (const char *)pIter->name);
1043
1044 p = pIter->_private;
1045 if (is_not_set(p->flags, xpf_deleted)) {
1046 crm_xml_add(result, (const char *)pIter->name, value);
1047 }
1048 }
1049 }
1050
1051 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
1052 __xml_build_changes(cIter, patchset);
1053 }
1054
1055 p = xml->_private;
1056 if(patchset && is_set(p->flags, xpf_moved)) {
1057 int offset = 0;
1058 char buffer[XML_BUFFER_SIZE];
1059
1060 crm_trace("%s.%s moved to position %d", xml->name, ID(xml), __xml_offset(xml));
1061 if(__get_prefix(NULL, xml, buffer, offset) > 0) {
1062 change = create_xml_node(patchset, XML_DIFF_CHANGE);
1063
1064 crm_xml_add(change, XML_DIFF_OP, "move");
1065 crm_xml_add(change, XML_DIFF_PATH, buffer);
1066 crm_xml_add_int(change, XML_DIFF_POSITION, __xml_offset_no_deletions(xml));
1067 }
1068 }
1069 }
1070
1071 static void
1072 __xml_accept_changes(xmlNode * xml)
1073 {
1074 xmlNode *cIter = NULL;
1075 xmlAttr *pIter = NULL;
1076 xml_private_t *p = xml->_private;
1077
1078 p->flags = xpf_none;
1079 pIter = crm_first_attr(xml);
1080
1081 while (pIter != NULL) {
1082 const xmlChar *name = pIter->name;
1083
1084 p = pIter->_private;
1085 pIter = pIter->next;
1086
1087 if(p->flags & xpf_deleted) {
1088 xml_remove_prop(xml, (const char *)name);
1089
1090 } else {
1091 p->flags = xpf_none;
1092 }
1093 }
1094
1095 for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
1096 __xml_accept_changes(cIter);
1097 }
1098 }
1099
1100 static bool
1101 is_config_change(xmlNode *xml)
1102 {
1103 GListPtr gIter = NULL;
1104 xml_private_t *p = NULL;
1105 xmlNode *config = first_named_child(xml, XML_CIB_TAG_CONFIGURATION);
1106
1107 if(config) {
1108 p = config->_private;
1109 }
1110 if(p && is_set(p->flags, xpf_dirty)) {
1111 return TRUE;
1112 }
1113
1114 if(xml->doc && xml->doc->_private) {
1115 p = xml->doc->_private;
1116 for(gIter = p->deleted_objs; gIter; gIter = gIter->next) {
1117 xml_deleted_obj_t *deleted_obj = gIter->data;
1118
1119 if(strstr(deleted_obj->path, "/"XML_TAG_CIB"/"XML_CIB_TAG_CONFIGURATION) != NULL) {
1120 return TRUE;
1121 }
1122 }
1123 }
1124
1125 return FALSE;
1126 }
1127
1128 static void
1129 xml_repair_v1_diff(xmlNode * last, xmlNode * next, xmlNode * local_diff, gboolean changed)
1130 {
1131 int lpc = 0;
1132 xmlNode *cib = NULL;
1133 xmlNode *diff_child = NULL;
1134
1135 const char *tag = NULL;
1136
1137 const char *vfields[] = {
1138 XML_ATTR_GENERATION_ADMIN,
1139 XML_ATTR_GENERATION,
1140 XML_ATTR_NUMUPDATES,
1141 };
1142
1143 if (local_diff == NULL) {
1144 crm_trace("Nothing to do");
1145 return;
1146 }
1147
1148 tag = "diff-removed";
1149 diff_child = find_xml_node(local_diff, tag, FALSE);
1150 if (diff_child == NULL) {
1151 diff_child = create_xml_node(local_diff, tag);
1152 }
1153
1154 tag = XML_TAG_CIB;
1155 cib = find_xml_node(diff_child, tag, FALSE);
1156 if (cib == NULL) {
1157 cib = create_xml_node(diff_child, tag);
1158 }
1159
1160 for(lpc = 0; last && lpc < DIMOF(vfields); lpc++){
1161 const char *value = crm_element_value(last, vfields[lpc]);
1162
1163 crm_xml_add(diff_child, vfields[lpc], value);
1164 if(changed || lpc == 2) {
1165 crm_xml_add(cib, vfields[lpc], value);
1166 }
1167 }
1168
1169 tag = "diff-added";
1170 diff_child = find_xml_node(local_diff, tag, FALSE);
1171 if (diff_child == NULL) {
1172 diff_child = create_xml_node(local_diff, tag);
1173 }
1174
1175 tag = XML_TAG_CIB;
1176 cib = find_xml_node(diff_child, tag, FALSE);
1177 if (cib == NULL) {
1178 cib = create_xml_node(diff_child, tag);
1179 }
1180
1181 for(lpc = 0; next && lpc < DIMOF(vfields); lpc++){
1182 const char *value = crm_element_value(next, vfields[lpc]);
1183
1184 crm_xml_add(diff_child, vfields[lpc], value);
1185 }
1186
1187 if (next) {
1188 xmlAttrPtr xIter = NULL;
1189
1190 for (xIter = next->properties; xIter; xIter = xIter->next) {
1191 const char *p_name = (const char *)xIter->name;
1192 const char *p_value = crm_element_value(next, p_name);
1193
1194 xmlSetProp(cib, (const xmlChar *)p_name, (const xmlChar *)p_value);
1195 }
1196 }
1197
1198 crm_log_xml_explicit(local_diff, "Repaired-diff");
1199 }
1200
1201 static xmlNode *
1202 xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config, bool suppress)
1203 {
1204 xmlNode *patchset = diff_xml_object(source, target, suppress);
1205
1206 if(patchset) {
1207 CRM_LOG_ASSERT(xml_document_dirty(target));
1208 xml_repair_v1_diff(source, target, patchset, config);
1209 crm_xml_add(patchset, "format", "1");
1210 }
1211 return patchset;
1212 }
1213
1214 static xmlNode *
1215 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
1216 {
1217 int lpc = 0;
1218 GListPtr gIter = NULL;
1219 xml_private_t *doc = NULL;
1220
1221 xmlNode *v = NULL;
1222 xmlNode *version = NULL;
1223 xmlNode *patchset = NULL;
1224 const char *vfields[] = {
1225 XML_ATTR_GENERATION_ADMIN,
1226 XML_ATTR_GENERATION,
1227 XML_ATTR_NUMUPDATES,
1228 };
1229
1230 CRM_ASSERT(target);
1231 if(xml_document_dirty(target) == FALSE) {
1232 return NULL;
1233 }
1234
1235 CRM_ASSERT(target->doc);
1236 doc = target->doc->_private;
1237
1238 patchset = create_xml_node(NULL, XML_TAG_DIFF);
1239 crm_xml_add_int(patchset, "format", 2);
1240
1241 version = create_xml_node(patchset, XML_DIFF_VERSION);
1242
1243 v = create_xml_node(version, XML_DIFF_VSOURCE);
1244 for(lpc = 0; lpc < DIMOF(vfields); lpc++){
1245 const char *value = crm_element_value(source, vfields[lpc]);
1246
1247 if(value == NULL) {
1248 value = "1";
1249 }
1250 crm_xml_add(v, vfields[lpc], value);
1251 }
1252
1253 v = create_xml_node(version, XML_DIFF_VTARGET);
1254 for(lpc = 0; lpc < DIMOF(vfields); lpc++){
1255 const char *value = crm_element_value(target, vfields[lpc]);
1256
1257 if(value == NULL) {
1258 value = "1";
1259 }
1260 crm_xml_add(v, vfields[lpc], value);
1261 }
1262
1263 for(gIter = doc->deleted_objs; gIter; gIter = gIter->next) {
1264 xml_deleted_obj_t *deleted_obj = gIter->data;
1265 xmlNode *change = create_xml_node(patchset, XML_DIFF_CHANGE);
1266
1267 crm_xml_add(change, XML_DIFF_OP, "delete");
1268 crm_xml_add(change, XML_DIFF_PATH, deleted_obj->path);
1269 if (deleted_obj->position >= 0) {
1270 crm_xml_add_int(change, XML_DIFF_POSITION, deleted_obj->position);
1271 }
1272 }
1273
1274 __xml_build_changes(target, patchset);
1275 return patchset;
1276 }
1277
1278 static gboolean patch_legacy_mode(void)
1279 {
1280 static gboolean init = TRUE;
1281 static gboolean legacy = FALSE;
1282
1283 if(init) {
1284 init = FALSE;
1285 legacy = daemon_option_enabled("cib", "legacy");
1286 if(legacy) {
1287 crm_notice("Enabled legacy mode");
1288 }
1289 }
1290 return legacy;
1291 }
1292
1293 xmlNode *
1294 xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version)
1295 {
1296 int counter = 0;
1297 bool config = FALSE;
1298 xmlNode *patch = NULL;
1299 const char *version = crm_element_value(source, XML_ATTR_CRM_VERSION);
1300
1301 xml_acl_disable(target);
1302 if(xml_document_dirty(target) == FALSE) {
1303 crm_trace("No change %d", format);
1304 return NULL;
1305 }
1306
1307 config = is_config_change(target);
1308 if(config_changed) {
1309 *config_changed = config;
1310 }
1311
1312 if(manage_version && config) {
1313 crm_trace("Config changed %d", format);
1314 crm_xml_add(target, XML_ATTR_NUMUPDATES, "0");
1315
1316 crm_element_value_int(target, XML_ATTR_GENERATION, &counter);
1317 crm_xml_add_int(target, XML_ATTR_GENERATION, counter+1);
1318
1319 } else if(manage_version) {
1320 crm_element_value_int(target, XML_ATTR_NUMUPDATES, &counter);
1321 crm_trace("Status changed %d - %d %s", format, counter, crm_element_value(source, XML_ATTR_NUMUPDATES));
1322 crm_xml_add_int(target, XML_ATTR_NUMUPDATES, counter+1);
1323 }
1324
1325 if(format == 0) {
1326 if(patch_legacy_mode()) {
1327 format = 1;
1328
1329 } else if(compare_version("3.0.8", version) < 0) {
1330 format = 2;
1331
1332 } else {
1333 format = 1;
1334 }
1335 crm_trace("Using patch format %d for version: %s", format, version);
1336 }
1337
1338 switch(format) {
1339 case 1:
1340 patch = xml_create_patchset_v1(source, target, config, FALSE);
1341 break;
1342 case 2:
1343 patch = xml_create_patchset_v2(source, target);
1344 break;
1345 default:
1346 crm_err("Unknown patch format: %d", format);
1347 return NULL;
1348 }
1349
1350 return patch;
1351 }
1352
1353 void
1354 patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
1355 {
1356 int format = 1;
1357 const char *version = NULL;
1358 char *digest = NULL;
1359
1360 if (patch == NULL || source == NULL || target == NULL) {
1361 return;
1362 }
1363
1364
1365
1366 CRM_LOG_ASSERT(xml_document_dirty(target) == FALSE);
1367
1368 crm_element_value_int(patch, "format", &format);
1369 if (format > 1 && with_digest == FALSE) {
1370 return;
1371 }
1372
1373 version = crm_element_value(source, XML_ATTR_CRM_VERSION);
1374 digest = calculate_xml_versioned_digest(target, FALSE, TRUE, version);
1375
1376 crm_xml_add(patch, XML_ATTR_DIGEST, digest);
1377 free(digest);
1378
1379 return;
1380 }
1381
1382 static void
1383 __xml_log_element(int log_level, const char *file, const char *function, int line,
1384 const char *prefix, xmlNode * data, int depth, int options);
1385
1386 void
1387 xml_log_patchset(uint8_t log_level, const char *function, xmlNode * patchset)
1388 {
1389 int format = 1;
1390 xmlNode *child = NULL;
1391 xmlNode *added = NULL;
1392 xmlNode *removed = NULL;
1393 gboolean is_first = TRUE;
1394
1395 int add[] = { 0, 0, 0 };
1396 int del[] = { 0, 0, 0 };
1397
1398 const char *fmt = NULL;
1399 const char *digest = NULL;
1400 int options = xml_log_option_formatted;
1401
1402 static struct qb_log_callsite *patchset_cs = NULL;
1403
1404 if (patchset_cs == NULL) {
1405 patchset_cs = qb_log_callsite_get(function, __FILE__, "xml-patchset", log_level, __LINE__, 0);
1406 }
1407
1408 if (patchset == NULL) {
1409 crm_trace("Empty patch");
1410 return;
1411
1412 } else if (log_level == 0) {
1413
1414 } else if (crm_is_callsite_active(patchset_cs, log_level, 0) == FALSE) {
1415 return;
1416 }
1417
1418 xml_patch_versions(patchset, add, del);
1419 fmt = crm_element_value(patchset, "format");
1420 digest = crm_element_value(patchset, XML_ATTR_DIGEST);
1421
1422 if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
1423 do_crm_log_alias(log_level, __FILE__, function, __LINE__,
1424 "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
1425 do_crm_log_alias(log_level, __FILE__, function, __LINE__,
1426 "Diff: +++ %d.%d.%d %s", add[0], add[1], add[2], digest);
1427
1428 } else if (patchset != NULL && (add[0] || add[1] || add[2])) {
1429 do_crm_log_alias(log_level, __FILE__, function, __LINE__,
1430 "%s: Local-only Change: %d.%d.%d", function ? function : "",
1431 add[0], add[1], add[2]);
1432 }
1433
1434 crm_element_value_int(patchset, "format", &format);
1435 if(format == 2) {
1436 xmlNode *change = NULL;
1437
1438 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
1439 const char *op = crm_element_value(change, XML_DIFF_OP);
1440 const char *xpath = crm_element_value(change, XML_DIFF_PATH);
1441
1442 if(op == NULL) {
1443 } else if(strcmp(op, "create") == 0) {
1444 int lpc = 0, max = 0;
1445 char *prefix = crm_strdup_printf("++ %s: ", xpath);
1446
1447 max = strlen(prefix);
1448 __xml_log_element(log_level, __FILE__, function, __LINE__, prefix, change->children,
1449 0, xml_log_option_formatted|xml_log_option_open);
1450
1451 for(lpc = 2; lpc < max; lpc++) {
1452 prefix[lpc] = ' ';
1453 }
1454
1455 __xml_log_element(log_level, __FILE__, function, __LINE__, prefix, change->children,
1456 0, xml_log_option_formatted|xml_log_option_close|xml_log_option_children);
1457 free(prefix);
1458
1459 } else if(strcmp(op, "move") == 0) {
1460 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "+~ %s moved to offset %s", xpath, crm_element_value(change, XML_DIFF_POSITION));
1461
1462 } else if(strcmp(op, "modify") == 0) {
1463 xmlNode *clist = first_named_child(change, XML_DIFF_LIST);
1464 char buffer_set[XML_BUFFER_SIZE];
1465 char buffer_unset[XML_BUFFER_SIZE];
1466 int o_set = 0;
1467 int o_unset = 0;
1468
1469 buffer_set[0] = 0;
1470 buffer_unset[0] = 0;
1471 for (child = __xml_first_child(clist); child != NULL; child = __xml_next(child)) {
1472 const char *name = crm_element_value(child, "name");
1473
1474 op = crm_element_value(child, XML_DIFF_OP);
1475 if(op == NULL) {
1476 } else if(strcmp(op, "set") == 0) {
1477 const char *value = crm_element_value(child, "value");
1478
1479 if(o_set > 0) {
1480 o_set += snprintf(buffer_set + o_set, XML_BUFFER_SIZE - o_set, ", ");
1481 }
1482 o_set += snprintf(buffer_set + o_set, XML_BUFFER_SIZE - o_set, "@%s=%s", name, value);
1483
1484 } else if(strcmp(op, "unset") == 0) {
1485 if(o_unset > 0) {
1486 o_unset += snprintf(buffer_unset + o_unset, XML_BUFFER_SIZE - o_unset, ", ");
1487 }
1488 o_unset += snprintf(buffer_unset + o_unset, XML_BUFFER_SIZE - o_unset, "@%s", name);
1489 }
1490 }
1491 if(o_set) {
1492 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "+ %s: %s", xpath, buffer_set);
1493 }
1494 if(o_unset) {
1495 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s: %s", xpath, buffer_unset);
1496 }
1497
1498 } else if(strcmp(op, "delete") == 0) {
1499 int position = -1;
1500
1501 crm_element_value_int(change, XML_DIFF_POSITION, &position);
1502 if (position >= 0) {
1503 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)", xpath, position);
1504
1505 } else {
1506 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s", xpath);
1507 }
1508 }
1509 }
1510 return;
1511 }
1512
1513 if (log_level < LOG_DEBUG || function == NULL) {
1514 options |= xml_log_option_diff_short;
1515 }
1516
1517 removed = find_xml_node(patchset, "diff-removed", FALSE);
1518 for (child = __xml_first_child(removed); child != NULL; child = __xml_next(child)) {
1519 log_data_element(log_level, __FILE__, function, __LINE__, "- ", child, 0,
1520 options | xml_log_option_diff_minus);
1521 if (is_first) {
1522 is_first = FALSE;
1523 } else {
1524 do_crm_log_alias(log_level, __FILE__, function, __LINE__, " --- ");
1525 }
1526 }
1527
1528 is_first = TRUE;
1529 added = find_xml_node(patchset, "diff-added", FALSE);
1530 for (child = __xml_first_child(added); child != NULL; child = __xml_next(child)) {
1531 log_data_element(log_level, __FILE__, function, __LINE__, "+ ", child, 0,
1532 options | xml_log_option_diff_plus);
1533 if (is_first) {
1534 is_first = FALSE;
1535 } else {
1536 do_crm_log_alias(log_level, __FILE__, function, __LINE__, " +++ ");
1537 }
1538 }
1539 }
1540
1541 void
1542 xml_log_changes(uint8_t log_level, const char *function, xmlNode * xml)
1543 {
1544 GListPtr gIter = NULL;
1545 xml_private_t *doc = NULL;
1546
1547 CRM_ASSERT(xml);
1548 CRM_ASSERT(xml->doc);
1549
1550 doc = xml->doc->_private;
1551 if(is_not_set(doc->flags, xpf_dirty)) {
1552 return;
1553 }
1554
1555 for(gIter = doc->deleted_objs; gIter; gIter = gIter->next) {
1556 xml_deleted_obj_t *deleted_obj = gIter->data;
1557
1558 if (deleted_obj->position >= 0) {
1559 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)",
1560 deleted_obj->path, deleted_obj->position);
1561
1562 } else {
1563 do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s",
1564 deleted_obj->path);
1565 }
1566 }
1567
1568 log_data_element(log_level, __FILE__, function, __LINE__, "+ ", xml, 0,
1569 xml_log_option_formatted|xml_log_option_dirty_add);
1570 }
1571
1572 void
1573 xml_accept_changes(xmlNode * xml)
1574 {
1575 xmlNode *top = NULL;
1576 xml_private_t *doc = NULL;
1577
1578 if(xml == NULL) {
1579 return;
1580 }
1581
1582 crm_trace("Accepting changes to %p", xml);
1583 doc = xml->doc->_private;
1584 top = xmlDocGetRootElement(xml->doc);
1585
1586 __xml_private_clean(xml->doc->_private);
1587
1588 if(is_not_set(doc->flags, xpf_dirty)) {
1589 doc->flags = xpf_none;
1590 return;
1591 }
1592
1593 doc->flags = xpf_none;
1594 __xml_accept_changes(top);
1595 }
1596
1597 static xmlNode *
1598 find_element(xmlNode *haystack, xmlNode *needle, gboolean exact)
1599 {
1600 CRM_CHECK(needle != NULL, return NULL);
1601 return (needle->type == XML_COMMENT_NODE)?
1602 find_xml_comment(haystack, needle, exact)
1603 : find_entity(haystack, crm_element_name(needle), ID(needle));
1604 }
1605
1606
1607 static void
1608 __subtract_xml_object(xmlNode * target, xmlNode * patch)
1609 {
1610 xmlNode *patch_child = NULL;
1611 xmlNode *cIter = NULL;
1612 xmlAttrPtr xIter = NULL;
1613
1614 char *id = NULL;
1615 const char *name = NULL;
1616 const char *value = NULL;
1617
1618 if (target == NULL || patch == NULL) {
1619 return;
1620 }
1621
1622 if (target->type == XML_COMMENT_NODE) {
1623 gboolean dummy;
1624
1625 subtract_xml_comment(target->parent, target, patch, &dummy);
1626 }
1627
1628 name = crm_element_name(target);
1629 CRM_CHECK(name != NULL, return);
1630 CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(patch)), return);
1631 CRM_CHECK(safe_str_eq(ID(target), ID(patch)), return);
1632
1633
1634 id = crm_element_value_copy(target, XML_ATTR_ID);
1635 value = crm_element_value(patch, XML_DIFF_MARKER);
1636 if (value != NULL && strcmp(value, "removed:top") == 0) {
1637 crm_trace("We are the root of the deletion: %s.id=%s", name, id);
1638 free_xml(target);
1639 free(id);
1640 return;
1641 }
1642
1643 for (xIter = crm_first_attr(patch); xIter != NULL; xIter = xIter->next) {
1644 const char *p_name = (const char *)xIter->name;
1645
1646
1647 if (safe_str_neq(p_name, XML_ATTR_ID)) {
1648 xml_remove_prop(target, p_name);
1649 }
1650 }
1651
1652
1653 cIter = __xml_first_child(target);
1654 while (cIter) {
1655 xmlNode *target_child = cIter;
1656
1657 cIter = __xml_next(cIter);
1658 patch_child = find_element(patch, target_child, FALSE);
1659 __subtract_xml_object(target_child, patch_child);
1660 }
1661 free(id);
1662 }
1663
1664 static void
1665 __add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * patch)
1666 {
1667 xmlNode *patch_child = NULL;
1668 xmlNode *target_child = NULL;
1669 xmlAttrPtr xIter = NULL;
1670
1671 const char *id = NULL;
1672 const char *name = NULL;
1673 const char *value = NULL;
1674
1675 if (patch == NULL) {
1676 return;
1677 } else if (parent == NULL && target == NULL) {
1678 return;
1679 }
1680
1681
1682 value = crm_element_value(patch, XML_DIFF_MARKER);
1683 if (target == NULL
1684 && value != NULL
1685 && strcmp(value, "added:top") == 0) {
1686 id = ID(patch);
1687 name = crm_element_name(patch);
1688 crm_trace("We are the root of the addition: %s.id=%s", name, id);
1689 add_node_copy(parent, patch);
1690 return;
1691
1692 } else if(target == NULL) {
1693 id = ID(patch);
1694 name = crm_element_name(patch);
1695 crm_err("Could not locate: %s.id=%s", name, id);
1696 return;
1697 }
1698
1699 if (target->type == XML_COMMENT_NODE) {
1700 add_xml_comment(parent, target, patch);
1701 }
1702
1703 name = crm_element_name(target);
1704 CRM_CHECK(name != NULL, return);
1705 CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(patch)), return);
1706 CRM_CHECK(safe_str_eq(ID(target), ID(patch)), return);
1707
1708 for (xIter = crm_first_attr(patch); xIter != NULL; xIter = xIter->next) {
1709 const char *p_name = (const char *)xIter->name;
1710 const char *p_value = crm_element_value(patch, p_name);
1711
1712 xml_remove_prop(target, p_name);
1713 crm_xml_add(target, p_name, p_value);
1714 }
1715
1716
1717 for (patch_child = __xml_first_child(patch); patch_child != NULL;
1718 patch_child = __xml_next(patch_child)) {
1719
1720 target_child = find_element(target, patch_child, FALSE);
1721 __add_xml_object(target, target_child, patch_child);
1722 }
1723 }
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736 static bool
1737 find_patch_xml_node(xmlNode *patchset, int format, bool added,
1738 xmlNode **patch_node)
1739 {
1740 xmlNode *cib_node;
1741 const char *label;
1742
1743 switch(format) {
1744 case 1:
1745 label = added? "diff-added" : "diff-removed";
1746 *patch_node = find_xml_node(patchset, label, FALSE);
1747 cib_node = find_xml_node(*patch_node, "cib", FALSE);
1748 if (cib_node != NULL) {
1749 *patch_node = cib_node;
1750 }
1751 break;
1752 case 2:
1753 label = added? "target" : "source";
1754 *patch_node = find_xml_node(patchset, "version", FALSE);
1755 *patch_node = find_xml_node(*patch_node, label, FALSE);
1756 break;
1757 default:
1758 crm_warn("Unknown patch format: %d", format);
1759 *patch_node = NULL;
1760 return FALSE;
1761 }
1762 return TRUE;
1763 }
1764
1765 bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3])
1766 {
1767 int lpc = 0;
1768 int format = 1;
1769 xmlNode *tmp = NULL;
1770
1771 const char *vfields[] = {
1772 XML_ATTR_GENERATION_ADMIN,
1773 XML_ATTR_GENERATION,
1774 XML_ATTR_NUMUPDATES,
1775 };
1776
1777
1778 crm_element_value_int(patchset, "format", &format);
1779
1780
1781 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
1782 return -EINVAL;
1783 }
1784 if (tmp) {
1785 for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1786 crm_element_value_int(tmp, vfields[lpc], &(del[lpc]));
1787 crm_trace("Got %d for del[%s]", del[lpc], vfields[lpc]);
1788 }
1789 }
1790
1791
1792 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
1793 return -EINVAL;
1794 }
1795 if (tmp) {
1796 for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1797 crm_element_value_int(tmp, vfields[lpc], &(add[lpc]));
1798 crm_trace("Got %d for add[%s]", add[lpc], vfields[lpc]);
1799 }
1800 }
1801
1802 return pcmk_ok;
1803 }
1804
1805 static int
1806 xml_patch_version_check(xmlNode *xml, xmlNode *patchset, int format)
1807 {
1808 int lpc = 0;
1809 bool changed = FALSE;
1810
1811 int this[] = { 0, 0, 0 };
1812 int add[] = { 0, 0, 0 };
1813 int del[] = { 0, 0, 0 };
1814
1815 const char *vfields[] = {
1816 XML_ATTR_GENERATION_ADMIN,
1817 XML_ATTR_GENERATION,
1818 XML_ATTR_NUMUPDATES,
1819 };
1820
1821 for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1822 crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
1823 crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
1824 if (this[lpc] < 0) {
1825 this[lpc] = 0;
1826 }
1827 }
1828
1829
1830 add[0] = this[0];
1831 add[1] = this[1];
1832 add[2] = this[2] + 1;
1833 for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1834 del[lpc] = this[lpc];
1835 }
1836
1837 xml_patch_versions(patchset, add, del);
1838
1839 for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1840 if(this[lpc] < del[lpc]) {
1841 crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)", vfields[lpc],
1842 this[0], this[1], this[2], del[0], del[1], del[2], add[0], add[1], add[2]);
1843 return -pcmk_err_diff_resync;
1844
1845 } else if(this[lpc] > del[lpc]) {
1846 crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p", vfields[lpc],
1847 this[0], this[1], this[2], del[0], del[1], del[2], add[0], add[1], add[2], patchset);
1848 crm_log_xml_info(patchset, "OldPatch");
1849 return -pcmk_err_old_data;
1850 }
1851 }
1852
1853 for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1854 if(add[lpc] > del[lpc]) {
1855 changed = TRUE;
1856 }
1857 }
1858
1859 if(changed == FALSE) {
1860 crm_notice("Versions did not change in patch %d.%d.%d", add[0], add[1], add[2]);
1861 return -pcmk_err_old_data;
1862 }
1863
1864 crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
1865 add[0], add[1], add[2], this[0], this[1], this[2]);
1866 return pcmk_ok;
1867 }
1868
1869 static int
1870 xml_apply_patchset_v1(xmlNode *xml, xmlNode *patchset)
1871 {
1872 int rc = pcmk_ok;
1873 int root_nodes_seen = 0;
1874
1875 xmlNode *child_diff = NULL;
1876 xmlNode *added = find_xml_node(patchset, "diff-added", FALSE);
1877 xmlNode *removed = find_xml_node(patchset, "diff-removed", FALSE);
1878 xmlNode *old = copy_xml(xml);
1879
1880 crm_trace("Subtraction Phase");
1881 for (child_diff = __xml_first_child(removed); child_diff != NULL;
1882 child_diff = __xml_next(child_diff)) {
1883 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1884 if (root_nodes_seen == 0) {
1885 __subtract_xml_object(xml, child_diff);
1886 }
1887 root_nodes_seen++;
1888 }
1889
1890 if (root_nodes_seen > 1) {
1891 crm_err("(-) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1892 rc = -ENOTUNIQ;
1893 }
1894
1895 root_nodes_seen = 0;
1896 crm_trace("Addition Phase");
1897 if (rc == pcmk_ok) {
1898 xmlNode *child_diff = NULL;
1899
1900 for (child_diff = __xml_first_child(added); child_diff != NULL;
1901 child_diff = __xml_next(child_diff)) {
1902 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1903 if (root_nodes_seen == 0) {
1904 __add_xml_object(NULL, xml, child_diff);
1905 }
1906 root_nodes_seen++;
1907 }
1908 }
1909
1910 if (root_nodes_seen > 1) {
1911 crm_err("(+) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1912 rc = -ENOTUNIQ;
1913 }
1914
1915 purge_diff_markers(xml);
1916
1917 free_xml(old);
1918 return rc;
1919 }
1920
1921 static xmlNode *
1922 __first_xml_child_match(xmlNode *parent, const char *name, const char *id, int position)
1923 {
1924 xmlNode *cIter = NULL;
1925
1926 for (cIter = __xml_first_child(parent); cIter != NULL; cIter = __xml_next(cIter)) {
1927 if(strcmp((const char *)cIter->name, name) != 0) {
1928 continue;
1929 } else if(id) {
1930 const char *cid = ID(cIter);
1931 if(cid == NULL || strcmp(cid, id) != 0) {
1932 continue;
1933 }
1934 }
1935
1936
1937 if (cIter->type == XML_COMMENT_NODE
1938 && position >= 0
1939 && __xml_offset(cIter) != position) {
1940 continue;
1941 }
1942
1943 return cIter;
1944 }
1945 return NULL;
1946 }
1947
1948 static xmlNode *
1949 __xml_find_path(xmlNode *top, const char *key, int target_position)
1950 {
1951 xmlNode *target = (xmlNode*)top->doc;
1952 char *id = malloc(XML_BUFFER_SIZE);
1953 char *tag = malloc(XML_BUFFER_SIZE);
1954 char *section = malloc(XML_BUFFER_SIZE);
1955 char *current = strdup(key);
1956 char *remainder = malloc(XML_BUFFER_SIZE);
1957 int rc = 0;
1958
1959 while(current) {
1960 rc = sscanf (current, "/%[^/]%s", section, remainder);
1961 if(rc <= 0) {
1962 crm_trace("Done");
1963 break;
1964
1965 } else if(rc > 2) {
1966 crm_trace("Aborting on %s", current);
1967 target = NULL;
1968 break;
1969
1970 } else if(tag && section) {
1971 int f = sscanf (section, "%[^[][@id='%[^']", tag, id);
1972 int current_position = -1;
1973
1974
1975 if (rc == 1 && target_position >= 0) {
1976 current_position = target_position;
1977 }
1978
1979 switch(f) {
1980 case 1:
1981 target = __first_xml_child_match(target, tag, NULL, current_position);
1982 break;
1983 case 2:
1984 target = __first_xml_child_match(target, tag, id, current_position);
1985 break;
1986 default:
1987 crm_trace("Aborting on %s", section);
1988 target = NULL;
1989 break;
1990 }
1991
1992 if(rc == 1 || target == NULL) {
1993 crm_trace("Done");
1994 break;
1995
1996 } else {
1997 char *tmp = current;
1998 current = remainder;
1999 remainder = tmp;
2000 }
2001 }
2002 }
2003
2004 if(target) {
2005 char *path = (char *)xmlGetNodePath(target);
2006
2007 crm_trace("Found %s for %s", path, key);
2008 free(path);
2009 } else {
2010 crm_debug("No match for %s", key);
2011 }
2012
2013 free(remainder);
2014 free(current);
2015 free(section);
2016 free(tag);
2017 free(id);
2018 return target;
2019 }
2020
2021 static int
2022 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
2023 {
2024 int rc = pcmk_ok;
2025 xmlNode *change = NULL;
2026 for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
2027 xmlNode *match = NULL;
2028 const char *op = crm_element_value(change, XML_DIFF_OP);
2029 const char *xpath = crm_element_value(change, XML_DIFF_PATH);
2030 int position = -1;
2031
2032 crm_trace("Processing %s %s", change->name, op);
2033 if(op == NULL) {
2034 continue;
2035 }
2036
2037 if(strcmp(op, "delete") == 0) {
2038 crm_element_value_int(change, XML_DIFF_POSITION, &position);
2039 }
2040 #if 0
2041 match = get_xpath_object(xpath, xml, LOG_TRACE);
2042 #else
2043 match = __xml_find_path(xml, xpath, position);
2044 #endif
2045 crm_trace("Performing %s on %s with %p", op, xpath, match);
2046
2047 if(match == NULL && strcmp(op, "delete") == 0) {
2048 crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
2049 continue;
2050
2051 } else if(match == NULL) {
2052 crm_err("No %s match for %s in %p", op, xpath, xml->doc);
2053 rc = -pcmk_err_diff_failed;
2054 continue;
2055
2056 } else if(strcmp(op, "create") == 0) {
2057 int position = 0;
2058 xmlNode *child = NULL;
2059 xmlNode *match_child = NULL;
2060
2061 match_child = match->children;
2062 crm_element_value_int(change, XML_DIFF_POSITION, &position);
2063
2064 while(match_child && position != __xml_offset(match_child)) {
2065 match_child = match_child->next;
2066 }
2067
2068 child = xmlDocCopyNode(change->children, match->doc, 1);
2069 if(match_child) {
2070 crm_trace("Adding %s at position %d", child->name, position);
2071 xmlAddPrevSibling(match_child, child);
2072
2073 } else if(match->last) {
2074 crm_trace("Adding %s at position %d (end)", child->name, position);
2075 xmlAddNextSibling(match->last, child);
2076
2077 } else {
2078 crm_trace("Adding %s at position %d (first)", child->name, position);
2079 CRM_LOG_ASSERT(position == 0);
2080 xmlAddChild(match, child);
2081 }
2082 crm_node_created(child);
2083
2084 } else if(strcmp(op, "move") == 0) {
2085 int position = 0;
2086
2087 crm_element_value_int(change, XML_DIFF_POSITION, &position);
2088 if(position != __xml_offset(match)) {
2089 xmlNode *match_child = NULL;
2090 int p = position;
2091
2092 if(p > __xml_offset(match)) {
2093 p++;
2094 }
2095
2096 CRM_ASSERT(match->parent != NULL);
2097 match_child = match->parent->children;
2098
2099 while(match_child && p != __xml_offset(match_child)) {
2100 match_child = match_child->next;
2101 }
2102
2103 crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
2104 match->name, position, __xml_offset(match), match->prev,
2105 match_child?"next":"last", match_child?match_child:match->parent->last);
2106
2107 if(match_child) {
2108 xmlAddPrevSibling(match_child, match);
2109
2110 } else {
2111 CRM_ASSERT(match->parent->last != NULL);
2112 xmlAddNextSibling(match->parent->last, match);
2113 }
2114
2115 } else {
2116 crm_trace("%s is already in position %d", match->name, position);
2117 }
2118
2119 if(position != __xml_offset(match)) {
2120 crm_err("Moved %s.%d to position %d instead of %d (%p)",
2121 match->name, ID(match), __xml_offset(match), position, match->prev);
2122 rc = -pcmk_err_diff_failed;
2123 }
2124
2125 } else if(strcmp(op, "delete") == 0) {
2126 free_xml(match);
2127
2128 } else if(strcmp(op, "modify") == 0) {
2129 xmlAttr *pIter = crm_first_attr(match);
2130 xmlNode *attrs = __xml_first_child(first_named_child(change, XML_DIFF_RESULT));
2131
2132 if(attrs == NULL) {
2133 rc = -ENOMSG;
2134 continue;
2135 }
2136 while(pIter != NULL) {
2137 const char *name = (const char *)pIter->name;
2138
2139 pIter = pIter->next;
2140 xml_remove_prop(match, name);
2141 }
2142
2143 for (pIter = crm_first_attr(attrs); pIter != NULL; pIter = pIter->next) {
2144 const char *name = (const char *)pIter->name;
2145 const char *value = crm_element_value(attrs, name);
2146
2147 crm_xml_add(match, name, value);
2148 }
2149
2150 } else {
2151 crm_err("Unknown operation: %s", op);
2152 }
2153 }
2154 return rc;
2155 }
2156
2157 int
2158 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
2159 {
2160 int format = 1;
2161 int rc = pcmk_ok;
2162 xmlNode *old = NULL;
2163 const char *digest = crm_element_value(patchset, XML_ATTR_DIGEST);
2164
2165 if(patchset == NULL) {
2166 return rc;
2167 }
2168
2169 xml_log_patchset(LOG_TRACE, __FUNCTION__, patchset);
2170
2171 crm_element_value_int(patchset, "format", &format);
2172 if(check_version) {
2173 rc = xml_patch_version_check(xml, patchset, format);
2174 if(rc != pcmk_ok) {
2175 return rc;
2176 }
2177 }
2178
2179 if(digest) {
2180
2181 old = copy_xml(xml);
2182 }
2183
2184 if(rc == pcmk_ok) {
2185 switch(format) {
2186 case 1:
2187 rc = xml_apply_patchset_v1(xml, patchset);
2188 break;
2189 case 2:
2190 rc = xml_apply_patchset_v2(xml, patchset);
2191 break;
2192 default:
2193 crm_err("Unknown patch format: %d", format);
2194 rc = -EINVAL;
2195 }
2196 }
2197
2198 if(rc == pcmk_ok && digest) {
2199 static struct qb_log_callsite *digest_cs = NULL;
2200
2201 char *new_digest = NULL;
2202 char *version = crm_element_value_copy(xml, XML_ATTR_CRM_VERSION);
2203
2204 if (digest_cs == NULL) {
2205 digest_cs =
2206 qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
2207 crm_trace_nonlog);
2208 }
2209
2210 new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
2211 if (safe_str_neq(new_digest, digest)) {
2212 crm_info("v%d digest mis-match: expected %s, calculated %s", format, digest, new_digest);
2213 rc = -pcmk_err_diff_failed;
2214
2215 if (digest_cs && digest_cs->targets) {
2216 save_xml_to_file(old, "PatchDigest:input", NULL);
2217 save_xml_to_file(xml, "PatchDigest:result", NULL);
2218 save_xml_to_file(patchset,"PatchDigest:diff", NULL);
2219
2220 } else {
2221 crm_trace("%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
2222 }
2223
2224 } else {
2225 crm_trace("v%d digest matched: expected %s, calculated %s", format, digest, new_digest);
2226 }
2227 free(new_digest);
2228 free(version);
2229 }
2230 free_xml(old);
2231 return rc;
2232 }
2233
2234 xmlNode *
2235 find_xml_node(xmlNode * root, const char *search_path, gboolean must_find)
2236 {
2237 xmlNode *a_child = NULL;
2238 const char *name = "NULL";
2239
2240 if (root != NULL) {
2241 name = crm_element_name(root);
2242 }
2243
2244 if (search_path == NULL) {
2245 crm_warn("Will never find <NULL>");
2246 return NULL;
2247 }
2248
2249 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
2250 if (strcmp((const char *)a_child->name, search_path) == 0) {
2251
2252 return a_child;
2253 }
2254 }
2255
2256 if (must_find) {
2257 crm_warn("Could not find %s in %s.", search_path, name);
2258 } else if (root != NULL) {
2259 crm_trace("Could not find %s in %s.", search_path, name);
2260 } else {
2261 crm_trace("Could not find %s in <NULL>.", search_path);
2262 }
2263
2264 return NULL;
2265 }
2266
2267 xmlNode *
2268 find_entity(xmlNode * parent, const char *node_name, const char *id)
2269 {
2270 xmlNode *a_child = NULL;
2271
2272 for (a_child = __xml_first_child(parent); a_child != NULL; a_child = __xml_next(a_child)) {
2273
2274 if (node_name == NULL || strcmp((const char *)a_child->name, node_name) == 0) {
2275 const char *cid = ID(a_child);
2276 if (id == NULL || (cid != NULL && strcmp(id, cid) == 0)) {
2277 return a_child;
2278 }
2279 }
2280 }
2281
2282 crm_trace("node <%s id=%s> not found in %s.", node_name, id, crm_element_name(parent));
2283 return NULL;
2284 }
2285
2286 void
2287 copy_in_properties(xmlNode * target, xmlNode * src)
2288 {
2289 if (src == NULL) {
2290 crm_warn("No node to copy properties from");
2291
2292 } else if (target == NULL) {
2293 crm_err("No node to copy properties into");
2294
2295 } else {
2296 xmlAttrPtr pIter = NULL;
2297
2298 for (pIter = crm_first_attr(src); pIter != NULL; pIter = pIter->next) {
2299 const char *p_name = (const char *)pIter->name;
2300 const char *p_value = crm_attr_value(pIter);
2301
2302 expand_plus_plus(target, p_name, p_value);
2303 }
2304 }
2305
2306 return;
2307 }
2308
2309 void
2310 fix_plus_plus_recursive(xmlNode * target)
2311 {
2312
2313 xmlNode *child = NULL;
2314 xmlAttrPtr pIter = NULL;
2315
2316 for (pIter = crm_first_attr(target); pIter != NULL; pIter = pIter->next) {
2317 const char *p_name = (const char *)pIter->name;
2318 const char *p_value = crm_attr_value(pIter);
2319
2320 expand_plus_plus(target, p_name, p_value);
2321 }
2322 for (child = __xml_first_child(target); child != NULL; child = __xml_next(child)) {
2323 fix_plus_plus_recursive(child);
2324 }
2325 }
2326
2327 void
2328 expand_plus_plus(xmlNode * target, const char *name, const char *value)
2329 {
2330 int offset = 1;
2331 int name_len = 0;
2332 int int_value = 0;
2333 int value_len = 0;
2334
2335 const char *old_value = NULL;
2336
2337 if (value == NULL || name == NULL) {
2338 return;
2339 }
2340
2341 old_value = crm_element_value(target, name);
2342
2343 if (old_value == NULL) {
2344
2345 goto set_unexpanded;
2346
2347 } else if (strstr(value, name) != value) {
2348 goto set_unexpanded;
2349 }
2350
2351 name_len = strlen(name);
2352 value_len = strlen(value);
2353 if (value_len < (name_len + 2)
2354 || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
2355 goto set_unexpanded;
2356 }
2357
2358
2359
2360
2361 if (old_value != value) {
2362 int_value = char2score(old_value);
2363 }
2364
2365 if (value[name_len + 1] != '+') {
2366 const char *offset_s = value + (name_len + 2);
2367
2368 offset = char2score(offset_s);
2369 }
2370 int_value += offset;
2371
2372 if (int_value > INFINITY) {
2373 int_value = (int)INFINITY;
2374 }
2375
2376 crm_xml_add_int(target, name, int_value);
2377 return;
2378
2379 set_unexpanded:
2380 if (old_value == value) {
2381
2382 return;
2383 }
2384 crm_xml_add(target, name, value);
2385 return;
2386 }
2387
2388 xmlDoc *
2389 getDocPtr(xmlNode * node)
2390 {
2391 xmlDoc *doc = NULL;
2392
2393 CRM_CHECK(node != NULL, return NULL);
2394
2395 doc = node->doc;
2396 if (doc == NULL) {
2397 doc = xmlNewDoc((const xmlChar *)"1.0");
2398 xmlDocSetRootElement(doc, node);
2399 xmlSetTreeDoc(node, doc);
2400 }
2401 return doc;
2402 }
2403
2404 xmlNode *
2405 add_node_copy(xmlNode * parent, xmlNode * src_node)
2406 {
2407 xmlNode *child = NULL;
2408 xmlDoc *doc = getDocPtr(parent);
2409
2410 CRM_CHECK(src_node != NULL, return NULL);
2411
2412 child = xmlDocCopyNode(src_node, doc, 1);
2413 xmlAddChild(parent, child);
2414 crm_node_created(child);
2415 return child;
2416 }
2417
2418 int
2419 add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child)
2420 {
2421 add_node_copy(parent, child);
2422 free_xml(child);
2423 return 1;
2424 }
2425
2426 static bool
2427 __xml_acl_check(xmlNode *xml, const char *name, enum xml_private_flags mode)
2428 {
2429 CRM_ASSERT(xml);
2430 CRM_ASSERT(xml->doc);
2431 CRM_ASSERT(xml->doc->_private);
2432
2433 #if ENABLE_ACL
2434 {
2435 if(TRACKING_CHANGES(xml) && xml_acl_enabled(xml)) {
2436 int offset = 0;
2437 xmlNode *parent = xml;
2438 char buffer[XML_BUFFER_SIZE];
2439 xml_private_t *docp = xml->doc->_private;
2440
2441 if(docp->acls == NULL) {
2442 crm_trace("Ordinary user %s cannot access the CIB without any defined ACLs", docp->user);
2443 set_doc_flag(xml, xpf_acl_denied);
2444 return FALSE;
2445 }
2446
2447 offset = __get_prefix(NULL, xml, buffer, offset);
2448 if(name) {
2449 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "[@%s]", name);
2450 }
2451 CRM_LOG_ASSERT(offset > 0);
2452
2453
2454
2455
2456
2457
2458 if(name) {
2459 xmlAttr *attr = xmlHasProp(xml, (const xmlChar *)name);
2460
2461 if(attr && mode == xpf_acl_create) {
2462 mode = xpf_acl_write;
2463 }
2464 }
2465
2466 while(parent && parent->_private) {
2467 xml_private_t *p = parent->_private;
2468 if(__xml_acl_mode_test(p->flags, mode)) {
2469 return TRUE;
2470
2471 } else if(is_set(p->flags, xpf_acl_deny)) {
2472 crm_trace("%x access denied to %s: parent", mode, buffer);
2473 set_doc_flag(xml, xpf_acl_denied);
2474 return FALSE;
2475 }
2476 parent = parent->parent;
2477 }
2478
2479 crm_trace("%x access denied to %s: default", mode, buffer);
2480 set_doc_flag(xml, xpf_acl_denied);
2481 return FALSE;
2482 }
2483 }
2484 #endif
2485
2486 return TRUE;
2487 }
2488
2489 const char *
2490 crm_xml_add(xmlNode * node, const char *name, const char *value)
2491 {
2492 bool dirty = FALSE;
2493 xmlAttr *attr = NULL;
2494
2495 CRM_CHECK(node != NULL, return NULL);
2496 CRM_CHECK(name != NULL, return NULL);
2497
2498 if (value == NULL) {
2499 return NULL;
2500 }
2501 #if XML_PARANOIA_CHECKS
2502 {
2503 const char *old_value = NULL;
2504
2505 old_value = crm_element_value(node, name);
2506
2507
2508 CRM_CHECK(old_value != value, crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
2509 return value);
2510 }
2511 #endif
2512
2513 if(TRACKING_CHANGES(node)) {
2514 const char *old = crm_element_value(node, name);
2515
2516 if(old == NULL || value == NULL || strcmp(old, value) != 0) {
2517 dirty = TRUE;
2518 }
2519 }
2520
2521 if(dirty && __xml_acl_check(node, name, xpf_acl_create) == FALSE) {
2522 crm_trace("Cannot add %s=%s to %s", name, value, node->name);
2523 return NULL;
2524 }
2525
2526 attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
2527 if(dirty) {
2528 crm_attr_dirty(attr);
2529 }
2530
2531 CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
2532 return (char *)attr->children->content;
2533 }
2534
2535 const char *
2536 crm_xml_replace(xmlNode * node, const char *name, const char *value)
2537 {
2538 bool dirty = FALSE;
2539 xmlAttr *attr = NULL;
2540 const char *old_value = NULL;
2541
2542 CRM_CHECK(node != NULL, return NULL);
2543 CRM_CHECK(name != NULL && name[0] != 0, return NULL);
2544
2545 old_value = crm_element_value(node, name);
2546
2547
2548 CRM_CHECK(old_value != value, return value);
2549
2550 if(__xml_acl_check(node, name, xpf_acl_write) == FALSE) {
2551
2552 crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
2553 return NULL;
2554
2555 } else if (old_value != NULL && value == NULL) {
2556 xml_remove_prop(node, name);
2557 return NULL;
2558
2559 } else if (value == NULL) {
2560 return NULL;
2561 }
2562
2563 if(TRACKING_CHANGES(node)) {
2564 if(old_value == NULL || value == NULL || strcmp(old_value, value) != 0) {
2565 dirty = TRUE;
2566 }
2567 }
2568
2569 attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
2570 if(dirty) {
2571 crm_attr_dirty(attr);
2572 }
2573 CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
2574 return (char *)attr->children->content;
2575 }
2576
2577 const char *
2578 crm_xml_add_int(xmlNode * node, const char *name, int value)
2579 {
2580 char *number = crm_itoa(value);
2581 const char *added = crm_xml_add(node, name, number);
2582
2583 free(number);
2584 return added;
2585 }
2586
2587 xmlNode *
2588 create_xml_node(xmlNode * parent, const char *name)
2589 {
2590 xmlDoc *doc = NULL;
2591 xmlNode *node = NULL;
2592
2593 if (name == NULL || name[0] == 0) {
2594 CRM_CHECK(name != NULL && name[0] == 0, return NULL);
2595 return NULL;
2596 }
2597
2598 if (parent == NULL) {
2599 doc = xmlNewDoc((const xmlChar *)"1.0");
2600 node = xmlNewDocRawNode(doc, NULL, (const xmlChar *)name, NULL);
2601 xmlDocSetRootElement(doc, node);
2602
2603 } else {
2604 doc = getDocPtr(parent);
2605 node = xmlNewDocRawNode(doc, NULL, (const xmlChar *)name, NULL);
2606 xmlAddChild(parent, node);
2607 }
2608 crm_node_created(node);
2609 return node;
2610 }
2611
2612 static inline int
2613 __get_prefix(const char *prefix, xmlNode *xml, char *buffer, int offset)
2614 {
2615 const char *id = ID(xml);
2616
2617 if(offset == 0 && prefix == NULL && xml->parent) {
2618 offset = __get_prefix(NULL, xml->parent, buffer, offset);
2619 }
2620
2621 if(id) {
2622 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "/%s[@id='%s']", (const char *)xml->name, id);
2623 } else if(xml->name) {
2624 offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "/%s", (const char *)xml->name);
2625 }
2626
2627 return offset;
2628 }
2629
2630 char *
2631 xml_get_path(xmlNode *xml)
2632 {
2633 int offset = 0;
2634 char buffer[XML_BUFFER_SIZE];
2635
2636 if(__get_prefix(NULL, xml, buffer, offset) > 0) {
2637 return strdup(buffer);
2638 }
2639 return NULL;
2640 }
2641
2642 static void
2643 free_xml_with_position(xmlNode * child, int position)
2644 {
2645 if (child != NULL) {
2646 xmlNode *top = NULL;
2647 xmlDoc *doc = child->doc;
2648 xml_private_t *p = child->_private;
2649
2650 if (doc != NULL) {
2651 top = xmlDocGetRootElement(doc);
2652 }
2653
2654 if (doc != NULL && top == child) {
2655
2656 xmlFreeDoc(doc);
2657
2658 } else if(__xml_acl_check(child, NULL, xpf_acl_write) == FALSE) {
2659 int offset = 0;
2660 char buffer[XML_BUFFER_SIZE];
2661
2662 __get_prefix(NULL, child, buffer, offset);
2663 crm_trace("Cannot remove %s %x", buffer, p->flags);
2664 return;
2665
2666 } else {
2667 if(doc && TRACKING_CHANGES(child) && is_not_set(p->flags, xpf_created)) {
2668 int offset = 0;
2669 char buffer[XML_BUFFER_SIZE];
2670
2671 if(__get_prefix(NULL, child, buffer, offset) > 0) {
2672 xml_deleted_obj_t *deleted_obj = calloc(1, sizeof(xml_deleted_obj_t));
2673
2674 crm_trace("Deleting %s %p from %p", buffer, child, doc);
2675
2676 deleted_obj->path = strdup(buffer);
2677
2678 deleted_obj->position = -1;
2679
2680 if (child->type == XML_COMMENT_NODE) {
2681 if (position >= 0) {
2682 deleted_obj->position = position;
2683
2684 } else {
2685 deleted_obj->position = __xml_offset(child);
2686 }
2687 }
2688
2689 p = doc->_private;
2690 p->deleted_objs = g_list_append(p->deleted_objs, deleted_obj);
2691 set_doc_flag(child, xpf_dirty);
2692 }
2693 }
2694
2695
2696
2697
2698 xmlUnlinkNode(child);
2699 xmlFreeNode(child);
2700 }
2701 }
2702 }
2703
2704
2705 void
2706 free_xml(xmlNode * child)
2707 {
2708 free_xml_with_position(child, -1);
2709 }
2710
2711 xmlNode *
2712 copy_xml(xmlNode * src)
2713 {
2714 xmlDoc *doc = xmlNewDoc((const xmlChar *)"1.0");
2715 xmlNode *copy = xmlDocCopyNode(src, doc, 1);
2716
2717 xmlDocSetRootElement(doc, copy);
2718 xmlSetTreeDoc(copy, doc);
2719 return copy;
2720 }
2721
2722 static void
2723 crm_xml_err(void *ctx, const char *fmt, ...)
2724 G_GNUC_PRINTF(2, 3);
2725
2726 static void
2727 crm_xml_err(void *ctx, const char *fmt, ...)
2728 {
2729 va_list ap;
2730 static struct qb_log_callsite *xml_error_cs = NULL;
2731
2732 if (xml_error_cs == NULL) {
2733 xml_error_cs = qb_log_callsite_get(
2734 __func__, __FILE__, "xml library error", LOG_TRACE, __LINE__, crm_trace_nonlog);
2735 }
2736
2737 va_start(ap, fmt);
2738 if (xml_error_cs && xml_error_cs->targets) {
2739 CRM_XML_LOG_BASE(LOG_ERR, TRUE,
2740 crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, "xml library error",
2741 TRUE, TRUE),
2742 "XML Error: ", fmt, ap);
2743 } else {
2744 CRM_XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
2745 }
2746 va_end(ap);
2747 }
2748
2749 xmlNode *
2750 string2xml(const char *input)
2751 {
2752 xmlNode *xml = NULL;
2753 xmlDocPtr output = NULL;
2754 xmlParserCtxtPtr ctxt = NULL;
2755 xmlErrorPtr last_error = NULL;
2756
2757 if (input == NULL) {
2758 crm_err("Can't parse NULL input");
2759 return NULL;
2760 }
2761
2762
2763 ctxt = xmlNewParserCtxt();
2764 CRM_CHECK(ctxt != NULL, return NULL);
2765
2766
2767
2768 xmlCtxtResetLastError(ctxt);
2769 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2770
2771 output =
2772 xmlCtxtReadDoc(ctxt, (const xmlChar *)input, NULL, NULL,
2773 XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
2774 if (output) {
2775 xml = xmlDocGetRootElement(output);
2776 }
2777 last_error = xmlCtxtGetLastError(ctxt);
2778 if (last_error && last_error->code != XML_ERR_OK) {
2779
2780
2781
2782
2783
2784 crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
2785 last_error->domain, last_error->level, last_error->code, last_error->message);
2786
2787 if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
2788 CRM_LOG_ASSERT("Cannot parse an empty string");
2789
2790 } else if (last_error->code != XML_ERR_DOCUMENT_END) {
2791 crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
2792 input);
2793 if (xml != NULL) {
2794 crm_log_xml_err(xml, "Partial");
2795 }
2796
2797 } else {
2798 int len = strlen(input);
2799 int lpc = 0;
2800
2801 while(lpc < len) {
2802 crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
2803 lpc += 80;
2804 }
2805
2806 CRM_LOG_ASSERT("String parsing error");
2807 }
2808 }
2809
2810 xmlFreeParserCtxt(ctxt);
2811 return xml;
2812 }
2813
2814 xmlNode *
2815 stdin2xml(void)
2816 {
2817 size_t data_length = 0;
2818 size_t read_chars = 0;
2819
2820 char *xml_buffer = NULL;
2821 xmlNode *xml_obj = NULL;
2822
2823 do {
2824 size_t next = XML_BUFFER_SIZE + data_length + 1;
2825
2826 if(next <= 0) {
2827 crm_err("Buffer size exceeded at: %l + %d", data_length, XML_BUFFER_SIZE);
2828 break;
2829 }
2830
2831 xml_buffer = realloc_safe(xml_buffer, next);
2832 read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin);
2833 data_length += read_chars;
2834 } while (read_chars > 0);
2835
2836 if (data_length == 0) {
2837 crm_warn("No XML supplied on stdin");
2838 free(xml_buffer);
2839 return NULL;
2840 }
2841
2842 xml_buffer[data_length] = '\0';
2843
2844 xml_obj = string2xml(xml_buffer);
2845 free(xml_buffer);
2846
2847 crm_log_xml_trace(xml_obj, "Created fragment");
2848 return xml_obj;
2849 }
2850
2851 static char *
2852 decompress_file(const char *filename)
2853 {
2854 char *buffer = NULL;
2855
2856 #if HAVE_BZLIB_H
2857 int rc = 0;
2858 size_t length = 0, read_len = 0;
2859
2860 BZFILE *bz_file = NULL;
2861 FILE *input = fopen(filename, "r");
2862
2863 if (input == NULL) {
2864 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
2865 return NULL;
2866 }
2867
2868 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
2869
2870 if (rc != BZ_OK) {
2871 BZ2_bzReadClose(&rc, bz_file);
2872 return NULL;
2873 }
2874
2875 rc = BZ_OK;
2876 while (rc == BZ_OK) {
2877 buffer = realloc_safe(buffer, XML_BUFFER_SIZE + length + 1);
2878 read_len = BZ2_bzRead(&rc, bz_file, buffer + length, XML_BUFFER_SIZE);
2879
2880 crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
2881
2882 if (rc == BZ_OK || rc == BZ_STREAM_END) {
2883 length += read_len;
2884 }
2885 }
2886
2887 buffer[length] = '\0';
2888
2889 if (rc != BZ_STREAM_END) {
2890 crm_err("Couldn't read compressed xml from file");
2891 free(buffer);
2892 buffer = NULL;
2893 }
2894
2895 BZ2_bzReadClose(&rc, bz_file);
2896 fclose(input);
2897
2898 #else
2899 crm_err("Cannot read compressed files:" " bzlib was not available at compile time");
2900 #endif
2901 return buffer;
2902 }
2903
2904 void
2905 strip_text_nodes(xmlNode * xml)
2906 {
2907 xmlNode *iter = xml->children;
2908
2909 while (iter) {
2910 xmlNode *next = iter->next;
2911
2912 switch (iter->type) {
2913 case XML_TEXT_NODE:
2914
2915 xmlUnlinkNode(iter);
2916 xmlFreeNode(iter);
2917 break;
2918
2919 case XML_ELEMENT_NODE:
2920
2921 strip_text_nodes(iter);
2922 break;
2923
2924 default:
2925
2926 break;
2927 }
2928
2929 iter = next;
2930 }
2931 }
2932
2933 xmlNode *
2934 filename2xml(const char *filename)
2935 {
2936 xmlNode *xml = NULL;
2937 xmlDocPtr output = NULL;
2938 gboolean uncompressed = TRUE;
2939 xmlParserCtxtPtr ctxt = NULL;
2940 xmlErrorPtr last_error = NULL;
2941 static int xml_options = XML_PARSE_NOBLANKS | XML_PARSE_RECOVER;
2942
2943
2944 ctxt = xmlNewParserCtxt();
2945 CRM_CHECK(ctxt != NULL, return NULL);
2946
2947
2948
2949 xmlCtxtResetLastError(ctxt);
2950 xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2951
2952
2953 if (filename) {
2954 uncompressed = !crm_ends_with_ext(filename, ".bz2");
2955 }
2956
2957 if (filename == NULL) {
2958
2959 output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, xml_options);
2960
2961 } else if (uncompressed) {
2962 output = xmlCtxtReadFile(ctxt, filename, NULL, xml_options);
2963
2964 } else {
2965 char *input = decompress_file(filename);
2966
2967 output = xmlCtxtReadDoc(ctxt, (const xmlChar *)input, NULL, NULL, xml_options);
2968 free(input);
2969 }
2970
2971 if (output && (xml = xmlDocGetRootElement(output))) {
2972 strip_text_nodes(xml);
2973 }
2974
2975 last_error = xmlCtxtGetLastError(ctxt);
2976 if (last_error && last_error->code != XML_ERR_OK) {
2977
2978
2979
2980
2981
2982 crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
2983 last_error->domain, last_error->level, last_error->code, last_error->message);
2984
2985 if (last_error && last_error->code != XML_ERR_OK) {
2986 crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
2987 if (xml != NULL) {
2988 crm_log_xml_err(xml, "Partial");
2989 }
2990 }
2991 }
2992
2993 xmlFreeParserCtxt(ctxt);
2994 return xml;
2995 }
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005 const char *
3006 crm_xml_add_last_written(xmlNode *xml_node)
3007 {
3008 time_t now = time(NULL);
3009 char *now_str = ctime(&now);
3010
3011 now_str[24] = EOS;
3012 return crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str);
3013 }
3014
3015
3016
3017
3018
3019
3020 void
3021 crm_xml_sanitize_id(char *id)
3022 {
3023 char *c;
3024
3025 for (c = id; *c; ++c) {
3026
3027 switch (*c) {
3028 case ':':
3029 case '#':
3030 *c = '.';
3031 }
3032 }
3033 }
3034
3035
3036
3037
3038
3039
3040
3041
3042 void
3043 crm_xml_set_id(xmlNode *xml, const char *format, ...)
3044 {
3045 va_list ap;
3046 int len = 0;
3047 char *id = NULL;
3048
3049
3050 va_start(ap, format);
3051 len = vasprintf(&id, format, ap);
3052 va_end(ap);
3053 CRM_ASSERT(len > 0);
3054
3055 crm_xml_sanitize_id(id);
3056 crm_xml_add(xml, XML_ATTR_ID, id);
3057 free(id);
3058 }
3059
3060 static int
3061 write_xml_stream(xmlNode * xml_node, const char *filename, FILE * stream, gboolean compress)
3062 {
3063 int res = 0;
3064 char *buffer = NULL;
3065 unsigned int out = 0;
3066
3067 CRM_CHECK(stream != NULL, return -1);
3068
3069 crm_trace("Writing XML out to %s", filename);
3070 if (xml_node == NULL) {
3071 crm_err("Cannot write NULL to %s", filename);
3072 fclose(stream);
3073 return -1;
3074 }
3075
3076
3077 crm_log_xml_trace(xml_node, "Writing out");
3078
3079 buffer = dump_xml_formatted(xml_node);
3080 CRM_CHECK(buffer != NULL && strlen(buffer) > 0, crm_log_xml_warn(xml_node, "dump:failed");
3081 goto bail);
3082
3083 if (compress) {
3084 #if HAVE_BZLIB_H
3085 int rc = BZ_OK;
3086 unsigned int in = 0;
3087 BZFILE *bz_file = NULL;
3088
3089 bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
3090 if (rc != BZ_OK) {
3091 crm_err("bzWriteOpen failed: %d", rc);
3092 } else {
3093 BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
3094 if (rc != BZ_OK) {
3095 crm_err("bzWrite() failed: %d", rc);
3096 }
3097 }
3098
3099 if (rc == BZ_OK) {
3100 BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
3101 if (rc != BZ_OK) {
3102 crm_err("bzWriteClose() failed: %d", rc);
3103 out = -1;
3104 } else {
3105 crm_trace("%s: In: %d, out: %d", filename, in, out);
3106 }
3107 }
3108 #else
3109 crm_err("Cannot write compressed files:" " bzlib was not available at compile time");
3110 #endif
3111 }
3112
3113 if (out <= 0) {
3114 res = fprintf(stream, "%s", buffer);
3115 if (res < 0) {
3116 crm_perror(LOG_ERR, "Cannot write output to %s", filename);
3117 goto bail;
3118 }
3119 }
3120
3121 bail:
3122
3123 if (fflush(stream) != 0) {
3124 crm_perror(LOG_ERR, "fflush for %s failed", filename);
3125 res = -1;
3126 }
3127
3128
3129 if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
3130 crm_perror(LOG_ERR, "fsync for %s failed", filename);
3131 res = -1;
3132 }
3133
3134 fclose(stream);
3135
3136 crm_trace("Saved %d bytes to the Cib as XML", res);
3137 free(buffer);
3138
3139 return res;
3140 }
3141
3142 int
3143 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
3144 {
3145 FILE *stream = NULL;
3146
3147 CRM_CHECK(fd > 0, return -1);
3148 stream = fdopen(fd, "w");
3149 return write_xml_stream(xml_node, filename, stream, compress);
3150 }
3151
3152 int
3153 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
3154 {
3155 FILE *stream = NULL;
3156
3157 stream = fopen(filename, "w");
3158
3159 return write_xml_stream(xml_node, filename, stream, compress);
3160 }
3161
3162 xmlNode *
3163 get_message_xml(xmlNode * msg, const char *field)
3164 {
3165 xmlNode *tmp = first_named_child(msg, field);
3166
3167 return __xml_first_child(tmp);
3168 }
3169
3170 gboolean
3171 add_message_xml(xmlNode * msg, const char *field, xmlNode * xml)
3172 {
3173 xmlNode *holder = create_xml_node(msg, field);
3174
3175 add_node_copy(holder, xml);
3176 return TRUE;
3177 }
3178
3179 static char *
3180 crm_xml_escape_shuffle(char *text, int start, int *length, const char *replace)
3181 {
3182 int lpc;
3183 int offset = strlen(replace) - 1;
3184
3185 *length += offset;
3186 text = realloc_safe(text, *length);
3187
3188 for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
3189 text[lpc] = text[lpc - offset];
3190 }
3191
3192 memcpy(text + start, replace, offset + 1);
3193 return text;
3194 }
3195
3196 char *
3197 crm_xml_escape(const char *text)
3198 {
3199 int index;
3200 int changes = 0;
3201 int length = 1 + strlen(text);
3202 char *copy = strdup(text);
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219 for (index = 0; index < length; index++) {
3220 switch (copy[index]) {
3221 case 0:
3222 break;
3223 case '<':
3224 copy = crm_xml_escape_shuffle(copy, index, &length, "<");
3225 changes++;
3226 break;
3227 case '>':
3228 copy = crm_xml_escape_shuffle(copy, index, &length, ">");
3229 changes++;
3230 break;
3231 case '"':
3232 copy = crm_xml_escape_shuffle(copy, index, &length, """);
3233 changes++;
3234 break;
3235 case '\'':
3236 copy = crm_xml_escape_shuffle(copy, index, &length, "'");
3237 changes++;
3238 break;
3239 case '&':
3240 copy = crm_xml_escape_shuffle(copy, index, &length, "&");
3241 changes++;
3242 break;
3243 case '\t':
3244
3245 copy = crm_xml_escape_shuffle(copy, index, &length, " ");
3246 changes++;
3247 break;
3248 case '\n':
3249
3250 copy = crm_xml_escape_shuffle(copy, index, &length, "\\n");
3251 changes++;
3252 break;
3253 case '\r':
3254 copy = crm_xml_escape_shuffle(copy, index, &length, "\\r");
3255 changes++;
3256 break;
3257
3258
3259
3260
3261
3262 default:
3263
3264 if(copy[index] < ' ' || copy[index] > '~') {
3265 char *replace = crm_strdup_printf("\\%.3o", copy[index]);
3266
3267
3268 copy = crm_xml_escape_shuffle(copy, index, &length, replace);
3269 free(replace);
3270 changes++;
3271 }
3272 }
3273 }
3274
3275 if (changes) {
3276 crm_trace("Dumped '%s'", copy);
3277 }
3278 return copy;
3279 }
3280
3281 static inline void
3282 dump_xml_attr(xmlAttrPtr attr, int options, char **buffer, int *offset, int *max)
3283 {
3284 char *p_value = NULL;
3285 const char *p_name = NULL;
3286 xml_private_t *p = NULL;
3287
3288 CRM_ASSERT(buffer != NULL);
3289 if (attr == NULL || attr->children == NULL) {
3290 return;
3291 }
3292
3293 p = attr->_private;
3294 if (p && is_set(p->flags, xpf_deleted)) {
3295 return;
3296 }
3297
3298 p_name = (const char *)attr->name;
3299 p_value = crm_xml_escape((const char *)attr->children->content);
3300 buffer_print(*buffer, *max, *offset, " %s=\"%s\"", p_name, p_value);
3301 free(p_value);
3302 }
3303
3304 static void
3305 __xml_log_element(int log_level, const char *file, const char *function, int line,
3306 const char *prefix, xmlNode * data, int depth, int options)
3307 {
3308 int max = 0;
3309 int offset = 0;
3310 const char *name = NULL;
3311 const char *hidden = NULL;
3312
3313 xmlNode *child = NULL;
3314 xmlAttrPtr pIter = NULL;
3315
3316 if(data == NULL) {
3317 return;
3318 }
3319
3320 name = crm_element_name(data);
3321
3322 if(is_set(options, xml_log_option_open)) {
3323 char *buffer = NULL;
3324
3325 insert_prefix(options, &buffer, &offset, &max, depth);
3326
3327 if (data->type == XML_COMMENT_NODE) {
3328 buffer_print(buffer, max, offset, "<!--%s-->", data->content);
3329
3330 } else {
3331 buffer_print(buffer, max, offset, "<%s", name);
3332
3333 hidden = crm_element_value(data, "hidden");
3334 for (pIter = crm_first_attr(data); pIter != NULL; pIter = pIter->next) {
3335 xml_private_t *p = pIter->_private;
3336 const char *p_name = (const char *)pIter->name;
3337 const char *p_value = crm_attr_value(pIter);
3338 char *p_copy = NULL;
3339
3340 if(is_set(p->flags, xpf_deleted)) {
3341 continue;
3342 } else if ((is_set(options, xml_log_option_diff_plus)
3343 || is_set(options, xml_log_option_diff_minus))
3344 && strcmp(XML_DIFF_MARKER, p_name) == 0) {
3345 continue;
3346
3347 } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
3348 p_copy = strdup("*****");
3349
3350 } else {
3351 p_copy = crm_xml_escape(p_value);
3352 }
3353
3354 buffer_print(buffer, max, offset, " %s=\"%s\"", p_name, p_copy);
3355 free(p_copy);
3356 }
3357
3358 if(xml_has_children(data) == FALSE) {
3359 buffer_print(buffer, max, offset, "/>");
3360
3361 } else if(is_set(options, xml_log_option_children)) {
3362 buffer_print(buffer, max, offset, ">");
3363
3364 } else {
3365 buffer_print(buffer, max, offset, "/>");
3366 }
3367 }
3368
3369 do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
3370 free(buffer);
3371 }
3372
3373 if(data->type == XML_COMMENT_NODE) {
3374 return;
3375
3376 } else if(xml_has_children(data) == FALSE) {
3377 return;
3378
3379 } else if(is_set(options, xml_log_option_children)) {
3380 offset = 0;
3381 max = 0;
3382
3383 for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
3384 __xml_log_element(log_level, file, function, line, prefix, child, depth + 1, options|xml_log_option_open|xml_log_option_close);
3385 }
3386 }
3387
3388 if(is_set(options, xml_log_option_close)) {
3389 char *buffer = NULL;
3390
3391 insert_prefix(options, &buffer, &offset, &max, depth);
3392 buffer_print(buffer, max, offset, "</%s>", name);
3393
3394 do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
3395 free(buffer);
3396 }
3397 }
3398
3399 static void
3400 __xml_log_change_element(int log_level, const char *file, const char *function, int line,
3401 const char *prefix, xmlNode * data, int depth, int options)
3402 {
3403 xml_private_t *p;
3404 char *prefix_m = NULL;
3405 xmlNode *child = NULL;
3406 xmlAttrPtr pIter = NULL;
3407
3408 if(data == NULL) {
3409 return;
3410 }
3411
3412 p = data->_private;
3413
3414 prefix_m = strdup(prefix);
3415 prefix_m[1] = '+';
3416
3417 if(is_set(p->flags, xpf_dirty) && is_set(p->flags, xpf_created)) {
3418
3419 __xml_log_element(log_level, file, function, line,
3420 prefix_m, data, depth, options|xml_log_option_open|xml_log_option_close|xml_log_option_children);
3421
3422 } else if(is_set(p->flags, xpf_dirty)) {
3423 char *spaces = calloc(80, 1);
3424 int s_count = 0, s_max = 80;
3425 char *prefix_del = NULL;
3426 char *prefix_moved = NULL;
3427 const char *flags = prefix;
3428
3429 insert_prefix(options, &spaces, &s_count, &s_max, depth);
3430 prefix_del = strdup(prefix);
3431 prefix_del[0] = '-';
3432 prefix_del[1] = '-';
3433 prefix_moved = strdup(prefix);
3434 prefix_moved[1] = '~';
3435
3436 if(is_set(p->flags, xpf_moved)) {
3437 flags = prefix_moved;
3438 } else {
3439 flags = prefix;
3440 }
3441
3442 __xml_log_element(log_level, file, function, line,
3443 flags, data, depth, options|xml_log_option_open);
3444
3445 for (pIter = crm_first_attr(data); pIter != NULL; pIter = pIter->next) {
3446 const char *aname = (const char*)pIter->name;
3447
3448 p = pIter->_private;
3449 if(is_set(p->flags, xpf_deleted)) {
3450 const char *value = crm_element_value(data, aname);
3451 flags = prefix_del;
3452 do_crm_log_alias(log_level, file, function, line,
3453 "%s %s @%s=%s", flags, spaces, aname, value);
3454
3455 } else if(is_set(p->flags, xpf_dirty)) {
3456 const char *value = crm_element_value(data, aname);
3457
3458 if(is_set(p->flags, xpf_created)) {
3459 flags = prefix_m;
3460
3461 } else if(is_set(p->flags, xpf_modified)) {
3462 flags = prefix;
3463
3464 } else if(is_set(p->flags, xpf_moved)) {
3465 flags = prefix_moved;
3466
3467 } else {
3468 flags = prefix;
3469 }
3470 do_crm_log_alias(log_level, file, function, line,
3471 "%s %s @%s=%s", flags, spaces, aname, value);
3472 }
3473 }
3474 free(prefix_moved);
3475 free(prefix_del);
3476 free(spaces);
3477
3478 for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
3479 __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
3480 }
3481
3482 __xml_log_element(log_level, file, function, line,
3483 prefix, data, depth, options|xml_log_option_close);
3484
3485 } else {
3486 for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
3487 __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
3488 }
3489 }
3490
3491 free(prefix_m);
3492
3493 }
3494
3495 void
3496 log_data_element(int log_level, const char *file, const char *function, int line,
3497 const char *prefix, xmlNode * data, int depth, int options)
3498 {
3499 xmlNode *a_child = NULL;
3500
3501 char *prefix_m = NULL;
3502
3503 if (prefix == NULL) {
3504 prefix = "";
3505 }
3506
3507
3508 if (data == NULL) {
3509 do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
3510 "No data to dump as XML");
3511 return;
3512 }
3513
3514 if(is_set(options, xml_log_option_dirty_add) || is_set(options, xml_log_option_dirty_add)) {
3515 __xml_log_change_element(log_level, file, function, line, prefix, data, depth, options);
3516 return;
3517 }
3518
3519 if (is_set(options, xml_log_option_formatted)) {
3520 if (is_set(options, xml_log_option_diff_plus)
3521 && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
3522 options |= xml_log_option_diff_all;
3523 prefix_m = strdup(prefix);
3524 prefix_m[1] = '+';
3525 prefix = prefix_m;
3526
3527 } else if (is_set(options, xml_log_option_diff_minus)
3528 && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
3529 options |= xml_log_option_diff_all;
3530 prefix_m = strdup(prefix);
3531 prefix_m[1] = '-';
3532 prefix = prefix_m;
3533 }
3534 }
3535
3536 if (is_set(options, xml_log_option_diff_short)
3537 && is_not_set(options, xml_log_option_diff_all)) {
3538
3539 for (a_child = __xml_first_child(data); a_child != NULL; a_child = __xml_next(a_child)) {
3540 log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
3541 }
3542 } else {
3543 __xml_log_element(log_level, file, function, line, prefix, data, depth,
3544 options|xml_log_option_open|xml_log_option_close|xml_log_option_children);
3545 }
3546 free(prefix_m);
3547 }
3548
3549 static void
3550 dump_filtered_xml(xmlNode * data, int options, char **buffer, int *offset, int *max)
3551 {
3552 int lpc;
3553 xmlAttrPtr xIter = NULL;
3554 static int filter_len = DIMOF(filter);
3555
3556 for (lpc = 0; options && lpc < filter_len; lpc++) {
3557 filter[lpc].found = FALSE;
3558 }
3559
3560 for (xIter = crm_first_attr(data); xIter != NULL; xIter = xIter->next) {
3561 bool skip = FALSE;
3562 const char *p_name = (const char *)xIter->name;
3563
3564 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
3565 if (filter[lpc].found == FALSE && strcmp(p_name, filter[lpc].string) == 0) {
3566 filter[lpc].found = TRUE;
3567 skip = TRUE;
3568 break;
3569 }
3570 }
3571
3572 if (skip == FALSE) {
3573 dump_xml_attr(xIter, options, buffer, offset, max);
3574 }
3575 }
3576 }
3577
3578 static void
3579 dump_xml_element(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3580 {
3581 const char *name = NULL;
3582
3583 CRM_ASSERT(max != NULL);
3584 CRM_ASSERT(offset != NULL);
3585 CRM_ASSERT(buffer != NULL);
3586
3587 if (data == NULL) {
3588 crm_trace("Nothing to dump");
3589 return;
3590 }
3591
3592 if (*buffer == NULL) {
3593 *offset = 0;
3594 *max = 0;
3595 }
3596
3597 name = crm_element_name(data);
3598 CRM_ASSERT(name != NULL);
3599
3600 insert_prefix(options, buffer, offset, max, depth);
3601 buffer_print(*buffer, *max, *offset, "<%s", name);
3602
3603 if (options & xml_log_option_filtered) {
3604 dump_filtered_xml(data, options, buffer, offset, max);
3605
3606 } else {
3607 xmlAttrPtr xIter = NULL;
3608
3609 for (xIter = crm_first_attr(data); xIter != NULL; xIter = xIter->next) {
3610 dump_xml_attr(xIter, options, buffer, offset, max);
3611 }
3612 }
3613
3614 if (data->children == NULL) {
3615 buffer_print(*buffer, *max, *offset, "/>");
3616
3617 } else {
3618 buffer_print(*buffer, *max, *offset, ">");
3619 }
3620
3621 if (options & xml_log_option_formatted) {
3622 buffer_print(*buffer, *max, *offset, "\n");
3623 }
3624
3625 if (data->children) {
3626 xmlNode *xChild = NULL;
3627 for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
3628 crm_xml_dump(xChild, options, buffer, offset, max, depth + 1);
3629 }
3630
3631 insert_prefix(options, buffer, offset, max, depth);
3632 buffer_print(*buffer, *max, *offset, "</%s>", name);
3633
3634 if (options & xml_log_option_formatted) {
3635 buffer_print(*buffer, *max, *offset, "\n");
3636 }
3637 }
3638 }
3639
3640 static void
3641 dump_xml_text(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3642 {
3643 CRM_ASSERT(max != NULL);
3644 CRM_ASSERT(offset != NULL);
3645 CRM_ASSERT(buffer != NULL);
3646
3647 if (data == NULL) {
3648 crm_trace("Nothing to dump");
3649 return;
3650 }
3651
3652 if (*buffer == NULL) {
3653 *offset = 0;
3654 *max = 0;
3655 }
3656
3657 insert_prefix(options, buffer, offset, max, depth);
3658
3659 buffer_print(*buffer, *max, *offset, "%s", data->content);
3660
3661 if (options & xml_log_option_formatted) {
3662 buffer_print(*buffer, *max, *offset, "\n");
3663 }
3664 }
3665
3666
3667 static void
3668 dump_xml_comment(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3669 {
3670 CRM_ASSERT(max != NULL);
3671 CRM_ASSERT(offset != NULL);
3672 CRM_ASSERT(buffer != NULL);
3673
3674 if (data == NULL) {
3675 crm_trace("Nothing to dump");
3676 return;
3677 }
3678
3679 if (*buffer == NULL) {
3680 *offset = 0;
3681 *max = 0;
3682 }
3683
3684 insert_prefix(options, buffer, offset, max, depth);
3685
3686 buffer_print(*buffer, *max, *offset, "<!--");
3687 buffer_print(*buffer, *max, *offset, "%s", data->content);
3688 buffer_print(*buffer, *max, *offset, "-->");
3689
3690 if (options & xml_log_option_formatted) {
3691 buffer_print(*buffer, *max, *offset, "\n");
3692 }
3693 }
3694
3695 void
3696 crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3697 {
3698 if(data == NULL) {
3699 *offset = 0;
3700 *max = 0;
3701 return;
3702 }
3703 #if 0
3704 if (is_not_set(options, xml_log_option_filtered)) {
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717 time_t now, next;
3718 xmlDoc *doc = NULL;
3719 xmlBuffer *xml_buffer = NULL;
3720
3721 *buffer = NULL;
3722 doc = getDocPtr(data);
3723
3724 CRM_CHECK(doc != NULL, return);
3725
3726 now = time(NULL);
3727 xml_buffer = xmlBufferCreate();
3728 CRM_ASSERT(xml_buffer != NULL);
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_DOUBLEIT);
3739
3740 *max = xmlNodeDump(xml_buffer, doc, data, 0, (options & xml_log_option_formatted));
3741 if (*max > 0) {
3742 *buffer = strdup((char *)xml_buffer->content);
3743 }
3744
3745 next = time(NULL);
3746 if ((now + 1) < next) {
3747 crm_log_xml_trace(data, "Long time");
3748 crm_err("xmlNodeDump() -> %dbytes took %ds", *max, next - now);
3749 }
3750
3751 xmlBufferFree(xml_buffer);
3752 return;
3753 }
3754 #endif
3755
3756 switch(data->type) {
3757 case XML_ELEMENT_NODE:
3758
3759 dump_xml_element(data, options, buffer, offset, max, depth);
3760 break;
3761 case XML_TEXT_NODE:
3762
3763 if (options & xml_log_option_text) {
3764 dump_xml_text(data, options, buffer, offset, max, depth);
3765 }
3766 return;
3767 case XML_COMMENT_NODE:
3768 dump_xml_comment(data, options, buffer, offset, max, depth);
3769 break;
3770 default:
3771 crm_warn("Unhandled type: %d", data->type);
3772 return;
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794 }
3795
3796 }
3797
3798 void
3799 crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
3800 {
3801 buffer_print(*buffer, *max, *offset, "%c", c);
3802 }
3803
3804 char *
3805 dump_xml_formatted_with_text(xmlNode * an_xml_node)
3806 {
3807 char *buffer = NULL;
3808 int offset = 0, max = 0;
3809
3810 crm_xml_dump(an_xml_node, xml_log_option_formatted|xml_log_option_text, &buffer, &offset, &max, 0);
3811 return buffer;
3812 }
3813
3814 char *
3815 dump_xml_formatted(xmlNode * an_xml_node)
3816 {
3817 char *buffer = NULL;
3818 int offset = 0, max = 0;
3819
3820 crm_xml_dump(an_xml_node, xml_log_option_formatted, &buffer, &offset, &max, 0);
3821 return buffer;
3822 }
3823
3824 char *
3825 dump_xml_unformatted(xmlNode * an_xml_node)
3826 {
3827 char *buffer = NULL;
3828 int offset = 0, max = 0;
3829
3830 crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
3831 return buffer;
3832 }
3833
3834 gboolean
3835 xml_has_children(const xmlNode * xml_root)
3836 {
3837 if (xml_root != NULL && xml_root->children != NULL) {
3838 return TRUE;
3839 }
3840 return FALSE;
3841 }
3842
3843 int
3844 crm_element_value_int(xmlNode * data, const char *name, int *dest)
3845 {
3846 const char *value = crm_element_value(data, name);
3847
3848 CRM_CHECK(dest != NULL, return -1);
3849 if (value) {
3850 *dest = crm_int_helper(value, NULL);
3851 return 0;
3852 }
3853 return -1;
3854 }
3855
3856 int
3857 crm_element_value_const_int(const xmlNode * data, const char *name, int *dest)
3858 {
3859 return crm_element_value_int((xmlNode *) data, name, dest);
3860 }
3861
3862 const char *
3863 crm_element_value_const(const xmlNode * data, const char *name)
3864 {
3865 return crm_element_value((xmlNode *) data, name);
3866 }
3867
3868 char *
3869 crm_element_value_copy(xmlNode * data, const char *name)
3870 {
3871 char *value_copy = NULL;
3872 const char *value = crm_element_value(data, name);
3873
3874 if (value != NULL) {
3875 value_copy = strdup(value);
3876 }
3877 return value_copy;
3878 }
3879
3880 void
3881 xml_remove_prop(xmlNode * obj, const char *name)
3882 {
3883 if(__xml_acl_check(obj, NULL, xpf_acl_write) == FALSE) {
3884 crm_trace("Cannot remove %s from %s", name, obj->name);
3885
3886 } else if(TRACKING_CHANGES(obj)) {
3887
3888 xml_private_t *p = NULL;
3889 xmlAttr *attr = xmlHasProp(obj, (const xmlChar *)name);
3890
3891 p = attr->_private;
3892 set_parent_flag(obj, xpf_dirty);
3893 p->flags |= xpf_deleted;
3894
3895
3896 } else {
3897 xmlUnsetProp(obj, (const xmlChar *)name);
3898 }
3899 }
3900
3901 void
3902 purge_diff_markers(xmlNode * a_node)
3903 {
3904 xmlNode *child = NULL;
3905
3906 CRM_CHECK(a_node != NULL, return);
3907
3908 xml_remove_prop(a_node, XML_DIFF_MARKER);
3909 for (child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
3910 purge_diff_markers(child);
3911 }
3912 }
3913
3914 void
3915 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
3916 {
3917 char *f = NULL;
3918
3919 if (filename == NULL) {
3920 char *uuid = crm_generate_uuid();
3921
3922 f = crm_strdup_printf("/tmp/%s", uuid);
3923 filename = f;
3924 free(uuid);
3925 }
3926
3927 crm_info("Saving %s to %s", desc, filename);
3928 write_xml_file(xml, filename, FALSE);
3929 free(f);
3930 }
3931
3932 gboolean
3933 apply_xml_diff(xmlNode * old, xmlNode * diff, xmlNode ** new)
3934 {
3935 gboolean result = TRUE;
3936 int root_nodes_seen = 0;
3937 static struct qb_log_callsite *digest_cs = NULL;
3938 const char *digest = crm_element_value(diff, XML_ATTR_DIGEST);
3939 const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION);
3940
3941 xmlNode *child_diff = NULL;
3942 xmlNode *added = find_xml_node(diff, "diff-added", FALSE);
3943 xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE);
3944
3945 CRM_CHECK(new != NULL, return FALSE);
3946 if (digest_cs == NULL) {
3947 digest_cs =
3948 qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
3949 crm_trace_nonlog);
3950 }
3951
3952 crm_trace("Subtraction Phase");
3953 for (child_diff = __xml_first_child(removed); child_diff != NULL;
3954 child_diff = __xml_next(child_diff)) {
3955 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3956 if (root_nodes_seen == 0) {
3957 *new = subtract_xml_object(NULL, old, child_diff, FALSE, NULL, NULL);
3958 }
3959 root_nodes_seen++;
3960 }
3961
3962 if (root_nodes_seen == 0) {
3963 *new = copy_xml(old);
3964
3965 } else if (root_nodes_seen > 1) {
3966 crm_err("(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3967 result = FALSE;
3968 }
3969
3970 root_nodes_seen = 0;
3971 crm_trace("Addition Phase");
3972 if (result) {
3973 xmlNode *child_diff = NULL;
3974
3975 for (child_diff = __xml_first_child(added); child_diff != NULL;
3976 child_diff = __xml_next(child_diff)) {
3977 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3978 if (root_nodes_seen == 0) {
3979 add_xml_object(NULL, *new, child_diff, TRUE);
3980 }
3981 root_nodes_seen++;
3982 }
3983 }
3984
3985 if (root_nodes_seen > 1) {
3986 crm_err("(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3987 result = FALSE;
3988
3989 } else if (result && digest) {
3990 char *new_digest = NULL;
3991
3992 purge_diff_markers(*new);
3993 new_digest = calculate_xml_versioned_digest(*new, FALSE, TRUE, version);
3994 if (safe_str_neq(new_digest, digest)) {
3995 crm_info("Digest mis-match: expected %s, calculated %s", digest, new_digest);
3996 result = FALSE;
3997
3998 crm_trace("%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
3999 if (digest_cs && digest_cs->targets) {
4000 save_xml_to_file(old, "diff:original", NULL);
4001 save_xml_to_file(diff, "diff:input", NULL);
4002 save_xml_to_file(*new, "diff:new", NULL);
4003 }
4004
4005 } else {
4006 crm_trace("Digest matched: expected %s, calculated %s", digest, new_digest);
4007 }
4008 free(new_digest);
4009
4010 } else if (result) {
4011 purge_diff_markers(*new);
4012 }
4013
4014 return result;
4015 }
4016
4017 static void
4018 __xml_diff_object(xmlNode * old, xmlNode * new)
4019 {
4020 xmlNode *cIter = NULL;
4021 xmlAttr *pIter = NULL;
4022
4023 CRM_CHECK(new != NULL, return);
4024 if(old == NULL) {
4025 crm_node_created(new);
4026 __xml_acl_post_process(new);
4027 return;
4028
4029 } else {
4030 xml_private_t *p = new->_private;
4031
4032 if(p->flags & xpf_processed) {
4033
4034 return;
4035 }
4036 p->flags |= xpf_processed;
4037 }
4038
4039 for (pIter = crm_first_attr(new); pIter != NULL; pIter = pIter->next) {
4040 xml_private_t *p = pIter->_private;
4041
4042
4043 p->flags |= xpf_created;
4044 }
4045
4046 for (pIter = crm_first_attr(old); pIter != NULL; ) {
4047 xmlAttr *prop = pIter;
4048 xml_private_t *p = NULL;
4049 const char *name = (const char *)pIter->name;
4050 const char *old_value = crm_element_value(old, name);
4051 xmlAttr *exists = xmlHasProp(new, pIter->name);
4052
4053 pIter = pIter->next;
4054 if(exists == NULL) {
4055 p = new->doc->_private;
4056
4057
4058 clear_bit(p->flags, xpf_tracking);
4059 exists = xmlSetProp(new, (const xmlChar *)name, (const xmlChar *)old_value);
4060 set_bit(p->flags, xpf_tracking);
4061
4062 p = exists->_private;
4063 p->flags = 0;
4064
4065 crm_trace("Lost %s@%s=%s", old->name, name, old_value);
4066 xml_remove_prop(new, name);
4067
4068 } else {
4069 int p_new = __xml_offset((xmlNode*)exists);
4070 int p_old = __xml_offset((xmlNode*)prop);
4071 const char *value = crm_element_value(new, name);
4072
4073 p = exists->_private;
4074 p->flags = (p->flags & ~xpf_created);
4075
4076 if(strcmp(value, old_value) != 0) {
4077
4078
4079
4080 char *vcopy = crm_element_value_copy(new, name);
4081
4082 crm_trace("Modified %s@%s %s->%s", old->name, name, old_value, vcopy);
4083 xmlSetProp(new, prop->name, (const xmlChar *)old_value);
4084 crm_xml_add(new, name, vcopy);
4085 free(vcopy);
4086
4087 } else if(p_old != p_new) {
4088 crm_info("Moved %s@%s (%d -> %d)", old->name, name, p_old, p_new);
4089 __xml_node_dirty(new);
4090 p->flags |= xpf_dirty|xpf_moved;
4091
4092 if(p_old > p_new) {
4093 p = prop->_private;
4094 p->flags |= xpf_skip;
4095
4096 } else {
4097 p = exists->_private;
4098 p->flags |= xpf_skip;
4099 }
4100 }
4101 }
4102 }
4103
4104 for (pIter = crm_first_attr(new); pIter != NULL; ) {
4105 xmlAttr *prop = pIter;
4106 xml_private_t *p = pIter->_private;
4107
4108 pIter = pIter->next;
4109 if(is_set(p->flags, xpf_created)) {
4110 char *name = strdup((const char *)prop->name);
4111 char *value = crm_element_value_copy(new, name);
4112
4113 crm_trace("Created %s@%s=%s", new->name, name, value);
4114
4115 if(__xml_acl_check(new, name, xpf_acl_write)) {
4116 crm_attr_dirty(prop);
4117 } else {
4118 xmlUnsetProp(new, prop->name);
4119 }
4120
4121 free(value);
4122 free(name);
4123 }
4124 }
4125
4126 for (cIter = __xml_first_child(old); cIter != NULL; ) {
4127 xmlNode *old_child = cIter;
4128 xmlNode *new_child = find_element(new, cIter, TRUE);
4129
4130 cIter = __xml_next(cIter);
4131 if(new_child) {
4132 __xml_diff_object(old_child, new_child);
4133
4134 } else {
4135
4136 xmlNode *candidate = add_node_copy(new, old_child);
4137 xmlNode *top = xmlDocGetRootElement(candidate->doc);
4138
4139 __xml_node_clean(candidate);
4140 __xml_acl_apply(top);
4141
4142 free_xml_with_position(candidate, __xml_offset(old_child));
4143
4144 if (find_element(new, old_child, TRUE) == NULL) {
4145 xml_private_t *p = old_child->_private;
4146
4147 p->flags |= xpf_skip;
4148 }
4149 }
4150 }
4151
4152 for (cIter = __xml_first_child(new); cIter != NULL; ) {
4153 xmlNode *new_child = cIter;
4154 xmlNode *old_child = find_element(old, cIter, TRUE);
4155
4156 cIter = __xml_next(cIter);
4157 if(old_child == NULL) {
4158 xml_private_t *p = new_child->_private;
4159 p->flags |= xpf_skip;
4160 __xml_diff_object(old_child, new_child);
4161
4162 } else {
4163
4164 int p_new = __xml_offset(new_child);
4165 int p_old = __xml_offset(old_child);
4166
4167 if(p_old != p_new) {
4168 xml_private_t *p = new_child->_private;
4169
4170 crm_info("%s.%s moved from %d to %d",
4171 new_child->name, ID(new_child), p_old, p_new);
4172 __xml_node_dirty(new);
4173 p->flags |= xpf_moved;
4174
4175 if(p_old > p_new) {
4176 p = old_child->_private;
4177 } else {
4178 p = new_child->_private;
4179 }
4180 p->flags |= xpf_skip;
4181 }
4182 }
4183 }
4184 }
4185
4186 void
4187 xml_calculate_changes(xmlNode * old, xmlNode * new)
4188 {
4189 CRM_CHECK(safe_str_eq(crm_element_name(old), crm_element_name(new)), return);
4190 CRM_CHECK(safe_str_eq(ID(old), ID(new)), return);
4191
4192 if(xml_tracking_changes(new) == FALSE) {
4193 xml_track_changes(new, NULL, NULL, FALSE);
4194 }
4195
4196 __xml_diff_object(old, new);
4197 }
4198
4199 xmlNode *
4200 diff_xml_object(xmlNode * old, xmlNode * new, gboolean suppress)
4201 {
4202 xmlNode *tmp1 = NULL;
4203 xmlNode *diff = create_xml_node(NULL, "diff");
4204 xmlNode *removed = create_xml_node(diff, "diff-removed");
4205 xmlNode *added = create_xml_node(diff, "diff-added");
4206
4207 crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
4208
4209 tmp1 = subtract_xml_object(removed, old, new, FALSE, NULL, "removed:top");
4210 if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
4211 free_xml(tmp1);
4212 }
4213
4214 tmp1 = subtract_xml_object(added, new, old, TRUE, NULL, "added:top");
4215 if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
4216 free_xml(tmp1);
4217 }
4218
4219 if (added->children == NULL && removed->children == NULL) {
4220 free_xml(diff);
4221 diff = NULL;
4222 }
4223
4224 return diff;
4225 }
4226
4227 gboolean
4228 can_prune_leaf(xmlNode * xml_node)
4229 {
4230 xmlNode *cIter = NULL;
4231 xmlAttrPtr pIter = NULL;
4232 gboolean can_prune = TRUE;
4233 const char *name = crm_element_name(xml_node);
4234
4235 if (safe_str_eq(name, XML_TAG_RESOURCE_REF)
4236 || safe_str_eq(name, XML_CIB_TAG_OBJ_REF)
4237 || safe_str_eq(name, XML_ACL_TAG_ROLE_REF)
4238 || safe_str_eq(name, XML_ACL_TAG_ROLE_REFv1)) {
4239 return FALSE;
4240 }
4241
4242 for (pIter = crm_first_attr(xml_node); pIter != NULL; pIter = pIter->next) {
4243 const char *p_name = (const char *)pIter->name;
4244
4245 if (strcmp(p_name, XML_ATTR_ID) == 0) {
4246 continue;
4247 }
4248 can_prune = FALSE;
4249 }
4250
4251 cIter = __xml_first_child(xml_node);
4252 while (cIter) {
4253 xmlNode *child = cIter;
4254
4255 cIter = __xml_next(cIter);
4256 if (can_prune_leaf(child)) {
4257 free_xml(child);
4258 } else {
4259 can_prune = FALSE;
4260 }
4261 }
4262 return can_prune;
4263 }
4264
4265 void
4266 diff_filter_context(int context, int upper_bound, int lower_bound,
4267 xmlNode * xml_node, xmlNode * parent)
4268 {
4269 xmlNode *us = NULL;
4270 xmlNode *child = NULL;
4271 xmlAttrPtr pIter = NULL;
4272 xmlNode *new_parent = parent;
4273 const char *name = crm_element_name(xml_node);
4274
4275 CRM_CHECK(xml_node != NULL && name != NULL, return);
4276
4277 us = create_xml_node(parent, name);
4278 for (pIter = crm_first_attr(xml_node); pIter != NULL; pIter = pIter->next) {
4279 const char *p_name = (const char *)pIter->name;
4280 const char *p_value = crm_attr_value(pIter);
4281
4282 lower_bound = context;
4283 crm_xml_add(us, p_name, p_value);
4284 }
4285
4286 if (lower_bound >= 0 || upper_bound >= 0) {
4287 crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
4288 new_parent = us;
4289
4290 } else {
4291 upper_bound = in_upper_context(0, context, xml_node);
4292 if (upper_bound >= 0) {
4293 crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
4294 new_parent = us;
4295 } else {
4296 free_xml(us);
4297 us = NULL;
4298 }
4299 }
4300
4301 for (child = __xml_first_child(us); child != NULL; child = __xml_next(child)) {
4302 diff_filter_context(context, upper_bound - 1, lower_bound - 1, child, new_parent);
4303 }
4304 }
4305
4306 int
4307 in_upper_context(int depth, int context, xmlNode * xml_node)
4308 {
4309 if (context == 0) {
4310 return 0;
4311 }
4312
4313 if (xml_node->properties) {
4314 return depth;
4315
4316 } else if (depth < context) {
4317 xmlNode *child = NULL;
4318
4319 for (child = __xml_first_child(xml_node); child != NULL; child = __xml_next(child)) {
4320 if (in_upper_context(depth + 1, context, child)) {
4321 return depth;
4322 }
4323 }
4324 }
4325 return 0;
4326 }
4327
4328 static xmlNode *
4329 find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact)
4330 {
4331 xmlNode *a_child = NULL;
4332 int search_offset = __xml_offset(search_comment);
4333
4334 CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
4335
4336 for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
4337 if (exact) {
4338 int offset = __xml_offset(a_child);
4339 xml_private_t *p = a_child->_private;
4340
4341 if (offset < search_offset) {
4342 continue;
4343
4344 } else if (offset > search_offset) {
4345 return NULL;
4346 }
4347
4348 if (is_set(p->flags, xpf_skip)) {
4349 continue;
4350 }
4351 }
4352
4353 if (a_child->type == XML_COMMENT_NODE
4354 && safe_str_eq((const char *)a_child->content, (const char *)search_comment->content)) {
4355 return a_child;
4356
4357 } else if (exact) {
4358 return NULL;
4359 }
4360 }
4361
4362 return NULL;
4363 }
4364
4365 static xmlNode *
4366 subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right,
4367 gboolean * changed)
4368 {
4369 CRM_CHECK(left != NULL, return NULL);
4370 CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL);
4371
4372 if (right == NULL
4373 || safe_str_neq((const char *)left->content, (const char *)right->content)) {
4374 xmlNode *deleted = NULL;
4375
4376 deleted = add_node_copy(parent, left);
4377 *changed = TRUE;
4378
4379 return deleted;
4380 }
4381
4382 return NULL;
4383 }
4384
4385 xmlNode *
4386 subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right,
4387 gboolean full, gboolean * changed, const char *marker)
4388 {
4389 gboolean dummy = FALSE;
4390 gboolean skip = FALSE;
4391 xmlNode *diff = NULL;
4392 xmlNode *right_child = NULL;
4393 xmlNode *left_child = NULL;
4394 xmlAttrPtr xIter = NULL;
4395
4396 const char *id = NULL;
4397 const char *name = NULL;
4398 const char *value = NULL;
4399 const char *right_val = NULL;
4400
4401 int lpc = 0;
4402 static int filter_len = DIMOF(filter);
4403
4404 if (changed == NULL) {
4405 changed = &dummy;
4406 }
4407
4408 if (left == NULL) {
4409 return NULL;
4410 }
4411
4412 if (left->type == XML_COMMENT_NODE) {
4413 return subtract_xml_comment(parent, left, right, changed);
4414 }
4415
4416 id = ID(left);
4417 if (right == NULL) {
4418 xmlNode *deleted = NULL;
4419
4420 crm_trace("Processing <%s id=%s> (complete copy)", crm_element_name(left), id);
4421 deleted = add_node_copy(parent, left);
4422 crm_xml_add(deleted, XML_DIFF_MARKER, marker);
4423
4424 *changed = TRUE;
4425 return deleted;
4426 }
4427
4428 name = crm_element_name(left);
4429 CRM_CHECK(name != NULL, return NULL);
4430 CRM_CHECK(safe_str_eq(crm_element_name(left), crm_element_name(right)), return NULL);
4431
4432
4433 value = crm_element_value(right, XML_DIFF_MARKER);
4434 if (value != NULL && strcmp(value, "removed:top") == 0) {
4435 crm_trace("We are the root of the deletion: %s.id=%s", name, id);
4436 *changed = TRUE;
4437 return NULL;
4438 }
4439
4440
4441 diff = create_xml_node(parent, name);
4442
4443
4444 for (lpc = 0; lpc < filter_len; lpc++) {
4445 filter[lpc].found = FALSE;
4446 }
4447
4448
4449 for (left_child = __xml_first_child(left); left_child != NULL;
4450 left_child = __xml_next(left_child)) {
4451 gboolean child_changed = FALSE;
4452
4453 right_child = find_element(right, left_child, FALSE);
4454 subtract_xml_object(diff, left_child, right_child, full, &child_changed, marker);
4455 if (child_changed) {
4456 *changed = TRUE;
4457 }
4458 }
4459
4460 if (*changed == FALSE) {
4461
4462
4463 } else if (full) {
4464 xmlAttrPtr pIter = NULL;
4465
4466 for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
4467 const char *p_name = (const char *)pIter->name;
4468 const char *p_value = crm_attr_value(pIter);
4469
4470 xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
4471 }
4472
4473
4474 goto done;
4475
4476 } else if (id) {
4477 xmlSetProp(diff, (const xmlChar *)XML_ATTR_ID, (const xmlChar *)id);
4478 }
4479
4480
4481 for (xIter = crm_first_attr(left); xIter != NULL; xIter = xIter->next) {
4482 const char *prop_name = (const char *)xIter->name;
4483 xmlAttrPtr right_attr = NULL;
4484 xml_private_t *p = NULL;
4485
4486 if (strcmp(prop_name, XML_ATTR_ID) == 0) {
4487 continue;
4488 }
4489
4490 skip = FALSE;
4491 for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
4492 if (filter[lpc].found == FALSE && strcmp(prop_name, filter[lpc].string) == 0) {
4493 filter[lpc].found = TRUE;
4494 skip = TRUE;
4495 break;
4496 }
4497 }
4498
4499 if (skip) {
4500 continue;
4501 }
4502
4503 right_attr = xmlHasProp(right, (const xmlChar *)prop_name);
4504 if (right_attr) {
4505 p = right_attr->_private;
4506 }
4507
4508 right_val = crm_element_value(right, prop_name);
4509 if (right_val == NULL || (p && is_set(p->flags, xpf_deleted))) {
4510
4511 *changed = TRUE;
4512 if (full) {
4513 xmlAttrPtr pIter = NULL;
4514
4515 for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
4516 const char *p_name = (const char *)pIter->name;
4517 const char *p_value = crm_attr_value(pIter);
4518
4519 xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
4520 }
4521 break;
4522
4523 } else {
4524 const char *left_value = crm_element_value(left, prop_name);
4525
4526 xmlSetProp(diff, (const xmlChar *)prop_name, (const xmlChar *)value);
4527 crm_xml_add(diff, prop_name, left_value);
4528 }
4529
4530 } else {
4531
4532 const char *left_value = crm_element_value(left, prop_name);
4533
4534 if (strcmp(left_value, right_val) == 0) {
4535
4536
4537 } else {
4538 *changed = TRUE;
4539 if (full) {
4540 xmlAttrPtr pIter = NULL;
4541
4542 crm_trace("Changes detected to %s in <%s id=%s>", prop_name,
4543 crm_element_name(left), id);
4544 for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
4545 const char *p_name = (const char *)pIter->name;
4546 const char *p_value = crm_attr_value(pIter);
4547
4548 xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
4549 }
4550 break;
4551
4552 } else {
4553 crm_trace("Changes detected to %s (%s -> %s) in <%s id=%s>",
4554 prop_name, left_value, right_val, crm_element_name(left), id);
4555 crm_xml_add(diff, prop_name, left_value);
4556 }
4557 }
4558 }
4559 }
4560
4561 if (*changed == FALSE) {
4562 free_xml(diff);
4563 return NULL;
4564
4565 } else if (full == FALSE && id) {
4566 crm_xml_add(diff, XML_ATTR_ID, id);
4567 }
4568 done:
4569 return diff;
4570 }
4571
4572 static int
4573 add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update)
4574 {
4575 CRM_CHECK(update != NULL, return 0);
4576 CRM_CHECK(update->type == XML_COMMENT_NODE, return 0);
4577
4578 if (target == NULL) {
4579 target = find_xml_comment(parent, update, FALSE);
4580 }
4581
4582 if (target == NULL) {
4583 add_node_copy(parent, update);
4584
4585
4586 } else if (safe_str_neq((const char *)target->content, (const char *)update->content)) {
4587 xmlFree(target->content);
4588 target->content = xmlStrdup(update->content);
4589 }
4590
4591 return 0;
4592 }
4593
4594 int
4595 add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff)
4596 {
4597 xmlNode *a_child = NULL;
4598 const char *object_id = NULL;
4599 const char *object_name = NULL;
4600
4601 #if XML_PARSE_DEBUG
4602 crm_log_xml_trace("update:", update);
4603 crm_log_xml_trace("target:", target);
4604 #endif
4605
4606 CRM_CHECK(update != NULL, return 0);
4607
4608 if (update->type == XML_COMMENT_NODE) {
4609 return add_xml_comment(parent, target, update);
4610 }
4611
4612 object_name = crm_element_name(update);
4613 object_id = ID(update);
4614
4615 CRM_CHECK(object_name != NULL, return 0);
4616
4617 if (target == NULL && object_id == NULL) {
4618
4619 target = find_xml_node(parent, object_name, FALSE);
4620
4621 } else if (target == NULL) {
4622 target = find_entity(parent, object_name, object_id);
4623 }
4624
4625 if (target == NULL) {
4626 target = create_xml_node(parent, object_name);
4627 CRM_CHECK(target != NULL, return 0);
4628 #if XML_PARSER_DEBUG
4629 crm_trace("Added <%s%s%s/>", crm_str(object_name),
4630 object_id ? " id=" : "", object_id ? object_id : "");
4631
4632 } else {
4633 crm_trace("Found node <%s%s%s/> to update",
4634 crm_str(object_name), object_id ? " id=" : "", object_id ? object_id : "");
4635 #endif
4636 }
4637
4638 CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(update)), return 0);
4639
4640 if (as_diff == FALSE) {
4641
4642 copy_in_properties(target, update);
4643
4644 } else {
4645
4646 xmlAttrPtr pIter = NULL;
4647
4648 for (pIter = crm_first_attr(update); pIter != NULL; pIter = pIter->next) {
4649 const char *p_name = (const char *)pIter->name;
4650 const char *p_value = crm_attr_value(pIter);
4651
4652
4653 xmlUnsetProp(target, (const xmlChar *)p_name);
4654 xmlSetProp(target, (const xmlChar *)p_name, (const xmlChar *)p_value);
4655 }
4656 }
4657
4658 for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
4659 #if XML_PARSER_DEBUG
4660 crm_trace("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child));
4661 #endif
4662 add_xml_object(target, NULL, a_child, as_diff);
4663 }
4664
4665 #if XML_PARSER_DEBUG
4666 crm_trace("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id));
4667 #endif
4668 return 0;
4669 }
4670
4671 gboolean
4672 update_xml_child(xmlNode * child, xmlNode * to_update)
4673 {
4674 gboolean can_update = TRUE;
4675 xmlNode *child_of_child = NULL;
4676
4677 CRM_CHECK(child != NULL, return FALSE);
4678 CRM_CHECK(to_update != NULL, return FALSE);
4679
4680 if (safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
4681 can_update = FALSE;
4682
4683 } else if (safe_str_neq(ID(to_update), ID(child))) {
4684 can_update = FALSE;
4685
4686 } else if (can_update) {
4687 #if XML_PARSER_DEBUG
4688 crm_log_xml_trace(child, "Update match found...");
4689 #endif
4690 add_xml_object(NULL, child, to_update, FALSE);
4691 }
4692
4693 for (child_of_child = __xml_first_child(child); child_of_child != NULL;
4694 child_of_child = __xml_next(child_of_child)) {
4695
4696 if (can_update) {
4697 break;
4698 }
4699 can_update = update_xml_child(child_of_child, to_update);
4700 }
4701
4702 return can_update;
4703 }
4704
4705 int
4706 find_xml_children(xmlNode ** children, xmlNode * root,
4707 const char *tag, const char *field, const char *value, gboolean search_matches)
4708 {
4709 int match_found = 0;
4710
4711 CRM_CHECK(root != NULL, return FALSE);
4712 CRM_CHECK(children != NULL, return FALSE);
4713
4714 if (tag != NULL && safe_str_neq(tag, crm_element_name(root))) {
4715
4716 } else if (value != NULL && safe_str_neq(value, crm_element_value(root, field))) {
4717
4718 } else {
4719 if (*children == NULL) {
4720 *children = create_xml_node(NULL, __FUNCTION__);
4721 }
4722 add_node_copy(*children, root);
4723 match_found = 1;
4724 }
4725
4726 if (search_matches || match_found == 0) {
4727 xmlNode *child = NULL;
4728
4729 for (child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
4730 match_found += find_xml_children(children, child, tag, field, value, search_matches);
4731 }
4732 }
4733
4734 return match_found;
4735 }
4736
4737 gboolean
4738 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
4739 {
4740 gboolean can_delete = FALSE;
4741 xmlNode *child_of_child = NULL;
4742
4743 const char *up_id = NULL;
4744 const char *child_id = NULL;
4745 const char *right_val = NULL;
4746
4747 CRM_CHECK(child != NULL, return FALSE);
4748 CRM_CHECK(update != NULL, return FALSE);
4749
4750 up_id = ID(update);
4751 child_id = ID(child);
4752
4753 if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
4754 can_delete = TRUE;
4755 }
4756 if (safe_str_neq(crm_element_name(update), crm_element_name(child))) {
4757 can_delete = FALSE;
4758 }
4759 if (can_delete && delete_only) {
4760 xmlAttrPtr pIter = NULL;
4761
4762 for (pIter = crm_first_attr(update); pIter != NULL; pIter = pIter->next) {
4763 const char *p_name = (const char *)pIter->name;
4764 const char *p_value = crm_attr_value(pIter);
4765
4766 right_val = crm_element_value(child, p_name);
4767 if (safe_str_neq(p_value, right_val)) {
4768 can_delete = FALSE;
4769 }
4770 }
4771 }
4772
4773 if (can_delete && parent != NULL) {
4774 crm_log_xml_trace(child, "Delete match found...");
4775 if (delete_only || update == NULL) {
4776 free_xml(child);
4777
4778 } else {
4779 xmlNode *tmp = copy_xml(update);
4780 xmlDoc *doc = tmp->doc;
4781 xmlNode *old = NULL;
4782
4783 xml_accept_changes(tmp);
4784 old = xmlReplaceNode(child, tmp);
4785
4786 if(xml_tracking_changes(tmp)) {
4787
4788 __xml_acl_apply(tmp);
4789 }
4790
4791 xml_calculate_changes(old, tmp);
4792 xmlDocSetRootElement(doc, old);
4793 free_xml(old);
4794 }
4795 child = NULL;
4796 return TRUE;
4797
4798 } else if (can_delete) {
4799 crm_log_xml_debug(child, "Cannot delete the search root");
4800 can_delete = FALSE;
4801 }
4802
4803 child_of_child = __xml_first_child(child);
4804 while (child_of_child) {
4805 xmlNode *next = __xml_next(child_of_child);
4806
4807 can_delete = replace_xml_child(child, child_of_child, update, delete_only);
4808
4809
4810 if (can_delete) {
4811 child_of_child = NULL;
4812 } else {
4813 child_of_child = next;
4814 }
4815 }
4816
4817 return can_delete;
4818 }
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830 xmlNode *
4831 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
4832 const char *value)
4833 {
4834 xmlNode *nvp;
4835
4836
4837
4838
4839 CRM_CHECK(id || name, return NULL);
4840
4841 nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
4842 CRM_CHECK(nvp, return NULL);
4843
4844 if (id) {
4845 crm_xml_add(nvp, XML_ATTR_ID, id);
4846 } else {
4847 const char *parent_id = ID(parent);
4848
4849 crm_xml_set_id(nvp, "%s-%s",
4850 (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
4851 }
4852 crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
4853 crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
4854 return nvp;
4855 }
4856
4857 void
4858 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
4859 {
4860 const char *name = key;
4861 const char *s_value = value;
4862 xmlNode *xml_node = user_data;
4863
4864 crm_create_nvpair_xml(xml_node, name, name, s_value);
4865 crm_trace("dumped: name=%s value=%s", name, s_value);
4866 }
4867
4868 void
4869 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
4870 {
4871 const char *name = key;
4872 const char *s_value = value;
4873
4874 xmlNode *xml_node = user_data;
4875
4876 if (isdigit(name[0])) {
4877 xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
4878
4879 crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
4880 crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
4881
4882 } else if (crm_element_value(xml_node, name) == NULL) {
4883 crm_xml_add(xml_node, name, s_value);
4884 crm_trace("dumped: %s=%s", name, s_value);
4885
4886 } else {
4887 crm_trace("duplicate: %s=%s", name, s_value);
4888 }
4889 }
4890
4891 void
4892 hash2field(gpointer key, gpointer value, gpointer user_data)
4893 {
4894 const char *name = key;
4895 const char *s_value = value;
4896
4897 xmlNode *xml_node = user_data;
4898
4899 if (crm_element_value(xml_node, name) == NULL) {
4900 crm_xml_add(xml_node, name, s_value);
4901
4902 } else {
4903 crm_trace("duplicate: %s=%s", name, s_value);
4904 }
4905 }
4906
4907 void
4908 hash2metafield(gpointer key, gpointer value, gpointer user_data)
4909 {
4910 char *crm_name = NULL;
4911
4912 if (key == NULL || value == NULL) {
4913 return;
4914 }
4915
4916
4917
4918
4919 for (crm_name = key; *crm_name; ++crm_name) {
4920 if ((*crm_name == '#') || (*crm_name == ':')) {
4921 return;
4922 }
4923 }
4924
4925 crm_name = crm_meta_name(key);
4926 hash2field(crm_name, value, user_data);
4927 free(crm_name);
4928 }
4929
4930 GHashTable *
4931 xml2list(xmlNode * parent)
4932 {
4933 xmlNode *child = NULL;
4934 xmlAttrPtr pIter = NULL;
4935 xmlNode *nvpair_list = NULL;
4936 GHashTable *nvpair_hash = crm_str_table_new();
4937
4938 CRM_CHECK(parent != NULL, return nvpair_hash);
4939
4940 nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
4941 if (nvpair_list == NULL) {
4942 crm_trace("No attributes in %s", crm_element_name(parent));
4943 crm_log_xml_trace(parent, "No attributes for resource op");
4944 }
4945
4946 crm_log_xml_trace(nvpair_list, "Unpacking");
4947
4948 for (pIter = crm_first_attr(nvpair_list); pIter != NULL; pIter = pIter->next) {
4949 const char *p_name = (const char *)pIter->name;
4950 const char *p_value = crm_attr_value(pIter);
4951
4952 crm_trace("Added %s=%s", p_name, p_value);
4953
4954 g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
4955 }
4956
4957 for (child = __xml_first_child(nvpair_list); child != NULL; child = __xml_next(child)) {
4958 if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
4959 const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
4960 const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
4961
4962 crm_trace("Added %s=%s", key, value);
4963 if (key != NULL && value != NULL) {
4964 g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
4965 }
4966 }
4967 }
4968
4969 return nvpair_hash;
4970 }
4971
4972 typedef struct name_value_s {
4973 const char *name;
4974 const void *value;
4975 } name_value_t;
4976
4977 static gint
4978 sort_pairs(gconstpointer a, gconstpointer b)
4979 {
4980 int rc = 0;
4981 const name_value_t *pair_a = a;
4982 const name_value_t *pair_b = b;
4983
4984 CRM_ASSERT(a != NULL);
4985 CRM_ASSERT(pair_a->name != NULL);
4986
4987 CRM_ASSERT(b != NULL);
4988 CRM_ASSERT(pair_b->name != NULL);
4989
4990 rc = strcmp(pair_a->name, pair_b->name);
4991 if (rc < 0) {
4992 return -1;
4993 } else if (rc > 0) {
4994 return 1;
4995 }
4996 return 0;
4997 }
4998
4999 static void
5000 dump_pair(gpointer data, gpointer user_data)
5001 {
5002 name_value_t *pair = data;
5003 xmlNode *parent = user_data;
5004
5005 crm_xml_add(parent, pair->name, pair->value);
5006 }
5007
5008 xmlNode *
5009 sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive)
5010 {
5011 xmlNode *child = NULL;
5012 GListPtr sorted = NULL;
5013 GListPtr unsorted = NULL;
5014 name_value_t *pair = NULL;
5015 xmlNode *result = NULL;
5016 const char *name = NULL;
5017 xmlAttrPtr pIter = NULL;
5018
5019 CRM_CHECK(input != NULL, return NULL);
5020
5021 name = crm_element_name(input);
5022 CRM_CHECK(name != NULL, return NULL);
5023
5024 result = create_xml_node(parent, name);
5025
5026 for (pIter = crm_first_attr(input); pIter != NULL; pIter = pIter->next) {
5027 const char *p_name = (const char *)pIter->name;
5028 const char *p_value = crm_attr_value(pIter);
5029
5030 pair = calloc(1, sizeof(name_value_t));
5031 pair->name = p_name;
5032 pair->value = p_value;
5033 unsorted = g_list_prepend(unsorted, pair);
5034 pair = NULL;
5035 }
5036
5037 sorted = g_list_sort(unsorted, sort_pairs);
5038 g_list_foreach(sorted, dump_pair, result);
5039 g_list_free_full(sorted, free);
5040
5041 for (child = __xml_first_child(input); child != NULL; child = __xml_next(child)) {
5042 if (recursive) {
5043 sorted_xml(child, result, recursive);
5044 } else {
5045 add_node_copy(result, child);
5046 }
5047 }
5048
5049 return result;
5050 }
5051
5052 xmlNode *
5053 first_named_child(xmlNode * parent, const char *name)
5054 {
5055 xmlNode *match = NULL;
5056
5057 for (match = __xml_first_child(parent); match != NULL; match = __xml_next(match)) {
5058
5059
5060
5061
5062
5063 if (name == NULL || strcmp((const char *)match->name, name) == 0) {
5064 return match;
5065 }
5066 }
5067 return NULL;
5068 }
5069
5070
5071
5072
5073
5074
5075
5076
5077 xmlNode *
5078 crm_next_same_xml(xmlNode *sibling)
5079 {
5080 xmlNode *match = __xml_next(sibling);
5081 const char *name = crm_element_name(sibling);
5082
5083 while (match != NULL) {
5084 if (!strcmp(crm_element_name(match), name)) {
5085 return match;
5086 }
5087 match = __xml_next(match);
5088 }
5089 return NULL;
5090 }
5091
5092 void
5093 crm_xml_init(void)
5094 {
5095 static bool init = TRUE;
5096
5097 if(init) {
5098 init = FALSE;
5099
5100
5101
5102
5103
5104 xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
5105
5106
5107 xmlDeregisterNodeDefault(pcmkDeregisterNode);
5108 xmlRegisterNodeDefault(pcmkRegisterNode);
5109
5110 crm_schema_init();
5111 }
5112 }
5113
5114 void
5115 crm_xml_cleanup(void)
5116 {
5117 crm_info("Cleaning up memory from libxml2");
5118 crm_schema_cleanup();
5119 xmlCleanupParser();
5120 }
5121
5122 #define XPATH_MAX 512
5123
5124 xmlNode *
5125 expand_idref(xmlNode * input, xmlNode * top)
5126 {
5127 const char *tag = NULL;
5128 const char *ref = NULL;
5129 xmlNode *result = input;
5130 char *xpath_string = NULL;
5131
5132 if (result == NULL) {
5133 return NULL;
5134
5135 } else if (top == NULL) {
5136 top = input;
5137 }
5138
5139 tag = crm_element_name(result);
5140 ref = crm_element_value(result, XML_ATTR_IDREF);
5141
5142 if (ref != NULL) {
5143 int offset = 0;
5144
5145 xpath_string = calloc(1, XPATH_MAX);
5146
5147 offset += snprintf(xpath_string + offset, XPATH_MAX - offset, "//%s[@id='%s']", tag, ref);
5148 CRM_LOG_ASSERT(offset > 0);
5149
5150 result = get_xpath_object(xpath_string, top, LOG_ERR);
5151 if (result == NULL) {
5152 char *nodePath = (char *)xmlGetNodePath(top);
5153
5154 crm_err("No match for %s found in %s: Invalid configuration", xpath_string,
5155 crm_str(nodePath));
5156 free(nodePath);
5157 }
5158 }
5159
5160 free(xpath_string);
5161 return result;
5162 }
5163
5164 const char *
5165 crm_element_value(xmlNode * data, const char *name)
5166 {
5167 xmlAttr *attr = NULL;
5168
5169 if (data == NULL) {
5170 crm_err("Couldn't find %s in NULL", name ? name : "<null>");
5171 CRM_LOG_ASSERT(data != NULL);
5172 return NULL;
5173
5174 } else if (name == NULL) {
5175 crm_err("Couldn't find NULL in %s", crm_element_name(data));
5176 return NULL;
5177 }
5178
5179 attr = xmlHasProp(data, (const xmlChar *)name);
5180 if (attr == NULL || attr->children == NULL) {
5181 return NULL;
5182 }
5183 return (const char *)attr->children->content;
5184 }
5185
5186 void
5187 crm_destroy_xml(gpointer data)
5188 {
5189 free_xml(data);
5190 }