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