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