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