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