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