pacemaker  1.1.14-70404b0
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
xml.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <crm_internal.h>
20 #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  /* Compatability handling for pacemaker < 1.1.12 */
621  }
622  if(ref == NULL) {
623  /* Compatability 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 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_trace("Status changed %d", format);
1558  crm_element_value_int(target, XML_ATTR_NUMUPDATES, &counter);
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)", vfields[lpc], this[lpc], del[lpc]);
2066  return -pcmk_err_diff_resync;
2067 
2068  } else if(this[lpc] > del[lpc]) {
2069  crm_info("Current %s is too high (%d > %d)", vfields[lpc], this[lpc], del[lpc]);
2070  return -pcmk_err_old_data;
2071  }
2072  }
2073 
2074  for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
2075  if(add[lpc] > del[lpc]) {
2076  changed = TRUE;
2077  }
2078  }
2079 
2080  if(changed == FALSE) {
2081  crm_notice("Versions did not change in patch %d.%d.%d", add[0], add[1], add[2]);
2082  return -pcmk_err_old_data;
2083  }
2084 
2085  crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
2086  add[0], add[1], add[2], this[0], this[1], this[2]);
2087  return pcmk_ok;
2088 }
2089 
2090 static int
2091 xml_apply_patchset_v1(xmlNode *xml, xmlNode *patchset, bool check_version)
2092 {
2093  int rc = pcmk_ok;
2094  int root_nodes_seen = 0;
2095  char *version = crm_element_value_copy(xml, XML_ATTR_CRM_VERSION);
2096 
2097  xmlNode *child_diff = NULL;
2098  xmlNode *added = find_xml_node(patchset, "diff-added", FALSE);
2099  xmlNode *removed = find_xml_node(patchset, "diff-removed", FALSE);
2100  xmlNode *old = copy_xml(xml);
2101 
2102  crm_trace("Substraction Phase");
2103  for (child_diff = __xml_first_child(removed); child_diff != NULL;
2104  child_diff = __xml_next(child_diff)) {
2105  CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
2106  if (root_nodes_seen == 0) {
2107  __subtract_xml_object(xml, child_diff);
2108  }
2109  root_nodes_seen++;
2110  }
2111 
2112  if (root_nodes_seen > 1) {
2113  crm_err("(-) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
2114  rc = -ENOTUNIQ;
2115  }
2116 
2117  root_nodes_seen = 0;
2118  crm_trace("Addition Phase");
2119  if (rc == pcmk_ok) {
2120  xmlNode *child_diff = NULL;
2121 
2122  for (child_diff = __xml_first_child(added); child_diff != NULL;
2123  child_diff = __xml_next(child_diff)) {
2124  CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
2125  if (root_nodes_seen == 0) {
2126  __add_xml_object(NULL, xml, child_diff);
2127  }
2128  root_nodes_seen++;
2129  }
2130  }
2131 
2132  if (root_nodes_seen > 1) {
2133  crm_err("(+) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
2134  rc = -ENOTUNIQ;
2135  }
2136 
2137  purge_diff_markers(xml); /* Purge prior to checking the digest */
2138 
2139  free_xml(old);
2140  free(version);
2141  return rc;
2142 }
2143 
2144 static xmlNode *
2145 __first_xml_child_match(xmlNode *parent, const char *name, const char *id)
2146 {
2147  xmlNode *cIter = NULL;
2148 
2149  for (cIter = __xml_first_child(parent); cIter != NULL; cIter = __xml_next(cIter)) {
2150  if(strcmp((const char *)cIter->name, name) != 0) {
2151  continue;
2152  } else if(id) {
2153  const char *cid = ID(cIter);
2154  if(cid == NULL || strcmp(cid, id) != 0) {
2155  continue;
2156  }
2157  }
2158  return cIter;
2159  }
2160  return NULL;
2161 }
2162 
2163 static xmlNode *
2164 __xml_find_path(xmlNode *top, const char *key)
2165 {
2166  xmlNode *target = (xmlNode*)top->doc;
2167  char *id = malloc(XML_BUFFER_SIZE);
2168  char *tag = malloc(XML_BUFFER_SIZE);
2169  char *section = malloc(XML_BUFFER_SIZE);
2170  char *current = strdup(key);
2171  char *remainder = malloc(XML_BUFFER_SIZE);
2172  int rc = 0;
2173 
2174  while(current) {
2175  rc = sscanf (current, "/%[^/]%s", section, remainder);
2176  if(rc <= 0) {
2177  crm_trace("Done");
2178  break;
2179 
2180  } else if(rc > 2) {
2181  crm_trace("Aborting on %s", current);
2182  target = NULL;
2183  break;
2184 
2185  } else if(tag && section) {
2186  int f = sscanf (section, "%[^[][@id='%[^']", tag, id);
2187 
2188  switch(f) {
2189  case 1:
2190  target = __first_xml_child_match(target, tag, NULL);
2191  break;
2192  case 2:
2193  target = __first_xml_child_match(target, tag, id);
2194  break;
2195  default:
2196  crm_trace("Aborting on %s", section);
2197  target = NULL;
2198  break;
2199  }
2200 
2201  if(rc == 1 || target == NULL) {
2202  crm_trace("Done");
2203  break;
2204 
2205  } else {
2206  char *tmp = current;
2207  current = remainder;
2208  remainder = tmp;
2209  }
2210  }
2211  }
2212 
2213  if(target) {
2214  char *path = (char *)xmlGetNodePath(target);
2215 
2216  crm_trace("Found %s for %s", path, key);
2217  free(path);
2218  } else {
2219  crm_debug("No match for %s", key);
2220  }
2221 
2222  free(remainder);
2223  free(current);
2224  free(section);
2225  free(tag);
2226  free(id);
2227  return target;
2228 }
2229 
2230 static int
2231 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset, bool check_version)
2232 {
2233  int rc = pcmk_ok;
2234  xmlNode *change = NULL;
2235  for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
2236  xmlNode *match = NULL;
2237  const char *op = crm_element_value(change, XML_DIFF_OP);
2238  const char *xpath = crm_element_value(change, XML_DIFF_PATH);
2239 
2240  crm_trace("Processing %s %s", change->name, op);
2241  if(op == NULL) {
2242  continue;
2243  }
2244 
2245 #if 0
2246  match = get_xpath_object(xpath, xml, LOG_TRACE);
2247 #else
2248  match = __xml_find_path(xml, xpath);
2249 #endif
2250  crm_trace("Performing %s on %s with %p", op, xpath, match);
2251 
2252  if(match == NULL && strcmp(op, "delete") == 0) {
2253  crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
2254  continue;
2255 
2256  } else if(match == NULL) {
2257  crm_err("No %s match for %s in %p", op, xpath, xml->doc);
2258  rc = -pcmk_err_diff_failed;
2259  continue;
2260 
2261  } else if(strcmp(op, "create") == 0) {
2262  int position = 0;
2263  xmlNode *child = NULL;
2264  xmlNode *match_child = NULL;
2265 
2266  match_child = match->children;
2267  crm_element_value_int(change, XML_DIFF_POSITION, &position);
2268 
2269  while(match_child && position != __xml_offset(match_child)) {
2270  match_child = match_child->next;
2271  }
2272 
2273  child = xmlDocCopyNode(change->children, match->doc, 1);
2274  if(match_child) {
2275  crm_trace("Adding %s at position %d", child->name, position);
2276  xmlAddPrevSibling(match_child, child);
2277 
2278  } else if(match->last) { /* Add to the end */
2279  crm_trace("Adding %s at position %d (end)", child->name, position);
2280  xmlAddNextSibling(match->last, child);
2281 
2282  } else {
2283  crm_trace("Adding %s at position %d (first)", child->name, position);
2284  CRM_LOG_ASSERT(position == 0);
2285  xmlAddChild(match, child);
2286  }
2287  crm_node_created(child);
2288 
2289  } else if(strcmp(op, "move") == 0) {
2290  int position = 0;
2291 
2292  crm_element_value_int(change, XML_DIFF_POSITION, &position);
2293  if(position != __xml_offset(match)) {
2294  xmlNode *match_child = NULL;
2295  int p = position;
2296 
2297  if(p > __xml_offset(match)) {
2298  p++; /* Skip ourselves */
2299  }
2300 
2301  CRM_ASSERT(match->parent != NULL);
2302  match_child = match->parent->children;
2303 
2304  while(match_child && p != __xml_offset(match_child)) {
2305  match_child = match_child->next;
2306  }
2307 
2308  crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
2309  match->name, position, __xml_offset(match), match->prev,
2310  match_child?"next":"last", match_child?match_child:match->parent->last);
2311 
2312  if(match_child) {
2313  xmlAddPrevSibling(match_child, match);
2314 
2315  } else {
2316  CRM_ASSERT(match->parent->last != NULL);
2317  xmlAddNextSibling(match->parent->last, match);
2318  }
2319 
2320  } else {
2321  crm_trace("%s is already in position %d", match->name, position);
2322  }
2323 
2324  if(position != __xml_offset(match)) {
2325  crm_err("Moved %s.%d to position %d instead of %d (%p)",
2326  match->name, ID(match), __xml_offset(match), position, match->prev);
2327  rc = -pcmk_err_diff_failed;
2328  }
2329 
2330  } else if(strcmp(op, "delete") == 0) {
2331  free_xml(match);
2332 
2333  } else if(strcmp(op, "modify") == 0) {
2334  xmlAttr *pIter = crm_first_attr(match);
2335  xmlNode *attrs = __xml_first_child(first_named_child(change, XML_DIFF_RESULT));
2336 
2337  if(attrs == NULL) {
2338  rc = -ENOMSG;
2339  continue;
2340  }
2341  while(pIter != NULL) {
2342  const char *name = (const char *)pIter->name;
2343 
2344  pIter = pIter->next;
2345  xml_remove_prop(match, name);
2346  }
2347 
2348  for (pIter = crm_first_attr(attrs); pIter != NULL; pIter = pIter->next) {
2349  const char *name = (const char *)pIter->name;
2350  const char *value = crm_element_value(attrs, name);
2351 
2352  crm_xml_add(match, name, value);
2353  }
2354 
2355  } else {
2356  crm_err("Unknown operation: %s", op);
2357  }
2358  }
2359  return rc;
2360 }
2361 
2362 int
2363 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
2364 {
2365  int format = 1;
2366  int rc = pcmk_ok;
2367  xmlNode *old = NULL;
2368  const char *digest = crm_element_value(patchset, XML_ATTR_DIGEST);
2369 
2370  if(patchset == NULL) {
2371  return rc;
2372  }
2373 
2374  xml_log_patchset(LOG_TRACE, __FUNCTION__, patchset);
2375 
2376  crm_element_value_int(patchset, "format", &format);
2377  if(check_version) {
2378  rc = xml_patch_version_check(xml, patchset, format);
2379  if(rc != pcmk_ok) {
2380  return rc;
2381  }
2382  }
2383 
2384  if(digest) {
2385  /* Make it available for logging if the result doesn't have the expected digest */
2386  old = copy_xml(xml);
2387  }
2388 
2389  if(rc == pcmk_ok) {
2390  switch(format) {
2391  case 1:
2392  rc = xml_apply_patchset_v1(xml, patchset, check_version);
2393  break;
2394  case 2:
2395  rc = xml_apply_patchset_v2(xml, patchset, check_version);
2396  break;
2397  default:
2398  crm_err("Unknown patch format: %d", format);
2399  rc = -EINVAL;
2400  }
2401  }
2402 
2403  if(rc == pcmk_ok && digest) {
2404  static struct qb_log_callsite *digest_cs = NULL;
2405 
2406  char *new_digest = NULL;
2407  char *version = crm_element_value_copy(xml, XML_ATTR_CRM_VERSION);
2408 
2409  if (digest_cs == NULL) {
2410  digest_cs =
2411  qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
2413  }
2414 
2415  new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
2416  if (safe_str_neq(new_digest, digest)) {
2417  crm_info("v%d digest mis-match: expected %s, calculated %s", format, digest, new_digest);
2418  rc = -pcmk_err_diff_failed;
2419 
2420  if (digest_cs && digest_cs->targets) {
2421  save_xml_to_file(old, "PatchDigest:input", NULL);
2422  save_xml_to_file(xml, "PatchDigest:result", NULL);
2423  save_xml_to_file(patchset,"PatchDigest:diff", NULL);
2424 
2425  } else {
2426  crm_trace("%p %0.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
2427  }
2428 
2429  } else {
2430  crm_trace("v%d digest matched: expected %s, calculated %s", format, digest, new_digest);
2431  }
2432  free(new_digest);
2433  free(version);
2434  }
2435  free_xml(old);
2436  return rc;
2437 }
2438 
2439 xmlNode *
2440 find_xml_node(xmlNode * root, const char *search_path, gboolean must_find)
2441 {
2442  xmlNode *a_child = NULL;
2443  const char *name = "NULL";
2444 
2445  if (root != NULL) {
2446  name = crm_element_name(root);
2447  }
2448 
2449  if (search_path == NULL) {
2450  crm_warn("Will never find <NULL>");
2451  return NULL;
2452  }
2453 
2454  for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
2455  if (strcmp((const char *)a_child->name, search_path) == 0) {
2456 /* crm_trace("returning node (%s).", crm_element_name(a_child)); */
2457  return a_child;
2458  }
2459  }
2460 
2461  if (must_find) {
2462  crm_warn("Could not find %s in %s.", search_path, name);
2463  } else if (root != NULL) {
2464  crm_trace("Could not find %s in %s.", search_path, name);
2465  } else {
2466  crm_trace("Could not find %s in <NULL>.", search_path);
2467  }
2468 
2469  return NULL;
2470 }
2471 
2472 xmlNode *
2473 find_entity(xmlNode * parent, const char *node_name, const char *id)
2474 {
2475  xmlNode *a_child = NULL;
2476 
2477  for (a_child = __xml_first_child(parent); a_child != NULL; a_child = __xml_next(a_child)) {
2478  /* Uncertain if node_name == NULL check is strictly necessary here */
2479  if (node_name == NULL || strcmp((const char *)a_child->name, node_name) == 0) {
2480  const char *cid = ID(a_child);
2481  if (id == NULL || (cid != NULL && strcmp(id, cid) == 0)) {
2482  return a_child;
2483  }
2484  }
2485  }
2486 
2487  crm_trace("node <%s id=%s> not found in %s.", node_name, id, crm_element_name(parent));
2488  return NULL;
2489 }
2490 
2491 void
2492 copy_in_properties(xmlNode * target, xmlNode * src)
2493 {
2494  if (src == NULL) {
2495  crm_warn("No node to copy properties from");
2496 
2497  } else if (target == NULL) {
2498  crm_err("No node to copy properties into");
2499 
2500  } else {
2501  xmlAttrPtr pIter = NULL;
2502 
2503  for (pIter = crm_first_attr(src); pIter != NULL; pIter = pIter->next) {
2504  const char *p_name = (const char *)pIter->name;
2505  const char *p_value = crm_attr_value(pIter);
2506 
2507  expand_plus_plus(target, p_name, p_value);
2508  }
2509  }
2510 
2511  return;
2512 }
2513 
2514 void
2515 fix_plus_plus_recursive(xmlNode * target)
2516 {
2517  /* TODO: Remove recursion and use xpath searches for value++ */
2518  xmlNode *child = NULL;
2519  xmlAttrPtr pIter = NULL;
2520 
2521  for (pIter = crm_first_attr(target); pIter != NULL; pIter = pIter->next) {
2522  const char *p_name = (const char *)pIter->name;
2523  const char *p_value = crm_attr_value(pIter);
2524 
2525  expand_plus_plus(target, p_name, p_value);
2526  }
2527  for (child = __xml_first_child(target); child != NULL; child = __xml_next(child)) {
2528  fix_plus_plus_recursive(child);
2529  }
2530 }
2531 
2532 void
2533 expand_plus_plus(xmlNode * target, const char *name, const char *value)
2534 {
2535  int offset = 1;
2536  int name_len = 0;
2537  int int_value = 0;
2538  int value_len = 0;
2539 
2540  const char *old_value = NULL;
2541 
2542  if (value == NULL || name == NULL) {
2543  return;
2544  }
2545 
2546  old_value = crm_element_value(target, name);
2547 
2548  if (old_value == NULL) {
2549  /* if no previous value, set unexpanded */
2550  goto set_unexpanded;
2551 
2552  } else if (strstr(value, name) != value) {
2553  goto set_unexpanded;
2554  }
2555 
2556  name_len = strlen(name);
2557  value_len = strlen(value);
2558  if (value_len < (name_len + 2)
2559  || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
2560  goto set_unexpanded;
2561  }
2562 
2563  /* if we are expanding ourselves,
2564  * then no previous value was set and leave int_value as 0
2565  */
2566  if (old_value != value) {
2567  int_value = char2score(old_value);
2568  }
2569 
2570  if (value[name_len + 1] != '+') {
2571  const char *offset_s = value + (name_len + 2);
2572 
2573  offset = char2score(offset_s);
2574  }
2575  int_value += offset;
2576 
2577  if (int_value > INFINITY) {
2578  int_value = (int)INFINITY;
2579  }
2580 
2581  crm_xml_add_int(target, name, int_value);
2582  return;
2583 
2584  set_unexpanded:
2585  if (old_value == value) {
2586  /* the old value is already set, nothing to do */
2587  return;
2588  }
2589  crm_xml_add(target, name, value);
2590  return;
2591 }
2592 
2593 xmlDoc *
2594 getDocPtr(xmlNode * node)
2595 {
2596  xmlDoc *doc = NULL;
2597 
2598  CRM_CHECK(node != NULL, return NULL);
2599 
2600  doc = node->doc;
2601  if (doc == NULL) {
2602  doc = xmlNewDoc((const xmlChar *)"1.0");
2603  xmlDocSetRootElement(doc, node);
2604  xmlSetTreeDoc(node, doc);
2605  }
2606  return doc;
2607 }
2608 
2609 xmlNode *
2610 add_node_copy(xmlNode * parent, xmlNode * src_node)
2611 {
2612  xmlNode *child = NULL;
2613  xmlDoc *doc = getDocPtr(parent);
2614 
2615  CRM_CHECK(src_node != NULL, return NULL);
2616 
2617  child = xmlDocCopyNode(src_node, doc, 1);
2618  xmlAddChild(parent, child);
2619  crm_node_created(child);
2620  return child;
2621 }
2622 
2623 int
2624 add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child)
2625 {
2626  add_node_copy(parent, child);
2627  free_xml(child);
2628  return 1;
2629 }
2630 
2631 static bool
2632 __xml_acl_check(xmlNode *xml, const char *name, enum xml_private_flags mode)
2633 {
2634  CRM_ASSERT(xml);
2635  CRM_ASSERT(xml->doc);
2636  CRM_ASSERT(xml->doc->_private);
2637 
2638 #if ENABLE_ACL
2639  {
2640  if(TRACKING_CHANGES(xml) && xml_acl_enabled(xml)) {
2641  int offset = 0;
2642  xmlNode *parent = xml;
2643  char buffer[XML_BUFFER_SIZE];
2644  xml_private_t *docp = xml->doc->_private;
2645 
2646  if(docp->acls == NULL) {
2647  crm_trace("Ordinary user %s cannot access the CIB without any defined ACLs", docp->user);
2648  set_doc_flag(xml, xpf_acl_denied);
2649  return FALSE;
2650  }
2651 
2652  offset = __get_prefix(NULL, xml, buffer, offset);
2653  if(name) {
2654  offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "[@%s]", name);
2655  }
2656  CRM_LOG_ASSERT(offset > 0);
2657 
2658  /* Walk the tree upwards looking for xml_acl_* flags
2659  * - Creating an attribute requires write permissions for the node
2660  * - Creating a child requires write permissions for the parent
2661  */
2662 
2663  if(name) {
2664  xmlAttr *attr = xmlHasProp(xml, (const xmlChar *)name);
2665 
2666  if(attr && mode == xpf_acl_create) {
2667  mode = xpf_acl_write;
2668  }
2669  }
2670 
2671  while(parent && parent->_private) {
2672  xml_private_t *p = parent->_private;
2673  if(__xml_acl_mode_test(p->flags, mode)) {
2674  return TRUE;
2675 
2676  } else if(is_set(p->flags, xpf_acl_deny)) {
2677  crm_trace("%x access denied to %s: parent", mode, buffer);
2678  set_doc_flag(xml, xpf_acl_denied);
2679  return FALSE;
2680  }
2681  parent = parent->parent;
2682  }
2683 
2684  crm_trace("%x access denied to %s: default", mode, buffer);
2685  set_doc_flag(xml, xpf_acl_denied);
2686  return FALSE;
2687  }
2688  }
2689 #endif
2690 
2691  return TRUE;
2692 }
2693 
2694 const char *
2695 crm_xml_add(xmlNode * node, const char *name, const char *value)
2696 {
2697  bool dirty = FALSE;
2698  xmlAttr *attr = NULL;
2699 
2700  CRM_CHECK(node != NULL, return NULL);
2701  CRM_CHECK(name != NULL, return NULL);
2702 
2703  if (value == NULL) {
2704  return NULL;
2705  }
2706 #if XML_PARANOIA_CHECKS
2707  {
2708  const char *old_value = NULL;
2709 
2710  old_value = crm_element_value(node, name);
2711 
2712  /* Could be re-setting the same value */
2713  CRM_CHECK(old_value != value, crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
2714  return value);
2715  }
2716 #endif
2717 
2718  if(TRACKING_CHANGES(node)) {
2719  const char *old = crm_element_value(node, name);
2720 
2721  if(old == NULL || value == NULL || strcmp(old, value) != 0) {
2722  dirty = TRUE;
2723  }
2724  }
2725 
2726  if(dirty && __xml_acl_check(node, name, xpf_acl_create) == FALSE) {
2727  crm_trace("Cannot add %s=%s to %s", name, value, node->name);
2728  return NULL;
2729  }
2730 
2731  attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
2732  if(dirty) {
2733  crm_attr_dirty(attr);
2734  }
2735 
2736  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
2737  return (char *)attr->children->content;
2738 }
2739 
2740 const char *
2741 crm_xml_replace(xmlNode * node, const char *name, const char *value)
2742 {
2743  bool dirty = FALSE;
2744  xmlAttr *attr = NULL;
2745  const char *old_value = NULL;
2746 
2747  CRM_CHECK(node != NULL, return NULL);
2748  CRM_CHECK(name != NULL && name[0] != 0, return NULL);
2749 
2750  old_value = crm_element_value(node, name);
2751 
2752  /* Could be re-setting the same value */
2753  CRM_CHECK(old_value != value, return value);
2754 
2755  if(__xml_acl_check(node, name, xpf_acl_write) == FALSE) {
2756  /* Create a fake object linked to doc->_private instead? */
2757  crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
2758  return NULL;
2759 
2760  } else if (old_value != NULL && value == NULL) {
2761  xml_remove_prop(node, name);
2762  return NULL;
2763 
2764  } else if (value == NULL) {
2765  return NULL;
2766  }
2767 
2768  if(TRACKING_CHANGES(node)) {
2769  if(old_value == NULL || value == NULL || strcmp(old_value, value) != 0) {
2770  dirty = TRUE;
2771  }
2772  }
2773 
2774  attr = xmlSetProp(node, (const xmlChar *)name, (const xmlChar *)value);
2775  if(dirty) {
2776  crm_attr_dirty(attr);
2777  }
2778  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
2779  return (char *)attr->children->content;
2780 }
2781 
2782 const char *
2783 crm_xml_add_int(xmlNode * node, const char *name, int value)
2784 {
2785  char *number = crm_itoa(value);
2786  const char *added = crm_xml_add(node, name, number);
2787 
2788  free(number);
2789  return added;
2790 }
2791 
2792 xmlNode *
2793 create_xml_node(xmlNode * parent, const char *name)
2794 {
2795  xmlDoc *doc = NULL;
2796  xmlNode *node = NULL;
2797 
2798  if (name == NULL || name[0] == 0) {
2799  CRM_CHECK(name != NULL && name[0] == 0, return NULL);
2800  return NULL;
2801  }
2802 
2803  if (parent == NULL) {
2804  doc = xmlNewDoc((const xmlChar *)"1.0");
2805  node = xmlNewDocRawNode(doc, NULL, (const xmlChar *)name, NULL);
2806  xmlDocSetRootElement(doc, node);
2807 
2808  } else {
2809  doc = getDocPtr(parent);
2810  node = xmlNewDocRawNode(doc, NULL, (const xmlChar *)name, NULL);
2811  xmlAddChild(parent, node);
2812  }
2813  crm_node_created(node);
2814  return node;
2815 }
2816 
2817 static inline int
2818 __get_prefix(const char *prefix, xmlNode *xml, char *buffer, int offset)
2819 {
2820  const char *id = ID(xml);
2821 
2822  if(offset == 0 && prefix == NULL && xml->parent) {
2823  offset = __get_prefix(NULL, xml->parent, buffer, offset);
2824  }
2825 
2826  if(id) {
2827  offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "/%s[@id='%s']", (const char *)xml->name, id);
2828  } else if(xml->name) {
2829  offset += snprintf(buffer + offset, XML_BUFFER_SIZE - offset, "/%s", (const char *)xml->name);
2830  }
2831 
2832  return offset;
2833 }
2834 
2835 char *
2836 xml_get_path(xmlNode *xml)
2837 {
2838  int offset = 0;
2839  char buffer[XML_BUFFER_SIZE];
2840 
2841  if(__get_prefix(NULL, xml, buffer, offset) > 0) {
2842  return strdup(buffer);
2843  }
2844  return NULL;
2845 }
2846 
2847 void
2848 free_xml(xmlNode * child)
2849 {
2850  if (child != NULL) {
2851  xmlNode *top = NULL;
2852  xmlDoc *doc = child->doc;
2853  xml_private_t *p = child->_private;
2854 
2855  if (doc != NULL) {
2856  top = xmlDocGetRootElement(doc);
2857  }
2858 
2859  if (doc != NULL && top == child) {
2860  /* Free everything */
2861  xmlFreeDoc(doc);
2862 
2863  } else if(__xml_acl_check(child, NULL, xpf_acl_write) == FALSE) {
2864  int offset = 0;
2865  char buffer[XML_BUFFER_SIZE];
2866 
2867  __get_prefix(NULL, child, buffer, offset);
2868  crm_trace("Cannot remove %s %x", buffer, p->flags);
2869  return;
2870 
2871  } else {
2872  if(doc && TRACKING_CHANGES(child) && is_not_set(p->flags, xpf_created)) {
2873  int offset = 0;
2874  char buffer[XML_BUFFER_SIZE];
2875 
2876  if(__get_prefix(NULL, child, buffer, offset) > 0) {
2877  crm_trace("Deleting %s %p from %p", buffer, child, doc);
2878  p = doc->_private;
2879  p->deleted_paths = g_list_append(p->deleted_paths, strdup(buffer));
2880  set_doc_flag(child, xpf_dirty);
2881  }
2882  }
2883 
2884  /* Free this particular subtree
2885  * Make sure to unlink it from the parent first
2886  */
2887  xmlUnlinkNode(child);
2888  xmlFreeNode(child);
2889  }
2890  }
2891 }
2892 
2893 xmlNode *
2894 copy_xml(xmlNode * src)
2895 {
2896  xmlDoc *doc = xmlNewDoc((const xmlChar *)"1.0");
2897  xmlNode *copy = xmlDocCopyNode(src, doc, 1);
2898 
2899  xmlDocSetRootElement(doc, copy);
2900  xmlSetTreeDoc(copy, doc);
2901  return copy;
2902 }
2903 
2904 static void
2905 crm_xml_err(void *ctx, const char *msg, ...)
2906 G_GNUC_PRINTF(2, 3);
2907 
2908 static void
2909 crm_xml_err(void *ctx, const char *msg, ...)
2910 {
2911  int len = 0;
2912  va_list args;
2913  char *buf = NULL;
2914  static int buffer_len = 0;
2915  static char *buffer = NULL;
2916  static struct qb_log_callsite *xml_error_cs = NULL;
2917 
2918  va_start(args, msg);
2919  len = vasprintf(&buf, msg, args);
2920 
2921  if(xml_error_cs == NULL) {
2922  xml_error_cs = qb_log_callsite_get(
2923  __func__, __FILE__, "xml library error", LOG_TRACE, __LINE__, crm_trace_nonlog);
2924  }
2925 
2926  if (strchr(buf, '\n')) {
2927  buf[len - 1] = 0;
2928  if (buffer) {
2929  crm_err("XML Error: %s%s", buffer, buf);
2930  free(buffer);
2931  } else {
2932  crm_err("XML Error: %s", buf);
2933  }
2934  if (xml_error_cs && xml_error_cs->targets) {
2935  crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, "xml library error", TRUE, TRUE);
2936  }
2937  buffer = NULL;
2938  buffer_len = 0;
2939 
2940  } else if (buffer == NULL) {
2941  buffer_len = len;
2942  buffer = buf;
2943  buf = NULL;
2944 
2945  } else {
2946  buffer = realloc_safe(buffer, 1 + buffer_len + len);
2947  memcpy(buffer + buffer_len, buf, len);
2948  buffer_len += len;
2949  buffer[buffer_len] = 0;
2950  }
2951 
2952  va_end(args);
2953  free(buf);
2954 }
2955 
2956 xmlNode *
2957 string2xml(const char *input)
2958 {
2959  xmlNode *xml = NULL;
2960  xmlDocPtr output = NULL;
2961  xmlParserCtxtPtr ctxt = NULL;
2962  xmlErrorPtr last_error = NULL;
2963 
2964  if (input == NULL) {
2965  crm_err("Can't parse NULL input");
2966  return NULL;
2967  }
2968 
2969  /* create a parser context */
2970  ctxt = xmlNewParserCtxt();
2971  CRM_CHECK(ctxt != NULL, return NULL);
2972 
2973  /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
2974 
2975  xmlCtxtResetLastError(ctxt);
2976  xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2977  /* initGenericErrorDefaultFunc(crm_xml_err); */
2978  output =
2979  xmlCtxtReadDoc(ctxt, (const xmlChar *)input, NULL, NULL,
2980  XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
2981  if (output) {
2982  xml = xmlDocGetRootElement(output);
2983  }
2984  last_error = xmlCtxtGetLastError(ctxt);
2985  if (last_error && last_error->code != XML_ERR_OK) {
2986  /* crm_abort(__FILE__,__FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
2987  /*
2988  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
2989  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
2990  */
2991  crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
2992  last_error->domain, last_error->level, last_error->code, last_error->message);
2993 
2994  if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
2995  CRM_LOG_ASSERT("Cannot parse an empty string");
2996 
2997  } else if (last_error->code != XML_ERR_DOCUMENT_END) {
2998  crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
2999  input);
3000  if (xml != NULL) {
3001  crm_log_xml_err(xml, "Partial");
3002  }
3003 
3004  } else {
3005  int len = strlen(input);
3006  int lpc = 0;
3007 
3008  while(lpc < len) {
3009  crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
3010  lpc += 80;
3011  }
3012 
3013  CRM_LOG_ASSERT("String parsing error");
3014  }
3015  }
3016 
3017  xmlFreeParserCtxt(ctxt);
3018  return xml;
3019 }
3020 
3021 xmlNode *
3023 {
3024  size_t data_length = 0;
3025  size_t read_chars = 0;
3026 
3027  char *xml_buffer = NULL;
3028  xmlNode *xml_obj = NULL;
3029 
3030  do {
3031  size_t next = XML_BUFFER_SIZE + data_length + 1;
3032 
3033  if(next <= 0) {
3034  crm_err("Buffer size exceeded at: %l + %d", data_length, XML_BUFFER_SIZE);
3035  break;
3036  }
3037 
3038  xml_buffer = realloc_safe(xml_buffer, next);
3039  read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin);
3040  data_length += read_chars;
3041  } while (read_chars > 0);
3042 
3043  if (data_length == 0) {
3044  crm_warn("No XML supplied on stdin");
3045  free(xml_buffer);
3046  return NULL;
3047  }
3048 
3049  xml_buffer[data_length] = '\0';
3050 
3051  xml_obj = string2xml(xml_buffer);
3052  free(xml_buffer);
3053 
3054  crm_log_xml_trace(xml_obj, "Created fragment");
3055  return xml_obj;
3056 }
3057 
3058 static char *
3059 decompress_file(const char *filename)
3060 {
3061  char *buffer = NULL;
3062 
3063 #if HAVE_BZLIB_H
3064  int rc = 0;
3065  size_t length = 0, read_len = 0;
3066 
3067  BZFILE *bz_file = NULL;
3068  FILE *input = fopen(filename, "r");
3069 
3070  if (input == NULL) {
3071  crm_perror(LOG_ERR, "Could not open %s for reading", filename);
3072  return NULL;
3073  }
3074 
3075  bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
3076 
3077  if (rc != BZ_OK) {
3078  BZ2_bzReadClose(&rc, bz_file);
3079  return NULL;
3080  }
3081 
3082  rc = BZ_OK;
3083  while (rc == BZ_OK) {
3084  buffer = realloc_safe(buffer, XML_BUFFER_SIZE + length + 1);
3085  read_len = BZ2_bzRead(&rc, bz_file, buffer + length, XML_BUFFER_SIZE);
3086 
3087  crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
3088 
3089  if (rc == BZ_OK || rc == BZ_STREAM_END) {
3090  length += read_len;
3091  }
3092  }
3093 
3094  buffer[length] = '\0';
3095 
3096  if (rc != BZ_STREAM_END) {
3097  crm_err("Couldn't read compressed xml from file");
3098  free(buffer);
3099  buffer = NULL;
3100  }
3101 
3102  BZ2_bzReadClose(&rc, bz_file);
3103  fclose(input);
3104 
3105 #else
3106  crm_err("Cannot read compressed files:" " bzlib was not available at compile time");
3107 #endif
3108  return buffer;
3109 }
3110 
3111 void
3112 strip_text_nodes(xmlNode * xml)
3113 {
3114  xmlNode *iter = xml->children;
3115 
3116  while (iter) {
3117  xmlNode *next = iter->next;
3118 
3119  switch (iter->type) {
3120  case XML_TEXT_NODE:
3121  /* Remove it */
3122  xmlUnlinkNode(iter);
3123  xmlFreeNode(iter);
3124  break;
3125 
3126  case XML_ELEMENT_NODE:
3127  /* Search it */
3128  strip_text_nodes(iter);
3129  break;
3130 
3131  default:
3132  /* Leave it */
3133  break;
3134  }
3135 
3136  iter = next;
3137  }
3138 }
3139 
3140 xmlNode *
3141 filename2xml(const char *filename)
3142 {
3143  xmlNode *xml = NULL;
3144  xmlDocPtr output = NULL;
3145  const char *match = NULL;
3146  xmlParserCtxtPtr ctxt = NULL;
3147  xmlErrorPtr last_error = NULL;
3148  static int xml_options = XML_PARSE_NOBLANKS | XML_PARSE_RECOVER;
3149 
3150  /* create a parser context */
3151  ctxt = xmlNewParserCtxt();
3152  CRM_CHECK(ctxt != NULL, return NULL);
3153 
3154  /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
3155 
3156  xmlCtxtResetLastError(ctxt);
3157  xmlSetGenericErrorFunc(ctxt, crm_xml_err);
3158  /* initGenericErrorDefaultFunc(crm_xml_err); */
3159 
3160  if (filename) {
3161  match = strstr(filename, ".bz2");
3162  }
3163 
3164  if (filename == NULL) {
3165  /* STDIN_FILENO == fileno(stdin) */
3166  output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, xml_options);
3167 
3168  } else if (match == NULL || match[4] != 0) {
3169  output = xmlCtxtReadFile(ctxt, filename, NULL, xml_options);
3170 
3171  } else {
3172  char *input = decompress_file(filename);
3173 
3174  output = xmlCtxtReadDoc(ctxt, (const xmlChar *)input, NULL, NULL, xml_options);
3175  free(input);
3176  }
3177 
3178  if (output && (xml = xmlDocGetRootElement(output))) {
3179  strip_text_nodes(xml);
3180  }
3181 
3182  last_error = xmlCtxtGetLastError(ctxt);
3183  if (last_error && last_error->code != XML_ERR_OK) {
3184  /* crm_abort(__FILE__,__FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
3185  /*
3186  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
3187  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
3188  */
3189  crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
3190  last_error->domain, last_error->level, last_error->code, last_error->message);
3191 
3192  if (last_error && last_error->code != XML_ERR_OK) {
3193  crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
3194  if (xml != NULL) {
3195  crm_log_xml_err(xml, "Partial");
3196  }
3197  }
3198  }
3199 
3200  xmlFreeParserCtxt(ctxt);
3201  return xml;
3202 }
3203 
3212 const char *
3213 crm_xml_add_last_written(xmlNode *xml_node)
3214 {
3215  time_t now = time(NULL);
3216  char *now_str = ctime(&now);
3217 
3218  now_str[24] = EOS; /* replace the newline */
3219  return crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str);
3220 }
3221 
3222 static int
3223 write_xml_stream(xmlNode * xml_node, const char *filename, FILE * stream, gboolean compress)
3224 {
3225  int res = 0;
3226  char *buffer = NULL;
3227  unsigned int out = 0;
3228 
3229  CRM_CHECK(stream != NULL, return -1);
3230 
3231  crm_trace("Writing XML out to %s", filename);
3232  if (xml_node == NULL) {
3233  crm_err("Cannot write NULL to %s", filename);
3234  fclose(stream);
3235  return -1;
3236  }
3237 
3238 
3239  crm_log_xml_trace(xml_node, "Writing out");
3240 
3241  buffer = dump_xml_formatted(xml_node);
3242  CRM_CHECK(buffer != NULL && strlen(buffer) > 0, crm_log_xml_warn(xml_node, "dump:failed");
3243  goto bail);
3244 
3245  if (compress) {
3246 #if HAVE_BZLIB_H
3247  int rc = BZ_OK;
3248  unsigned int in = 0;
3249  BZFILE *bz_file = NULL;
3250 
3251  bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
3252  if (rc != BZ_OK) {
3253  crm_err("bzWriteOpen failed: %d", rc);
3254  } else {
3255  BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
3256  if (rc != BZ_OK) {
3257  crm_err("bzWrite() failed: %d", rc);
3258  }
3259  }
3260 
3261  if (rc == BZ_OK) {
3262  BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
3263  if (rc != BZ_OK) {
3264  crm_err("bzWriteClose() failed: %d", rc);
3265  out = -1;
3266  } else {
3267  crm_trace("%s: In: %d, out: %d", filename, in, out);
3268  }
3269  }
3270 #else
3271  crm_err("Cannot write compressed files:" " bzlib was not available at compile time");
3272 #endif
3273  }
3274 
3275  if (out <= 0) {
3276  res = fprintf(stream, "%s", buffer);
3277  if (res < 0) {
3278  crm_perror(LOG_ERR, "Cannot write output to %s", filename);
3279  goto bail;
3280  }
3281  }
3282 
3283  bail:
3284 
3285  if (fflush(stream) != 0) {
3286  crm_perror(LOG_ERR, "fflush for %s failed:", filename);
3287  res = -1;
3288  }
3289 
3290  if (fsync(fileno(stream)) < 0) {
3291  crm_perror(LOG_ERR, "fsync for %s failed:", filename);
3292  res = -1;
3293  }
3294 
3295  fclose(stream);
3296 
3297  crm_trace("Saved %d bytes to the Cib as XML", res);
3298  free(buffer);
3299 
3300  return res;
3301 }
3302 
3303 int
3304 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
3305 {
3306  FILE *stream = NULL;
3307 
3308  CRM_CHECK(fd > 0, return -1);
3309  stream = fdopen(fd, "w");
3310  return write_xml_stream(xml_node, filename, stream, compress);
3311 }
3312 
3313 int
3314 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
3315 {
3316  FILE *stream = NULL;
3317 
3318  stream = fopen(filename, "w");
3319 
3320  return write_xml_stream(xml_node, filename, stream, compress);
3321 }
3322 
3323 xmlNode *
3324 get_message_xml(xmlNode * msg, const char *field)
3325 {
3326  xmlNode *tmp = first_named_child(msg, field);
3327 
3328  return __xml_first_child(tmp);
3329 }
3330 
3331 gboolean
3332 add_message_xml(xmlNode * msg, const char *field, xmlNode * xml)
3333 {
3334  xmlNode *holder = create_xml_node(msg, field);
3335 
3336  add_node_copy(holder, xml);
3337  return TRUE;
3338 }
3339 
3340 static char *
3341 crm_xml_escape_shuffle(char *text, int start, int *length, const char *replace)
3342 {
3343  int lpc;
3344  int offset = strlen(replace) - 1; /* We have space for 1 char already */
3345 
3346  *length += offset;
3347  text = realloc_safe(text, *length);
3348 
3349  for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
3350  text[lpc] = text[lpc - offset];
3351  }
3352 
3353  memcpy(text + start, replace, offset + 1);
3354  return text;
3355 }
3356 
3357 char *
3358 crm_xml_escape(const char *text)
3359 {
3360  int index;
3361  int changes = 0;
3362  int length = 1 + strlen(text);
3363  char *copy = strdup(text);
3364 
3365  /*
3366  * When xmlCtxtReadDoc() parses &lt; and friends in a
3367  * value, it converts them to their human readable
3368  * form.
3369  *
3370  * If one uses xmlNodeDump() to convert it back to a
3371  * string, all is well, because special characters are
3372  * converted back to their escape sequences.
3373  *
3374  * However xmlNodeDump() is randomly dog slow, even with the same
3375  * input. So we need to replicate the escapeing in our custom
3376  * version so that the result can be re-parsed by xmlCtxtReadDoc()
3377  * when necessary.
3378  */
3379 
3380  for (index = 0; index < length; index++) {
3381  switch (copy[index]) {
3382  case 0:
3383  break;
3384  case '<':
3385  copy = crm_xml_escape_shuffle(copy, index, &length, "&lt;");
3386  changes++;
3387  break;
3388  case '>':
3389  copy = crm_xml_escape_shuffle(copy, index, &length, "&gt;");
3390  changes++;
3391  break;
3392  case '"':
3393  copy = crm_xml_escape_shuffle(copy, index, &length, "&quot;");
3394  changes++;
3395  break;
3396  case '\'':
3397  copy = crm_xml_escape_shuffle(copy, index, &length, "&apos;");
3398  changes++;
3399  break;
3400  case '&':
3401  copy = crm_xml_escape_shuffle(copy, index, &length, "&amp;");
3402  changes++;
3403  break;
3404  case '\t':
3405  /* Might as well just expand to a few spaces... */
3406  copy = crm_xml_escape_shuffle(copy, index, &length, " ");
3407  changes++;
3408  break;
3409  case '\n':
3410  /* crm_trace("Convert: \\%.3o", copy[index]); */
3411  copy = crm_xml_escape_shuffle(copy, index, &length, "\\n");
3412  changes++;
3413  break;
3414  case '\r':
3415  copy = crm_xml_escape_shuffle(copy, index, &length, "\\r");
3416  changes++;
3417  break;
3418  /* For debugging...
3419  case '\\':
3420  crm_trace("Passthrough: \\%c", copy[index+1]);
3421  break;
3422  */
3423  default:
3424  /* Check for and replace non-printing characters with their octal equivalent */
3425  if(copy[index] < ' ' || copy[index] > '~') {
3426  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
3427 
3428  /* crm_trace("Convert to octal: \\%.3o", copy[index]); */
3429  copy = crm_xml_escape_shuffle(copy, index, &length, replace);
3430  free(replace);
3431  changes++;
3432  }
3433  }
3434  }
3435 
3436  if (changes) {
3437  crm_trace("Dumped '%s'", copy);
3438  }
3439  return copy;
3440 }
3441 
3442 static inline void
3443 dump_xml_attr(xmlAttrPtr attr, int options, char **buffer, int *offset, int *max)
3444 {
3445  char *p_value = NULL;
3446  const char *p_name = NULL;
3447  xml_private_t *p = NULL;
3448 
3449  CRM_ASSERT(buffer != NULL);
3450  if (attr == NULL || attr->children == NULL) {
3451  return;
3452  }
3453 
3454  p = attr->_private;
3455  if (p && is_set(p->flags, xpf_deleted)) {
3456  return;
3457  }
3458 
3459  p_name = (const char *)attr->name;
3460  p_value = crm_xml_escape((const char *)attr->children->content);
3461  buffer_print(*buffer, *max, *offset, " %s=\"%s\"", p_name, p_value);
3462  free(p_value);
3463 }
3464 
3465 static void
3466 __xml_log_element(int log_level, const char *file, const char *function, int line,
3467  const char *prefix, xmlNode * data, int depth, int options)
3468 {
3469  int max = 0;
3470  int offset = 0;
3471  const char *name = NULL;
3472  const char *hidden = NULL;
3473 
3474  xmlNode *child = NULL;
3475  xmlAttrPtr pIter = NULL;
3476 
3477  if(data == NULL) {
3478  return;
3479  }
3480 
3481  name = crm_element_name(data);
3482 
3483  if(is_set(options, xml_log_option_open)) {
3484  char *buffer = NULL;
3485 
3486  insert_prefix(options, &buffer, &offset, &max, depth);
3487  if(data->type == XML_COMMENT_NODE) {
3488  buffer_print(buffer, max, offset, "<!--");
3489  buffer_print(buffer, max, offset, "%s", data->content);
3490  buffer_print(buffer, max, offset, "-->");
3491 
3492  } else {
3493  buffer_print(buffer, max, offset, "<%s", name);
3494  }
3495 
3496  hidden = crm_element_value(data, "hidden");
3497  for (pIter = crm_first_attr(data); pIter != NULL; pIter = pIter->next) {
3498  xml_private_t *p = pIter->_private;
3499  const char *p_name = (const char *)pIter->name;
3500  const char *p_value = crm_attr_value(pIter);
3501  char *p_copy = NULL;
3502 
3503  if(is_set(p->flags, xpf_deleted)) {
3504  continue;
3505  } else if ((is_set(options, xml_log_option_diff_plus)
3506  || is_set(options, xml_log_option_diff_minus))
3507  && strcmp(XML_DIFF_MARKER, p_name) == 0) {
3508  continue;
3509 
3510  } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
3511  p_copy = strdup("*****");
3512 
3513  } else {
3514  p_copy = crm_xml_escape(p_value);
3515  }
3516 
3517  buffer_print(buffer, max, offset, " %s=\"%s\"", p_name, p_copy);
3518  free(p_copy);
3519  }
3520 
3521  if(xml_has_children(data) == FALSE) {
3522  buffer_print(buffer, max, offset, "/>");
3523 
3524  } else if(is_set(options, xml_log_option_children)) {
3525  buffer_print(buffer, max, offset, ">");
3526 
3527  } else {
3528  buffer_print(buffer, max, offset, "/>");
3529  }
3530 
3531  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
3532  free(buffer);
3533  }
3534 
3535  if(data->type == XML_COMMENT_NODE) {
3536  return;
3537 
3538  } else if(xml_has_children(data) == FALSE) {
3539  return;
3540 
3541  } else if(is_set(options, xml_log_option_children)) {
3542  offset = 0;
3543  max = 0;
3544 
3545  for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
3546  __xml_log_element(log_level, file, function, line, prefix, child, depth + 1, options|xml_log_option_open|xml_log_option_close);
3547  }
3548  }
3549 
3550  if(is_set(options, xml_log_option_close)) {
3551  char *buffer = NULL;
3552 
3553  insert_prefix(options, &buffer, &offset, &max, depth);
3554  buffer_print(buffer, max, offset, "</%s>", name);
3555 
3556  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
3557  free(buffer);
3558  }
3559 }
3560 
3561 static void
3562 __xml_log_change_element(int log_level, const char *file, const char *function, int line,
3563  const char *prefix, xmlNode * data, int depth, int options)
3564 {
3565  xml_private_t *p;
3566  char *prefix_m = NULL;
3567  xmlNode *child = NULL;
3568  xmlAttrPtr pIter = NULL;
3569 
3570  if(data == NULL) {
3571  return;
3572  }
3573 
3574  p = data->_private;
3575 
3576  prefix_m = strdup(prefix);
3577  prefix_m[1] = '+';
3578 
3579  if(is_set(p->flags, xpf_dirty) && is_set(p->flags, xpf_created)) {
3580  /* Continue and log full subtree */
3581  __xml_log_element(log_level, file, function, line,
3582  prefix_m, data, depth, options|xml_log_option_open|xml_log_option_close|xml_log_option_children);
3583 
3584  } else if(is_set(p->flags, xpf_dirty)) {
3585  char *spaces = calloc(80, 1);
3586  int s_count = 0, s_max = 80;
3587  char *prefix_del = NULL;
3588  char *prefix_moved = NULL;
3589  const char *flags = prefix;
3590 
3591  insert_prefix(options, &spaces, &s_count, &s_max, depth);
3592  prefix_del = strdup(prefix);
3593  prefix_del[0] = '-';
3594  prefix_del[1] = '-';
3595  prefix_moved = strdup(prefix);
3596  prefix_moved[1] = '~';
3597 
3598  if(is_set(p->flags, xpf_moved)) {
3599  flags = prefix_moved;
3600  } else {
3601  flags = prefix;
3602  }
3603 
3604  __xml_log_element(log_level, file, function, line,
3605  flags, data, depth, options|xml_log_option_open);
3606 
3607  for (pIter = crm_first_attr(data); pIter != NULL; pIter = pIter->next) {
3608  const char *aname = (const char*)pIter->name;
3609 
3610  p = pIter->_private;
3611  if(is_set(p->flags, xpf_deleted)) {
3612  const char *value = crm_element_value(data, aname);
3613  flags = prefix_del;
3614  do_crm_log_alias(log_level, file, function, line,
3615  "%s %s @%s=%s", flags, spaces, aname, value);
3616 
3617  } else if(is_set(p->flags, xpf_dirty)) {
3618  const char *value = crm_element_value(data, aname);
3619 
3620  if(is_set(p->flags, xpf_created)) {
3621  flags = prefix_m;
3622 
3623  } else if(is_set(p->flags, xpf_modified)) {
3624  flags = prefix;
3625 
3626  } else if(is_set(p->flags, xpf_moved)) {
3627  flags = prefix_moved;
3628 
3629  } else {
3630  flags = prefix;
3631  }
3632  do_crm_log_alias(log_level, file, function, line,
3633  "%s %s @%s=%s", flags, spaces, aname, value);
3634  }
3635  }
3636  free(prefix_moved);
3637  free(prefix_del);
3638  free(spaces);
3639 
3640  for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
3641  __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
3642  }
3643 
3644  __xml_log_element(log_level, file, function, line,
3645  prefix, data, depth, options|xml_log_option_close);
3646 
3647  } else {
3648  for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
3649  __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
3650  }
3651  }
3652 
3653  free(prefix_m);
3654 
3655 }
3656 
3657 void
3658 log_data_element(int log_level, const char *file, const char *function, int line,
3659  const char *prefix, xmlNode * data, int depth, int options)
3660 {
3661  xmlNode *a_child = NULL;
3662 
3663  char *prefix_m = NULL;
3664 
3665  if (prefix == NULL) {
3666  prefix = "";
3667  }
3668 
3669  /* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
3670  if (data == NULL) {
3671  do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
3672  "No data to dump as XML");
3673  return;
3674  }
3675 
3676  if(is_set(options, xml_log_option_dirty_add) || is_set(options, xml_log_option_dirty_add)) {
3677  __xml_log_change_element(log_level, file, function, line, prefix, data, depth, options);
3678  return;
3679  }
3680 
3681  if (is_set(options, xml_log_option_formatted)) {
3682  if (is_set(options, xml_log_option_diff_plus)
3683  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
3684  options |= xml_log_option_diff_all;
3685  prefix_m = strdup(prefix);
3686  prefix_m[1] = '+';
3687  prefix = prefix_m;
3688 
3689  } else if (is_set(options, xml_log_option_diff_minus)
3690  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
3691  options |= xml_log_option_diff_all;
3692  prefix_m = strdup(prefix);
3693  prefix_m[1] = '-';
3694  prefix = prefix_m;
3695  }
3696  }
3697 
3698  if (is_set(options, xml_log_option_diff_short)
3699  && is_not_set(options, xml_log_option_diff_all)) {
3700  /* Still searching for the actual change */
3701  for (a_child = __xml_first_child(data); a_child != NULL; a_child = __xml_next(a_child)) {
3702  log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
3703  }
3704  } else {
3705  __xml_log_element(log_level, file, function, line, prefix, data, depth,
3707  }
3708  free(prefix_m);
3709 }
3710 
3711 static void
3712 dump_filtered_xml(xmlNode * data, int options, char **buffer, int *offset, int *max)
3713 {
3714  int lpc;
3715  xmlAttrPtr xIter = NULL;
3716  static int filter_len = DIMOF(filter);
3717 
3718  for (lpc = 0; options && lpc < filter_len; lpc++) {
3719  filter[lpc].found = FALSE;
3720  }
3721 
3722  for (xIter = crm_first_attr(data); xIter != NULL; xIter = xIter->next) {
3723  bool skip = FALSE;
3724  const char *p_name = (const char *)xIter->name;
3725 
3726  for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
3727  if (filter[lpc].found == FALSE && strcmp(p_name, filter[lpc].string) == 0) {
3728  filter[lpc].found = TRUE;
3729  skip = TRUE;
3730  break;
3731  }
3732  }
3733 
3734  if (skip == FALSE) {
3735  dump_xml_attr(xIter, options, buffer, offset, max);
3736  }
3737  }
3738 }
3739 
3740 static void
3741 dump_xml_element(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3742 {
3743  const char *name = NULL;
3744 
3745  CRM_ASSERT(max != NULL);
3746  CRM_ASSERT(offset != NULL);
3747  CRM_ASSERT(buffer != NULL);
3748 
3749  if (data == NULL) {
3750  crm_trace("Nothing to dump");
3751  return;
3752  }
3753 
3754  if (*buffer == NULL) {
3755  *offset = 0;
3756  *max = 0;
3757  }
3758 
3759  name = crm_element_name(data);
3760  CRM_ASSERT(name != NULL);
3761 
3762  insert_prefix(options, buffer, offset, max, depth);
3763  buffer_print(*buffer, *max, *offset, "<%s", name);
3764 
3765  if (options & xml_log_option_filtered) {
3766  dump_filtered_xml(data, options, buffer, offset, max);
3767 
3768  } else {
3769  xmlAttrPtr xIter = NULL;
3770 
3771  for (xIter = crm_first_attr(data); xIter != NULL; xIter = xIter->next) {
3772  dump_xml_attr(xIter, options, buffer, offset, max);
3773  }
3774  }
3775 
3776  if (data->children == NULL) {
3777  buffer_print(*buffer, *max, *offset, "/>");
3778 
3779  } else {
3780  buffer_print(*buffer, *max, *offset, ">");
3781  }
3782 
3783  if (options & xml_log_option_formatted) {
3784  buffer_print(*buffer, *max, *offset, "\n");
3785  }
3786 
3787  if (data->children) {
3788  xmlNode *xChild = NULL;
3789  for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
3790  crm_xml_dump(xChild, options, buffer, offset, max, depth + 1);
3791  }
3792 
3793  insert_prefix(options, buffer, offset, max, depth);
3794  buffer_print(*buffer, *max, *offset, "</%s>", name);
3795 
3796  if (options & xml_log_option_formatted) {
3797  buffer_print(*buffer, *max, *offset, "\n");
3798  }
3799  }
3800 }
3801 
3802 static void
3803 dump_xml_text(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3804 {
3805  CRM_ASSERT(max != NULL);
3806  CRM_ASSERT(offset != NULL);
3807  CRM_ASSERT(buffer != NULL);
3808 
3809  if (data == NULL) {
3810  crm_trace("Nothing to dump");
3811  return;
3812  }
3813 
3814  if (*buffer == NULL) {
3815  *offset = 0;
3816  *max = 0;
3817  }
3818 
3819  insert_prefix(options, buffer, offset, max, depth);
3820 
3821  buffer_print(*buffer, *max, *offset, "%s", data->content);
3822 
3823  if (options & xml_log_option_formatted) {
3824  buffer_print(*buffer, *max, *offset, "\n");
3825  }
3826 }
3827 
3828 
3829 static void
3830 dump_xml_comment(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3831 {
3832  CRM_ASSERT(max != NULL);
3833  CRM_ASSERT(offset != NULL);
3834  CRM_ASSERT(buffer != NULL);
3835 
3836  if (data == NULL) {
3837  crm_trace("Nothing to dump");
3838  return;
3839  }
3840 
3841  if (*buffer == NULL) {
3842  *offset = 0;
3843  *max = 0;
3844  }
3845 
3846  insert_prefix(options, buffer, offset, max, depth);
3847 
3848  buffer_print(*buffer, *max, *offset, "<!--");
3849  buffer_print(*buffer, *max, *offset, "%s", data->content);
3850  buffer_print(*buffer, *max, *offset, "-->");
3851 
3852  if (options & xml_log_option_formatted) {
3853  buffer_print(*buffer, *max, *offset, "\n");
3854  }
3855 }
3856 
3857 void
3858 crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3859 {
3860  if(data == NULL) {
3861  *offset = 0;
3862  *max = 0;
3863  return;
3864  }
3865 #if 0
3866  if (is_not_set(options, xml_log_option_filtered)) {
3867  /* Turning this code on also changes the PE tests for some reason
3868  * (not just newlines). Figure out why before considering to
3869  * enable this permanently.
3870  *
3871  * It exists to help debug slowness in xmlNodeDump() and
3872  * potentially if we ever want to go back to it.
3873  *
3874  * In theory its a good idea (reuse) but our custom version does
3875  * better for the filtered case and avoids the final strdup() for
3876  * everything
3877  */
3878 
3879  time_t now, next;
3880  xmlDoc *doc = NULL;
3881  xmlBuffer *xml_buffer = NULL;
3882 
3883  *buffer = NULL;
3884  doc = getDocPtr(data);
3885  /* doc will only be NULL if data is */
3886  CRM_CHECK(doc != NULL, return);
3887 
3888  now = time(NULL);
3889  xml_buffer = xmlBufferCreate();
3890  CRM_ASSERT(xml_buffer != NULL);
3891 
3892  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
3893  * realloc()s and it can take upwards of 18 seconds (yes, seconds)
3894  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
3895  * less than 1 second.
3896  *
3897  * We could also use xmlBufferCreateSize() to start with a
3898  * sane-ish initial size and avoid the first few doubles.
3899  */
3900  xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_DOUBLEIT);
3901 
3902  *max = xmlNodeDump(xml_buffer, doc, data, 0, (options & xml_log_option_formatted));
3903  if (*max > 0) {
3904  *buffer = strdup((char *)xml_buffer->content);
3905  }
3906 
3907  next = time(NULL);
3908  if ((now + 1) < next) {
3909  crm_log_xml_trace(data, "Long time");
3910  crm_err("xmlNodeDump() -> %dbytes took %ds", *max, next - now);
3911  }
3912 
3913  xmlBufferFree(xml_buffer);
3914  return;
3915  }
3916 #endif
3917 
3918  switch(data->type) {
3919  case XML_ELEMENT_NODE:
3920  /* Handle below */
3921  dump_xml_element(data, options, buffer, offset, max, depth);
3922  break;
3923  case XML_TEXT_NODE:
3924  /* if option xml_log_option_text is enabled, then dump XML_TEXT_NODE */
3925  if (options & xml_log_option_text) {
3926  dump_xml_text(data, options, buffer, offset, max, depth);
3927  }
3928  return;
3929  case XML_COMMENT_NODE:
3930  dump_xml_comment(data, options, buffer, offset, max, depth);
3931  break;
3932  default:
3933  crm_warn("Unhandled type: %d", data->type);
3934  return;
3935 
3936  /*
3937  XML_ATTRIBUTE_NODE = 2
3938  XML_CDATA_SECTION_NODE = 4
3939  XML_ENTITY_REF_NODE = 5
3940  XML_ENTITY_NODE = 6
3941  XML_PI_NODE = 7
3942  XML_DOCUMENT_NODE = 9
3943  XML_DOCUMENT_TYPE_NODE = 10
3944  XML_DOCUMENT_FRAG_NODE = 11
3945  XML_NOTATION_NODE = 12
3946  XML_HTML_DOCUMENT_NODE = 13
3947  XML_DTD_NODE = 14
3948  XML_ELEMENT_DECL = 15
3949  XML_ATTRIBUTE_DECL = 16
3950  XML_ENTITY_DECL = 17
3951  XML_NAMESPACE_DECL = 18
3952  XML_XINCLUDE_START = 19
3953  XML_XINCLUDE_END = 20
3954  XML_DOCB_DOCUMENT_NODE = 21
3955  */
3956  }
3957 
3958 }
3959 
3960 void
3961 crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
3962 {
3963  buffer_print(*buffer, *max, *offset, "%c", c);
3964 }
3965 
3966 char *
3967 dump_xml_formatted_with_text(xmlNode * an_xml_node)
3968 {
3969  char *buffer = NULL;
3970  int offset = 0, max = 0;
3971 
3972  crm_xml_dump(an_xml_node, xml_log_option_formatted|xml_log_option_text, &buffer, &offset, &max, 0);
3973  return buffer;
3974 }
3975 
3976 char *
3977 dump_xml_formatted(xmlNode * an_xml_node)
3978 {
3979  char *buffer = NULL;
3980  int offset = 0, max = 0;
3981 
3982  crm_xml_dump(an_xml_node, xml_log_option_formatted, &buffer, &offset, &max, 0);
3983  return buffer;
3984 }
3985 
3986 char *
3987 dump_xml_unformatted(xmlNode * an_xml_node)
3988 {
3989  char *buffer = NULL;
3990  int offset = 0, max = 0;
3991 
3992  crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
3993  return buffer;
3994 }
3995 
3996 gboolean
3997 xml_has_children(const xmlNode * xml_root)
3998 {
3999  if (xml_root != NULL && xml_root->children != NULL) {
4000  return TRUE;
4001  }
4002  return FALSE;
4003 }
4004 
4005 int
4006 crm_element_value_int(xmlNode * data, const char *name, int *dest)
4007 {
4008  const char *value = crm_element_value(data, name);
4009 
4010  CRM_CHECK(dest != NULL, return -1);
4011  if (value) {
4012  *dest = crm_int_helper(value, NULL);
4013  return 0;
4014  }
4015  return -1;
4016 }
4017 
4018 int
4019 crm_element_value_const_int(const xmlNode * data, const char *name, int *dest)
4020 {
4021  return crm_element_value_int((xmlNode *) data, name, dest);
4022 }
4023 
4024 const char *
4025 crm_element_value_const(const xmlNode * data, const char *name)
4026 {
4027  return crm_element_value((xmlNode *) data, name);
4028 }
4029 
4030 char *
4031 crm_element_value_copy(xmlNode * data, const char *name)
4032 {
4033  char *value_copy = NULL;
4034  const char *value = crm_element_value(data, name);
4035 
4036  if (value != NULL) {
4037  value_copy = strdup(value);
4038  }
4039  return value_copy;
4040 }
4041 
4042 void
4043 xml_remove_prop(xmlNode * obj, const char *name)
4044 {
4045  if(__xml_acl_check(obj, NULL, xpf_acl_write) == FALSE) {
4046  crm_trace("Cannot remove %s from %s", name, obj->name);
4047 
4048  } else if(TRACKING_CHANGES(obj)) {
4049  /* Leave in place (marked for removal) until after the diff is calculated */
4050  xml_private_t *p = NULL;
4051  xmlAttr *attr = xmlHasProp(obj, (const xmlChar *)name);
4052 
4053  p = attr->_private;
4054  set_parent_flag(obj, xpf_dirty);
4055  p->flags |= xpf_deleted;
4056  /* crm_trace("Setting flag %x due to %s[@id=%s].%s", xpf_dirty, obj->name, ID(obj), name); */
4057 
4058  } else {
4059  xmlUnsetProp(obj, (const xmlChar *)name);
4060  }
4061 }
4062 
4063 void
4064 purge_diff_markers(xmlNode * a_node)
4065 {
4066  xmlNode *child = NULL;
4067 
4068  CRM_CHECK(a_node != NULL, return);
4069 
4071  for (child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
4072  purge_diff_markers(child);
4073  }
4074 }
4075 
4076 void
4077 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
4078 {
4079  char *f = NULL;
4080 
4081  if (filename == NULL) {
4082  char *uuid = crm_generate_uuid();
4083 
4084  f = crm_strdup_printf("/tmp/%s", uuid);
4085  filename = f;
4086  free(uuid);
4087  }
4088 
4089  crm_info("Saving %s to %s", desc, filename);
4090  write_xml_file(xml, filename, FALSE);
4091  free(f);
4092 }
4093 
4094 gboolean
4095 apply_xml_diff(xmlNode * old, xmlNode * diff, xmlNode ** new)
4096 {
4097  gboolean result = TRUE;
4098  int root_nodes_seen = 0;
4099  static struct qb_log_callsite *digest_cs = NULL;
4100  const char *digest = crm_element_value(diff, XML_ATTR_DIGEST);
4101  const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION);
4102 
4103  xmlNode *child_diff = NULL;
4104  xmlNode *added = find_xml_node(diff, "diff-added", FALSE);
4105  xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE);
4106 
4107  CRM_CHECK(new != NULL, return FALSE);
4108  if (digest_cs == NULL) {
4109  digest_cs =
4110  qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
4112  }
4113 
4114  crm_trace("Substraction Phase");
4115  for (child_diff = __xml_first_child(removed); child_diff != NULL;
4116  child_diff = __xml_next(child_diff)) {
4117  CRM_CHECK(root_nodes_seen == 0, result = FALSE);
4118  if (root_nodes_seen == 0) {
4119  *new = subtract_xml_object(NULL, old, child_diff, FALSE, NULL, NULL);
4120  }
4121  root_nodes_seen++;
4122  }
4123 
4124  if (root_nodes_seen == 0) {
4125  *new = copy_xml(old);
4126 
4127  } else if (root_nodes_seen > 1) {
4128  crm_err("(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
4129  result = FALSE;
4130  }
4131 
4132  root_nodes_seen = 0;
4133  crm_trace("Addition Phase");
4134  if (result) {
4135  xmlNode *child_diff = NULL;
4136 
4137  for (child_diff = __xml_first_child(added); child_diff != NULL;
4138  child_diff = __xml_next(child_diff)) {
4139  CRM_CHECK(root_nodes_seen == 0, result = FALSE);
4140  if (root_nodes_seen == 0) {
4141  add_xml_object(NULL, *new, child_diff, TRUE);
4142  }
4143  root_nodes_seen++;
4144  }
4145  }
4146 
4147  if (root_nodes_seen > 1) {
4148  crm_err("(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
4149  result = FALSE;
4150 
4151  } else if (result && digest) {
4152  char *new_digest = NULL;
4153 
4154  purge_diff_markers(*new); /* Purge now so the diff is ok */
4155  new_digest = calculate_xml_versioned_digest(*new, FALSE, TRUE, version);
4156  if (safe_str_neq(new_digest, digest)) {
4157  crm_info("Digest mis-match: expected %s, calculated %s", digest, new_digest);
4158  result = FALSE;
4159 
4160  crm_trace("%p %0.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
4161  if (digest_cs && digest_cs->targets) {
4162  save_xml_to_file(old, "diff:original", NULL);
4163  save_xml_to_file(diff, "diff:input", NULL);
4164  save_xml_to_file(*new, "diff:new", NULL);
4165  }
4166 
4167  } else {
4168  crm_trace("Digest matched: expected %s, calculated %s", digest, new_digest);
4169  }
4170  free(new_digest);
4171 
4172  } else if (result) {
4173  purge_diff_markers(*new); /* Purge now so the diff is ok */
4174  }
4175 
4176  return result;
4177 }
4178 
4179 static void
4180 __xml_diff_object(xmlNode * old, xmlNode * new)
4181 {
4182  xmlNode *cIter = NULL;
4183  xmlAttr *pIter = NULL;
4184 
4185  CRM_CHECK(new != NULL, return);
4186  if(old == NULL) {
4187  crm_node_created(new);
4188  __xml_acl_post_process(new); /* Check creation is allowed */
4189  return;
4190 
4191  } else {
4192  xml_private_t *p = new->_private;
4193 
4194  if(p->flags & xpf_processed) {
4195  /* Avoid re-comparing nodes */
4196  return;
4197  }
4198  p->flags |= xpf_processed;
4199  }
4200 
4201  for (pIter = crm_first_attr(new); pIter != NULL; pIter = pIter->next) {
4202  xml_private_t *p = pIter->_private;
4203 
4204  /* Assume everything was just created and take it from there */
4205  p->flags |= xpf_created;
4206  }
4207 
4208  for (pIter = crm_first_attr(old); pIter != NULL; ) {
4209  xmlAttr *prop = pIter;
4210  xml_private_t *p = NULL;
4211  const char *name = (const char *)pIter->name;
4212  const char *old_value = crm_element_value(old, name);
4213  xmlAttr *exists = xmlHasProp(new, pIter->name);
4214 
4215  pIter = pIter->next;
4216  if(exists == NULL) {
4217  p = new->doc->_private;
4218 
4219  /* Prevent the dirty flag being set recursively upwards */
4220  clear_bit(p->flags, xpf_tracking);
4221  exists = xmlSetProp(new, (const xmlChar *)name, (const xmlChar *)old_value);
4222  set_bit(p->flags, xpf_tracking);
4223 
4224  p = exists->_private;
4225  p->flags = 0;
4226 
4227  crm_trace("Lost %s@%s=%s", old->name, name, old_value);
4228  xml_remove_prop(new, name);
4229 
4230  } else {
4231  int p_new = __xml_offset((xmlNode*)exists);
4232  int p_old = __xml_offset((xmlNode*)prop);
4233  const char *value = crm_element_value(new, name);
4234 
4235  p = exists->_private;
4236  p->flags = (p->flags & ~xpf_created);
4237 
4238  if(strcmp(value, old_value) != 0) {
4239  /* Restore the original value, so we can call crm_xml_add() whcih checks ACLs */
4240  char *vcopy = crm_element_value_copy(new, name);
4241 
4242  crm_trace("Modified %s@%s %s->%s", old->name, name, old_value, vcopy);
4243  xmlSetProp(new, prop->name, (const xmlChar *)old_value);
4244  crm_xml_add(new, name, vcopy);
4245  free(vcopy);
4246 
4247  } else if(p_old != p_new) {
4248  crm_info("Moved %s@%s (%d -> %d)", old->name, name, p_old, p_new);
4249  __xml_node_dirty(new);
4250  p->flags |= xpf_dirty|xpf_moved;
4251 
4252  if(p_old > p_new) {
4253  p = prop->_private;
4254  p->flags |= xpf_skip;
4255 
4256  } else {
4257  p = exists->_private;
4258  p->flags |= xpf_skip;
4259  }
4260  }
4261  }
4262  }
4263 
4264  for (pIter = crm_first_attr(new); pIter != NULL; ) {
4265  xmlAttr *prop = pIter;
4266  xml_private_t *p = pIter->_private;
4267 
4268  pIter = pIter->next;
4269  if(is_set(p->flags, xpf_created)) {
4270  char *name = strdup((const char *)prop->name);
4271  char *value = crm_element_value_copy(new, name);
4272 
4273  crm_trace("Created %s@%s=%s", new->name, name, value);
4274  /* Remove plus create won't work as it will modify the relative attribute ordering */
4275  if(__xml_acl_check(new, name, xpf_acl_write)) {
4276  crm_attr_dirty(prop);
4277  } else {
4278  xmlUnsetProp(new, prop->name); /* Remove - change not allowed */
4279  }
4280 
4281  free(value);
4282  free(name);
4283  }
4284  }
4285 
4286  for (cIter = __xml_first_child(old); cIter != NULL; ) {
4287  xmlNode *old_child = cIter;
4288  xmlNode *new_child = find_entity(new, crm_element_name(cIter), ID(cIter));
4289 
4290  cIter = __xml_next(cIter);
4291  if(new_child) {
4292  __xml_diff_object(old_child, new_child);
4293 
4294  } else {
4295  xml_private_t *p = old_child->_private;
4296 
4297  /* Create then free (which will check the acls if necessary) */
4298  xmlNode *candidate = add_node_copy(new, old_child);
4299  xmlNode *top = xmlDocGetRootElement(candidate->doc);
4300 
4301  __xml_node_clean(candidate);
4302  __xml_acl_apply(top); /* Make sure any ACLs are applied to 'candidate' */
4303  free_xml(candidate);
4304 
4305  if(NULL == find_entity(new, crm_element_name(old_child), ID(old_child))) {
4306  p->flags |= xpf_skip;
4307  }
4308  }
4309  }
4310 
4311  for (cIter = __xml_first_child(new); cIter != NULL; ) {
4312  xmlNode *new_child = cIter;
4313  xmlNode *old_child = find_entity(old, crm_element_name(cIter), ID(cIter));
4314 
4315  cIter = __xml_next(cIter);
4316  if(old_child == NULL) {
4317  xml_private_t *p = new_child->_private;
4318  p->flags |= xpf_skip;
4319  __xml_diff_object(old_child, new_child);
4320 
4321  } else {
4322  /* Check for movement, we already checked for differences */
4323  int p_new = __xml_offset(new_child);
4324  int p_old = __xml_offset(old_child);
4325  xml_private_t *p = new_child->_private;
4326 
4327  if(p_old != p_new) {
4328  crm_info("%s.%s moved from %d to %d - %d",
4329  new_child->name, ID(new_child), p_old, p_new);
4330  __xml_node_dirty(new);
4331  p->flags |= xpf_moved;
4332 
4333  if(p_old > p_new) {
4334  p = old_child->_private;
4335  p->flags |= xpf_skip;
4336 
4337  } else {
4338  p = new_child->_private;
4339  p->flags |= xpf_skip;
4340  }
4341  }
4342  }
4343  }
4344 }
4345 
4346 void
4347 xml_calculate_changes(xmlNode * old, xmlNode * new)
4348 {
4349  CRM_CHECK(safe_str_eq(crm_element_name(old), crm_element_name(new)), return);
4350  CRM_CHECK(safe_str_eq(ID(old), ID(new)), return);
4351 
4352  if(xml_tracking_changes(new) == FALSE) {
4353  xml_track_changes(new, NULL, NULL, FALSE);
4354  }
4355 
4356  __xml_diff_object(old, new);
4357 }
4358 
4359 xmlNode *
4360 diff_xml_object(xmlNode * old, xmlNode * new, gboolean suppress)
4361 {
4362  xmlNode *tmp1 = NULL;
4363  xmlNode *diff = create_xml_node(NULL, "diff");
4364  xmlNode *removed = create_xml_node(diff, "diff-removed");
4365  xmlNode *added = create_xml_node(diff, "diff-added");
4366 
4368 
4369  tmp1 = subtract_xml_object(removed, old, new, FALSE, NULL, "removed:top");
4370  if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
4371  free_xml(tmp1);
4372  }
4373 
4374  tmp1 = subtract_xml_object(added, new, old, TRUE, NULL, "added:top");
4375  if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
4376  free_xml(tmp1);
4377  }
4378 
4379  if (added->children == NULL && removed->children == NULL) {
4380  free_xml(diff);
4381  diff = NULL;
4382  }
4383 
4384  return diff;
4385 }
4386 
4387 gboolean
4388 can_prune_leaf(xmlNode * xml_node)
4389 {
4390  xmlNode *cIter = NULL;
4391  xmlAttrPtr pIter = NULL;
4392  gboolean can_prune = TRUE;
4393  const char *name = crm_element_name(xml_node);
4394 
4398  || safe_str_eq(name, XML_ACL_TAG_ROLE_REFv1)) {
4399  return FALSE;
4400  }
4401 
4402  for (pIter = crm_first_attr(xml_node); pIter != NULL; pIter = pIter->next) {
4403  const char *p_name = (const char *)pIter->name;
4404 
4405  if (strcmp(p_name, XML_ATTR_ID) == 0) {
4406  continue;
4407  }
4408  can_prune = FALSE;
4409  }
4410 
4411  cIter = __xml_first_child(xml_node);
4412  while (cIter) {
4413  xmlNode *child = cIter;
4414 
4415  cIter = __xml_next(cIter);
4416  if (can_prune_leaf(child)) {
4417  free_xml(child);
4418  } else {
4419  can_prune = FALSE;
4420  }
4421  }
4422  return can_prune;
4423 }
4424 
4425 void
4426 diff_filter_context(int context, int upper_bound, int lower_bound,
4427  xmlNode * xml_node, xmlNode * parent)
4428 {
4429  xmlNode *us = NULL;
4430  xmlNode *child = NULL;
4431  xmlAttrPtr pIter = NULL;
4432  xmlNode *new_parent = parent;
4433  const char *name = crm_element_name(xml_node);
4434 
4435  CRM_CHECK(xml_node != NULL && name != NULL, return);
4436 
4437  us = create_xml_node(parent, name);
4438  for (pIter = crm_first_attr(xml_node); pIter != NULL; pIter = pIter->next) {
4439  const char *p_name = (const char *)pIter->name;
4440  const char *p_value = crm_attr_value(pIter);
4441 
4442  lower_bound = context;
4443  crm_xml_add(us, p_name, p_value);
4444  }
4445 
4446  if (lower_bound >= 0 || upper_bound >= 0) {
4447  crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
4448  new_parent = us;
4449 
4450  } else {
4451  upper_bound = in_upper_context(0, context, xml_node);
4452  if (upper_bound >= 0) {
4453  crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
4454  new_parent = us;
4455  } else {
4456  free_xml(us);
4457  us = NULL;
4458  }
4459  }
4460 
4461  for (child = __xml_first_child(us); child != NULL; child = __xml_next(child)) {
4462  diff_filter_context(context, upper_bound - 1, lower_bound - 1, child, new_parent);
4463  }
4464 }
4465 
4466 int
4467 in_upper_context(int depth, int context, xmlNode * xml_node)
4468 {
4469  if (context == 0) {
4470  return 0;
4471  }
4472 
4473  if (xml_node->properties) {
4474  return depth;
4475 
4476  } else if (depth < context) {
4477  xmlNode *child = NULL;
4478 
4479  for (child = __xml_first_child(xml_node); child != NULL; child = __xml_next(child)) {
4480  if (in_upper_context(depth + 1, context, child)) {
4481  return depth;
4482  }
4483  }
4484  }
4485  return 0;
4486 }
4487 
4488 static xmlNode *
4489 find_xml_comment(xmlNode * root, xmlNode * search_comment)
4490 {
4491  xmlNode *a_child = NULL;
4492 
4493  CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
4494 
4495  for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
4496  if (a_child->type != XML_COMMENT_NODE) {
4497  continue;
4498  }
4499  if (safe_str_eq((const char *)a_child->content, (const char *)search_comment->content)) {
4500  return a_child;
4501  }
4502  }
4503 
4504  return NULL;
4505 }
4506 
4507 static xmlNode *
4508 subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right,
4509  gboolean * changed)
4510 {
4511  CRM_CHECK(left != NULL, return NULL);
4512  CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL);
4513 
4514  if (right == NULL
4515  || safe_str_neq((const char *)left->content, (const char *)right->content)) {
4516  xmlNode *deleted = NULL;
4517 
4518  deleted = add_node_copy(parent, left);
4519  *changed = TRUE;
4520 
4521  return deleted;
4522  }
4523 
4524  return NULL;
4525 }
4526 
4527 xmlNode *
4528 subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right,
4529  gboolean full, gboolean * changed, const char *marker)
4530 {
4531  gboolean dummy = FALSE;
4532  gboolean skip = FALSE;
4533  xmlNode *diff = NULL;
4534  xmlNode *right_child = NULL;
4535  xmlNode *left_child = NULL;
4536  xmlAttrPtr xIter = NULL;
4537 
4538  const char *id = NULL;
4539  const char *name = NULL;
4540  const char *value = NULL;
4541  const char *right_val = NULL;
4542 
4543  int lpc = 0;
4544  static int filter_len = DIMOF(filter);
4545 
4546  if (changed == NULL) {
4547  changed = &dummy;
4548  }
4549 
4550  if (left == NULL) {
4551  return NULL;
4552  }
4553 
4554  if (left->type == XML_COMMENT_NODE) {
4555  return subtract_xml_comment(parent, left, right, changed);
4556  }
4557 
4558  id = ID(left);
4559  if (right == NULL) {
4560  xmlNode *deleted = NULL;
4561 
4562  crm_trace("Processing <%s id=%s> (complete copy)", crm_element_name(left), id);
4563  deleted = add_node_copy(parent, left);
4564  crm_xml_add(deleted, XML_DIFF_MARKER, marker);
4565 
4566  *changed = TRUE;
4567  return deleted;
4568  }
4569 
4570  name = crm_element_name(left);
4571  CRM_CHECK(name != NULL, return NULL);
4572  CRM_CHECK(safe_str_eq(crm_element_name(left), crm_element_name(right)), return NULL);
4573 
4574  /* check for XML_DIFF_MARKER in a child */
4575  value = crm_element_value(right, XML_DIFF_MARKER);
4576  if (value != NULL && strcmp(value, "removed:top") == 0) {
4577  crm_trace("We are the root of the deletion: %s.id=%s", name, id);
4578  *changed = TRUE;
4579  return NULL;
4580  }
4581 
4582  /* Avoiding creating the full heirarchy would save even more work here */
4583  diff = create_xml_node(parent, name);
4584 
4585  /* Reset filter */
4586  for (lpc = 0; lpc < filter_len; lpc++) {
4587  filter[lpc].found = FALSE;
4588  }
4589 
4590  /* changes to child objects */
4591  for (left_child = __xml_first_child(left); left_child != NULL;
4592  left_child = __xml_next(left_child)) {
4593  gboolean child_changed = FALSE;
4594 
4595  if (left_child->type == XML_COMMENT_NODE) {
4596  right_child = find_xml_comment(right, left_child);
4597 
4598  } else {
4599  right_child = find_entity(right, crm_element_name(left_child), ID(left_child));
4600  }
4601 
4602  subtract_xml_object(diff, left_child, right_child, full, &child_changed, marker);
4603  if (child_changed) {
4604  *changed = TRUE;
4605  }
4606  }
4607 
4608  if (*changed == FALSE) {
4609  /* Nothing to do */
4610 
4611  } else if (full) {
4612  xmlAttrPtr pIter = NULL;
4613 
4614  for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
4615  const char *p_name = (const char *)pIter->name;
4616  const char *p_value = crm_attr_value(pIter);
4617 
4618  xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
4619  }
4620 
4621  /* We already have everything we need... */
4622  goto done;
4623 
4624  } else if (id) {
4625  xmlSetProp(diff, (const xmlChar *)XML_ATTR_ID, (const xmlChar *)id);
4626  }
4627 
4628  /* changes to name/value pairs */
4629  for (xIter = crm_first_attr(left); xIter != NULL; xIter = xIter->next) {
4630  const char *prop_name = (const char *)xIter->name;
4631  xmlAttrPtr right_attr = NULL;
4632  xml_private_t *p = NULL;
4633 
4634  if (strcmp(prop_name, XML_ATTR_ID) == 0) {
4635  continue;
4636  }
4637 
4638  skip = FALSE;
4639  for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
4640  if (filter[lpc].found == FALSE && strcmp(prop_name, filter[lpc].string) == 0) {
4641  filter[lpc].found = TRUE;
4642  skip = TRUE;
4643  break;
4644  }
4645  }
4646 
4647  if (skip) {
4648  continue;
4649  }
4650 
4651  right_attr = xmlHasProp(right, (const xmlChar *)prop_name);
4652  if (right_attr) {
4653  p = right_attr->_private;
4654  }
4655 
4656  right_val = crm_element_value(right, prop_name);
4657  if (right_val == NULL || (p && is_set(p->flags, xpf_deleted))) {
4658  /* new */
4659  *changed = TRUE;
4660  if (full) {
4661  xmlAttrPtr pIter = NULL;
4662 
4663  for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
4664  const char *p_name = (const char *)pIter->name;
4665  const char *p_value = crm_attr_value(pIter);
4666 
4667  xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
4668  }
4669  break;
4670 
4671  } else {
4672  const char *left_value = crm_element_value(left, prop_name);
4673 
4674  xmlSetProp(diff, (const xmlChar *)prop_name, (const xmlChar *)value);
4675  crm_xml_add(diff, prop_name, left_value);
4676  }
4677 
4678  } else {
4679  /* Only now do we need the left value */
4680  const char *left_value = crm_element_value(left, prop_name);
4681 
4682  if (strcmp(left_value, right_val) == 0) {
4683  /* unchanged */
4684 
4685  } else {
4686  *changed = TRUE;
4687  if (full) {
4688  xmlAttrPtr pIter = NULL;
4689 
4690  crm_trace("Changes detected to %s in <%s id=%s>", prop_name,
4691  crm_element_name(left), id);
4692  for (pIter = crm_first_attr(left); pIter != NULL; pIter = pIter->next) {
4693  const char *p_name = (const char *)pIter->name;
4694  const char *p_value = crm_attr_value(pIter);
4695 
4696  xmlSetProp(diff, (const xmlChar *)p_name, (const xmlChar *)p_value);
4697  }
4698  break;
4699 
4700  } else {
4701  crm_trace("Changes detected to %s (%s -> %s) in <%s id=%s>",
4702  prop_name, left_value, right_val, crm_element_name(left), id);
4703  crm_xml_add(diff, prop_name, left_value);
4704  }
4705  }
4706  }
4707  }
4708 
4709  if (*changed == FALSE) {
4710  free_xml(diff);
4711  return NULL;
4712 
4713  } else if (full == FALSE && id) {
4714  crm_xml_add(diff, XML_ATTR_ID, id);
4715  }
4716  done:
4717  return diff;
4718 }
4719 
4720 static int
4721 add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update)
4722 {
4723  CRM_CHECK(update != NULL, return 0);
4724  CRM_CHECK(update->type == XML_COMMENT_NODE, return 0);
4725 
4726  if (target == NULL) {
4727  target = find_xml_comment(parent, update);
4728  }
4729 
4730  if (target == NULL) {
4731  add_node_copy(parent, update);
4732 
4733  /* We won't reach here currently */
4734  } else if (safe_str_neq((const char *)target->content, (const char *)update->content)) {
4735  xmlFree(target->content);
4736  target->content = xmlStrdup(update->content);
4737  }
4738 
4739  return 0;
4740 }
4741 
4742 int
4743 add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff)
4744 {
4745  xmlNode *a_child = NULL;
4746  const char *object_id = NULL;
4747  const char *object_name = NULL;
4748 
4749 #if XML_PARSE_DEBUG
4750  crm_log_xml_trace("update:", update);
4751  crm_log_xml_trace("target:", target);
4752 #endif
4753 
4754  CRM_CHECK(update != NULL, return 0);
4755 
4756  if (update->type == XML_COMMENT_NODE) {
4757  return add_xml_comment(parent, target, update);
4758  }
4759 
4760  object_name = crm_element_name(update);
4761  object_id = ID(update);
4762 
4763  CRM_CHECK(object_name != NULL, return 0);
4764 
4765  if (target == NULL && object_id == NULL) {
4766  /* placeholder object */
4767  target = find_xml_node(parent, object_name, FALSE);
4768 
4769  } else if (target == NULL) {
4770  target = find_entity(parent, object_name, object_id);
4771  }
4772 
4773  if (target == NULL) {
4774  target = create_xml_node(parent, object_name);
4775  CRM_CHECK(target != NULL, return 0);
4776 #if XML_PARSER_DEBUG
4777  crm_trace("Added <%s%s%s/>", crm_str(object_name),
4778  object_id ? " id=" : "", object_id ? object_id : "");
4779 
4780  } else {
4781  crm_trace("Found node <%s%s%s/> to update",
4782  crm_str(object_name), object_id ? " id=" : "", object_id ? object_id : "");
4783 #endif
4784  }
4785 
4786  CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(update)), return 0);
4787 
4788  if (as_diff == FALSE) {
4789  /* So that expand_plus_plus() gets called */
4790  copy_in_properties(target, update);
4791 
4792  } else {
4793  /* No need for expand_plus_plus(), just raw speed */
4794  xmlAttrPtr pIter = NULL;
4795 
4796  for (pIter = crm_first_attr(update); pIter != NULL; pIter = pIter->next) {
4797  const char *p_name = (const char *)pIter->name;
4798  const char *p_value = crm_attr_value(pIter);
4799 
4800  /* Remove it first so the ordering of the update is preserved */
4801  xmlUnsetProp(target, (const xmlChar *)p_name);
4802  xmlSetProp(target, (const xmlChar *)p_name, (const xmlChar *)p_value);
4803  }
4804  }
4805 
4806  for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
4807 #if XML_PARSER_DEBUG
4808  crm_trace("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child));
4809 #endif
4810  add_xml_object(target, NULL, a_child, as_diff);
4811  }
4812 
4813 #if XML_PARSER_DEBUG
4814  crm_trace("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id));
4815 #endif
4816  return 0;
4817 }
4818 
4819 gboolean
4820 update_xml_child(xmlNode * child, xmlNode * to_update)
4821 {
4822  gboolean can_update = TRUE;
4823  xmlNode *child_of_child = NULL;
4824 
4825  CRM_CHECK(child != NULL, return FALSE);
4826  CRM_CHECK(to_update != NULL, return FALSE);
4827 
4828  if (safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
4829  can_update = FALSE;
4830 
4831  } else if (safe_str_neq(ID(to_update), ID(child))) {
4832  can_update = FALSE;
4833 
4834  } else if (can_update) {
4835 #if XML_PARSER_DEBUG
4836  crm_log_xml_trace(child, "Update match found...");
4837 #endif
4838  add_xml_object(NULL, child, to_update, FALSE);
4839  }
4840 
4841  for (child_of_child = __xml_first_child(child); child_of_child != NULL;
4842  child_of_child = __xml_next(child_of_child)) {
4843  /* only update the first one */
4844  if (can_update) {
4845  break;
4846  }
4847  can_update = update_xml_child(child_of_child, to_update);
4848  }
4849 
4850  return can_update;
4851 }
4852 
4853 int
4854 find_xml_children(xmlNode ** children, xmlNode * root,
4855  const char *tag, const char *field, const char *value, gboolean search_matches)
4856 {
4857  int match_found = 0;
4858 
4859  CRM_CHECK(root != NULL, return FALSE);
4860  CRM_CHECK(children != NULL, return FALSE);
4861 
4862  if (tag != NULL && safe_str_neq(tag, crm_element_name(root))) {
4863 
4864  } else if (value != NULL && safe_str_neq(value, crm_element_value(root, field))) {
4865 
4866  } else {
4867  if (*children == NULL) {
4868  *children = create_xml_node(NULL, __FUNCTION__);
4869  }
4870  add_node_copy(*children, root);
4871  match_found = 1;
4872  }
4873 
4874  if (search_matches || match_found == 0) {
4875  xmlNode *child = NULL;
4876 
4877  for (child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
4878  match_found += find_xml_children(children, child, tag, field, value, search_matches);
4879  }
4880  }
4881 
4882  return match_found;
4883 }
4884 
4885 gboolean
4886 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
4887 {
4888  gboolean can_delete = FALSE;
4889  xmlNode *child_of_child = NULL;
4890 
4891  const char *up_id = NULL;
4892  const char *child_id = NULL;
4893  const char *right_val = NULL;
4894 
4895  CRM_CHECK(child != NULL, return FALSE);
4896  CRM_CHECK(update != NULL, return FALSE);
4897 
4898  up_id = ID(update);
4899  child_id = ID(child);
4900 
4901  if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
4902  can_delete = TRUE;
4903  }
4904  if (safe_str_neq(crm_element_name(update), crm_element_name(child))) {
4905  can_delete = FALSE;
4906  }
4907  if (can_delete && delete_only) {
4908  xmlAttrPtr pIter = NULL;
4909 
4910  for (pIter = crm_first_attr(update); pIter != NULL; pIter = pIter->next) {
4911  const char *p_name = (const char *)pIter->name;
4912  const char *p_value = crm_attr_value(pIter);
4913 
4914  right_val = crm_element_value(child, p_name);
4915  if (safe_str_neq(p_value, right_val)) {
4916  can_delete = FALSE;
4917  }
4918  }
4919  }
4920 
4921  if (can_delete && parent != NULL) {
4922  crm_log_xml_trace(child, "Delete match found...");
4923  if (delete_only || update == NULL) {
4924  free_xml(child);
4925 
4926  } else {
4927  xmlNode *tmp = copy_xml(update);
4928  xmlDoc *doc = tmp->doc;
4929  xmlNode *old = NULL;
4930 
4931  xml_accept_changes(tmp);
4932  old = xmlReplaceNode(child, tmp);
4933 
4934  if(xml_tracking_changes(tmp)) {
4935  /* Replaced sections may have included relevant ACLs */
4936  __xml_acl_apply(tmp);
4937  }
4938 
4939  xml_calculate_changes(old, tmp);
4940  xmlDocSetRootElement(doc, old);
4941  free_xml(old);
4942  }
4943  child = NULL;
4944  return TRUE;
4945 
4946  } else if (can_delete) {
4947  crm_log_xml_debug(child, "Cannot delete the search root");
4948  can_delete = FALSE;
4949  }
4950 
4951  child_of_child = __xml_first_child(child);
4952  while (child_of_child) {
4953  xmlNode *next = __xml_next(child_of_child);
4954 
4955  can_delete = replace_xml_child(child, child_of_child, update, delete_only);
4956 
4957  /* only delete the first one */
4958  if (can_delete) {
4959  child_of_child = NULL;
4960  } else {
4961  child_of_child = next;
4962  }
4963  }
4964 
4965  return can_delete;
4966 }
4967 
4968 void
4969 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
4970 {
4971  const char *name = key;
4972  const char *s_value = value;
4973 
4974  xmlNode *xml_node = user_data;
4975  xmlNode *xml_child = create_xml_node(xml_node, XML_CIB_TAG_NVPAIR);
4976 
4977  crm_xml_add(xml_child, XML_ATTR_ID, name);
4978  crm_xml_add(xml_child, XML_NVPAIR_ATTR_NAME, name);
4979  crm_xml_add(xml_child, XML_NVPAIR_ATTR_VALUE, s_value);
4980 
4981  crm_trace("dumped: name=%s value=%s", name, s_value);
4982 }
4983 
4984 void
4985 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
4986 {
4987  const char *name = key;
4988  const char *s_value = value;
4989 
4990  xmlNode *xml_node = user_data;
4991 
4992  if (isdigit(name[0])) {
4993  xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
4994 
4995  crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
4996  crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
4997 
4998  } else if (crm_element_value(xml_node, name) == NULL) {
4999  crm_xml_add(xml_node, name, s_value);
5000  crm_trace("dumped: %s=%s", name, s_value);
5001 
5002  } else {
5003  crm_trace("duplicate: %s=%s", name, s_value);
5004  }
5005 }
5006 
5007 void
5008 hash2field(gpointer key, gpointer value, gpointer user_data)
5009 {
5010  const char *name = key;
5011  const char *s_value = value;
5012 
5013  xmlNode *xml_node = user_data;
5014 
5015  if (crm_element_value(xml_node, name) == NULL) {
5016  crm_xml_add(xml_node, name, s_value);
5017 
5018  } else {
5019  crm_trace("duplicate: %s=%s", name, s_value);
5020  }
5021 }
5022 
5023 void
5024 hash2metafield(gpointer key, gpointer value, gpointer user_data)
5025 {
5026  char *crm_name = NULL;
5027 
5028  if (key == NULL || value == NULL) {
5029  return;
5030  } else if (((char *)key)[0] == '#') {
5031  return;
5032  } else if (strstr(key, ":")) {
5033  return;
5034  }
5035 
5036  crm_name = crm_meta_name(key);
5037  hash2field(crm_name, value, user_data);
5038  free(crm_name);
5039 }
5040 
5041 GHashTable *
5042 xml2list(xmlNode * parent)
5043 {
5044  xmlNode *child = NULL;
5045  xmlAttrPtr pIter = NULL;
5046  xmlNode *nvpair_list = NULL;
5047  GHashTable *nvpair_hash = g_hash_table_new_full(crm_str_hash, g_str_equal,
5049 
5050  CRM_CHECK(parent != NULL, return nvpair_hash);
5051 
5052  nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
5053  if (nvpair_list == NULL) {
5054  crm_trace("No attributes in %s", crm_element_name(parent));
5055  crm_log_xml_trace(parent, "No attributes for resource op");
5056  }
5057 
5058  crm_log_xml_trace(nvpair_list, "Unpacking");
5059 
5060  for (pIter = crm_first_attr(nvpair_list); pIter != NULL; pIter = pIter->next) {
5061  const char *p_name = (const char *)pIter->name;
5062  const char *p_value = crm_attr_value(pIter);
5063 
5064  crm_trace("Added %s=%s", p_name, p_value);
5065 
5066  g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
5067  }
5068 
5069  for (child = __xml_first_child(nvpair_list); child != NULL; child = __xml_next(child)) {
5070  if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
5071  const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
5072  const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
5073 
5074  crm_trace("Added %s=%s", key, value);
5075  if (key != NULL && value != NULL) {
5076  g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
5077  }
5078  }
5079  }
5080 
5081  return nvpair_hash;
5082 }
5083 
5084 typedef struct name_value_s {
5085  const char *name;
5086  const void *value;
5087 } name_value_t;
5088 
5089 static gint
5090 sort_pairs(gconstpointer a, gconstpointer b)
5091 {
5092  int rc = 0;
5093  const name_value_t *pair_a = a;
5094  const name_value_t *pair_b = b;
5095 
5096  CRM_ASSERT(a != NULL);
5097  CRM_ASSERT(pair_a->name != NULL);
5098 
5099  CRM_ASSERT(b != NULL);
5100  CRM_ASSERT(pair_b->name != NULL);
5101 
5102  rc = strcmp(pair_a->name, pair_b->name);
5103  if (rc < 0) {
5104  return -1;
5105  } else if (rc > 0) {
5106  return 1;
5107  }
5108  return 0;
5109 }
5110 
5111 static void
5112 dump_pair(gpointer data, gpointer user_data)
5113 {
5114  name_value_t *pair = data;
5115  xmlNode *parent = user_data;
5116 
5117  crm_xml_add(parent, pair->name, pair->value);
5118 }
5119 
5120 xmlNode *
5121 sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive)
5122 {
5123  xmlNode *child = NULL;
5124  GListPtr sorted = NULL;
5125  GListPtr unsorted = NULL;
5126  name_value_t *pair = NULL;
5127  xmlNode *result = NULL;
5128  const char *name = NULL;
5129  xmlAttrPtr pIter = NULL;
5130 
5131  CRM_CHECK(input != NULL, return NULL);
5132 
5133  name = crm_element_name(input);
5134  CRM_CHECK(name != NULL, return NULL);
5135 
5136  result = create_xml_node(parent, name);
5137 
5138  for (pIter = crm_first_attr(input); pIter != NULL; pIter = pIter->next) {
5139  const char *p_name = (const char *)pIter->name;
5140  const char *p_value = crm_attr_value(pIter);
5141 
5142  pair = calloc(1, sizeof(name_value_t));
5143  pair->name = p_name;
5144  pair->value = p_value;
5145  unsorted = g_list_prepend(unsorted, pair);
5146  pair = NULL;
5147  }
5148 
5149  sorted = g_list_sort(unsorted, sort_pairs);
5150  g_list_foreach(sorted, dump_pair, result);
5151  g_list_free_full(sorted, free);
5152 
5153  for (child = __xml_first_child(input); child != NULL; child = __xml_next(child)) {
5154  if (recursive) {
5155  sorted_xml(child, result, recursive);
5156  } else {
5157  add_node_copy(result, child);
5158  }
5159  }
5160 
5161  return result;
5162 }
5163 
5164 static gboolean
5165 validate_with_dtd(xmlDocPtr doc, gboolean to_logs, const char *dtd_file)
5166 {
5167  gboolean valid = TRUE;
5168 
5169  xmlDtdPtr dtd = NULL;
5170  xmlValidCtxtPtr cvp = NULL;
5171 
5172  CRM_CHECK(doc != NULL, return FALSE);
5173  CRM_CHECK(dtd_file != NULL, return FALSE);
5174 
5175  dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file);
5176  if(dtd == NULL) {
5177  crm_err("Could not locate/parse DTD: %s", dtd_file);
5178  return TRUE;
5179  }
5180 
5181  cvp = xmlNewValidCtxt();
5182  if(cvp) {
5183  if (to_logs) {
5184  cvp->userData = (void *)LOG_ERR;
5185  cvp->error = (xmlValidityErrorFunc) xml_log;
5186  cvp->warning = (xmlValidityWarningFunc) xml_log;
5187  } else {
5188  cvp->userData = (void *)stderr;
5189  cvp->error = (xmlValidityErrorFunc) fprintf;
5190  cvp->warning = (xmlValidityWarningFunc) fprintf;
5191  }
5192 
5193  if (!xmlValidateDtd(cvp, doc, dtd)) {
5194  valid = FALSE;
5195  }
5196  xmlFreeValidCtxt(cvp);
5197 
5198  } else {
5199  crm_err("Internal error: No valid context");
5200  }
5201 
5202  xmlFreeDtd(dtd);
5203  return valid;
5204 }
5205 
5206 xmlNode *
5207 first_named_child(xmlNode * parent, const char *name)
5208 {
5209  xmlNode *match = NULL;
5210 
5211  for (match = __xml_first_child(parent); match != NULL; match = __xml_next(match)) {
5212  /*
5213  * name == NULL gives first child regardless of name; this is
5214  * semantically incorrect in this funciton, but may be necessary
5215  * due to prior use of xml_child_iter_filter
5216  */
5217  if (name == NULL || strcmp((const char *)match->name, name) == 0) {
5218  return match;
5219  }
5220  }
5221  return NULL;
5222 }
5223 
5224 #if 0
5225 static void
5226 relaxng_invalid_stderr(void *userData, xmlErrorPtr error)
5227 {
5228  /*
5229  Structure xmlError
5230  struct _xmlError {
5231  int domain : What part of the library raised this er
5232  int code : The error code, e.g. an xmlParserError
5233  char * message : human-readable informative error messag
5234  xmlErrorLevel level : how consequent is the error
5235  char * file : the filename
5236  int line : the line number if available
5237  char * str1 : extra string information
5238  char * str2 : extra string information
5239  char * str3 : extra string information
5240  int int1 : extra number information
5241  int int2 : column number of the error or 0 if N/A
5242  void * ctxt : the parser context if available
5243  void * node : the node in the tree
5244  }
5245  */
5246  crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
5247 }
5248 #endif
5249 
5250 static gboolean
5251 validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
5252  relaxng_ctx_cache_t ** cached_ctx)
5253 {
5254  int rc = 0;
5255  gboolean valid = TRUE;
5256  relaxng_ctx_cache_t *ctx = NULL;
5257 
5258  CRM_CHECK(doc != NULL, return FALSE);
5259  CRM_CHECK(relaxng_file != NULL, return FALSE);
5260 
5261  if (cached_ctx && *cached_ctx) {
5262  ctx = *cached_ctx;
5263 
5264  } else {
5265  crm_info("Creating RNG parser context");
5266  ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
5267 
5268  xmlLoadExtDtdDefaultValue = 1;
5269  ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
5270  CRM_CHECK(ctx->parser != NULL, goto cleanup);
5271 
5272  if (to_logs) {
5273  xmlRelaxNGSetParserErrors(ctx->parser,
5274  (xmlRelaxNGValidityErrorFunc) xml_log,
5275  (xmlRelaxNGValidityWarningFunc) xml_log,
5276  GUINT_TO_POINTER(LOG_ERR));
5277  } else {
5278  xmlRelaxNGSetParserErrors(ctx->parser,
5279  (xmlRelaxNGValidityErrorFunc) fprintf,
5280  (xmlRelaxNGValidityWarningFunc) fprintf, stderr);
5281  }
5282 
5283  ctx->rng = xmlRelaxNGParse(ctx->parser);
5284  CRM_CHECK(ctx->rng != NULL, crm_err("Could not find/parse %s", relaxng_file);
5285  goto cleanup);
5286 
5287  ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
5288  CRM_CHECK(ctx->valid != NULL, goto cleanup);
5289 
5290  if (to_logs) {
5291  xmlRelaxNGSetValidErrors(ctx->valid,
5292  (xmlRelaxNGValidityErrorFunc) xml_log,
5293  (xmlRelaxNGValidityWarningFunc) xml_log,
5294  GUINT_TO_POINTER(LOG_ERR));
5295  } else {
5296  xmlRelaxNGSetValidErrors(ctx->valid,
5297  (xmlRelaxNGValidityErrorFunc) fprintf,
5298  (xmlRelaxNGValidityWarningFunc) fprintf, stderr);
5299  }
5300  }
5301 
5302  /* xmlRelaxNGSetValidStructuredErrors( */
5303  /* valid, relaxng_invalid_stderr, valid); */
5304 
5305  xmlLineNumbersDefault(1);
5306  rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
5307  if (rc > 0) {
5308  valid = FALSE;
5309 
5310  } else if (rc < 0) {
5311  crm_err("Internal libxml error during validation\n");
5312  }
5313 
5314  cleanup:
5315 
5316  if (cached_ctx) {
5317  *cached_ctx = ctx;
5318 
5319  } else {
5320  if (ctx->parser != NULL) {
5321  xmlRelaxNGFreeParserCtxt(ctx->parser);
5322  }
5323  if (ctx->valid != NULL) {
5324  xmlRelaxNGFreeValidCtxt(ctx->valid);
5325  }
5326  if (ctx->rng != NULL) {
5327  xmlRelaxNGFree(ctx->rng);
5328  }
5329  free(ctx);
5330  }
5331 
5332  return valid;
5333 }
5334 
5335 void
5337 {
5338  static bool init = TRUE;
5339 
5340  if(init) {
5341  init = FALSE;
5342  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
5343  * realloc_safe()s and it can take upwards of 18 seconds (yes, seconds)
5344  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
5345  * less than 1 second.
5346  */
5347  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
5348 
5349  /* Populate and free the _private field when nodes are created and destroyed */
5350  xmlDeregisterNodeDefault(pcmkDeregisterNode);
5351  xmlRegisterNodeDefault(pcmkRegisterNode);
5352 
5353  __xml_build_schema_list();
5354  }
5355 }
5356 
5357 void
5359 {
5360  int lpc = 0;
5361  relaxng_ctx_cache_t *ctx = NULL;
5362 
5363  crm_info("Cleaning up memory from libxml2");
5364  for (; lpc < xml_schema_max; lpc++) {
5365 
5366  switch (known_schemas[lpc].type) {
5367  case 0:
5368  /* None */
5369  break;
5370  case 1:
5371  /* DTD - Not cached */
5372  break;
5373  case 2:
5374  /* RNG - Cached */
5375  ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
5376  if (ctx == NULL) {
5377  break;
5378  }
5379  if (ctx->parser != NULL) {
5380  xmlRelaxNGFreeParserCtxt(ctx->parser);
5381  }
5382  if (ctx->valid != NULL) {
5383  xmlRelaxNGFreeValidCtxt(ctx->valid);
5384  }
5385  if (ctx->rng != NULL) {
5386  xmlRelaxNGFree(ctx->rng);
5387  }
5388  free(ctx);
5389  known_schemas[lpc].cache = NULL;
5390  break;
5391  default:
5392  break;
5393  }
5394  free(known_schemas[lpc].name);
5395  free(known_schemas[lpc].location);
5396  free(known_schemas[lpc].transform);
5397  }
5398  free(known_schemas);
5399  xsltCleanupGlobals();
5400  xmlCleanupParser();
5401 }
5402 
5403 static gboolean
5404 validate_with(xmlNode * xml, int method, gboolean to_logs)
5405 {
5406  xmlDocPtr doc = NULL;
5407  gboolean valid = FALSE;
5408  int type = 0;
5409  char *file = NULL;
5410 
5411  if(method < 0) {
5412  return FALSE;
5413  }
5414 
5415  type = known_schemas[method].type;
5416  if(type == 0) {
5417  return TRUE;
5418  }
5419 
5420  CRM_CHECK(xml != NULL, return FALSE);
5421  doc = getDocPtr(xml);
5422  file = get_schema_path(known_schemas[method].name, known_schemas[method].location);
5423 
5424  crm_trace("Validating with: %s (type=%d)", crm_str(file), type);
5425  switch (type) {
5426  case 1:
5427  valid = validate_with_dtd(doc, to_logs, file);
5428  break;
5429  case 2:
5430  valid =
5431  validate_with_relaxng(doc, to_logs, file,
5432  (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
5433  break;
5434  default:
5435  crm_err("Unknown validator type: %d", type);
5436  break;
5437  }
5438 
5439  free(file);
5440  return valid;
5441 }
5442 
5443 #include <stdio.h>
5444 static void
5445 dump_file(const char *filename)
5446 {
5447 
5448  FILE *fp = NULL;
5449  int ch, line = 0;
5450 
5451  CRM_CHECK(filename != NULL, return);
5452 
5453  fp = fopen(filename, "r");
5454  CRM_CHECK(fp != NULL, return);
5455 
5456  fprintf(stderr, "%4d ", ++line);
5457  do {
5458  ch = getc(fp);
5459  if (ch == EOF) {
5460  putc('\n', stderr);
5461  break;
5462  } else if (ch == '\n') {
5463  fprintf(stderr, "\n%4d ", ++line);
5464  } else {
5465  putc(ch, stderr);
5466  }
5467  } while (1);
5468 
5469  fclose(fp);
5470 }
5471 
5472 gboolean
5473 validate_xml_verbose(xmlNode * xml_blob)
5474 {
5475  int fd = 0;
5476  xmlDoc *doc = NULL;
5477  xmlNode *xml = NULL;
5478  gboolean rc = FALSE;
5479  char *filename = strdup(CRM_STATE_DIR "/cib-invalid.XXXXXX");
5480 
5481  umask(S_IWGRP | S_IWOTH | S_IROTH);
5482  fd = mkstemp(filename);
5483  write_xml_fd(xml_blob, filename, fd, FALSE);
5484 
5485  dump_file(filename);
5486 
5487  doc = xmlParseFile(filename);
5488  xml = xmlDocGetRootElement(doc);
5489  rc = validate_xml(xml, NULL, FALSE);
5490  free_xml(xml);
5491 
5492  unlink(filename);
5493  free(filename);
5494 
5495  return rc;
5496 }
5497 
5498 gboolean
5499 validate_xml(xmlNode * xml_blob, const char *validation, gboolean to_logs)
5500 {
5501  int version = 0;
5502 
5503  if (validation == NULL) {
5504  validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
5505  }
5506 
5507  if (validation == NULL) {
5508  int lpc = 0;
5509  bool valid = FALSE;
5510 
5511  validation = crm_element_value(xml_blob, "ignore-dtd");
5512  if (crm_is_true(validation)) {
5513  /* Legacy compatibilty */
5514  crm_xml_add(xml_blob, XML_ATTR_VALIDATION, "none");
5515  return TRUE;
5516  }
5517 
5518  /* Work it out */
5519  for (lpc = 0; lpc < xml_schema_max; lpc++) {
5520  if(validate_with(xml_blob, lpc, FALSE)) {
5521  valid = TRUE;
5522  crm_xml_add(xml_blob, XML_ATTR_VALIDATION, known_schemas[lpc].name);
5523  crm_info("XML validated against %s", known_schemas[lpc].name);
5524  if(known_schemas[lpc].after_transform == 0) {
5525  break;
5526  }
5527  }
5528  }
5529 
5530  return valid;
5531  }
5532 
5533  version = get_schema_version(validation);
5534  if (strcmp(validation, "none") == 0) {
5535  return TRUE;
5536 
5537  } else if(version < xml_schema_max) {
5538  return validate_with(xml_blob, version, to_logs);
5539 
5540  }
5541 
5542  crm_err("Unknown validator: %s", validation);
5543  return FALSE;
5544 }
5545 
5546 #if HAVE_LIBXSLT
5547 static xmlNode *
5548 apply_transformation(xmlNode * xml, const char *transform)
5549 {
5550  char *xform = NULL;
5551  xmlNode *out = NULL;
5552  xmlDocPtr res = NULL;
5553  xmlDocPtr doc = NULL;
5554  xsltStylesheet *xslt = NULL;
5555 
5556  CRM_CHECK(xml != NULL, return FALSE);
5557  doc = getDocPtr(xml);
5558  xform = get_schema_path(NULL, transform);
5559 
5560  xmlLoadExtDtdDefaultValue = 1;
5561  xmlSubstituteEntitiesDefault(1);
5562 
5563  xslt = xsltParseStylesheetFile((const xmlChar *)xform);
5564  CRM_CHECK(xslt != NULL, goto cleanup);
5565 
5566  res = xsltApplyStylesheet(xslt, doc, NULL);
5567  CRM_CHECK(res != NULL, goto cleanup);
5568 
5569  out = xmlDocGetRootElement(res);
5570 
5571  cleanup:
5572  if (xslt) {
5573  xsltFreeStylesheet(xslt);
5574  }
5575 
5576  free(xform);
5577 
5578  return out;
5579 }
5580 #endif
5581 
5582 const char *
5583 get_schema_name(int version)
5584 {
5585  if (version < 0 || version >= xml_schema_max) {
5586  return "unknown";
5587  }
5588  return known_schemas[version].name;
5589 }
5590 
5591 int
5592 get_schema_version(const char *name)
5593 {
5594  int lpc = 0;
5595 
5596  if(name == NULL) {
5597  name = "none";
5598  }
5599  for (; lpc < xml_schema_max; lpc++) {
5600  if (safe_str_eq(name, known_schemas[lpc].name)) {
5601  return lpc;
5602  }
5603  }
5604  return -1;
5605 }
5606 
5607 /* set which validation to use */
5608 #include <crm/cib.h>
5609 int
5610 update_validation(xmlNode ** xml_blob, int *best, int max, gboolean transform, gboolean to_logs)
5611 {
5612  xmlNode *xml = NULL;
5613  char *value = NULL;
5614  int max_stable_schemas = xml_latest_schema_index();
5615  int lpc = 0, match = -1, rc = pcmk_ok;
5616 
5617  CRM_CHECK(best != NULL, return -EINVAL);
5618  CRM_CHECK(xml_blob != NULL, return -EINVAL);
5619  CRM_CHECK(*xml_blob != NULL, return -EINVAL);
5620 
5621  *best = 0;
5622  xml = *xml_blob;
5624 
5625  if (value != NULL) {
5626  match = get_schema_version(value);
5627 
5628  lpc = match;
5629  if (lpc >= 0 && transform == FALSE) {
5630  lpc++;
5631 
5632  } else if (lpc < 0) {
5633  crm_debug("Unknown validation type");
5634  lpc = 0;
5635  }
5636  }
5637 
5638  if (match >= max_stable_schemas) {
5639  /* nothing to do */
5640  free(value);
5641  *best = match;
5642  return pcmk_ok;
5643  }
5644 
5645  while(lpc <= max_stable_schemas) {
5646  gboolean valid = TRUE;
5647 
5648  crm_debug("Testing '%s' validation (%d of %d)",
5649  known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
5650  lpc, max_stable_schemas);
5651  valid = validate_with(xml, lpc, to_logs);
5652 
5653  if (valid) {
5654  *best = lpc;
5655  } else {
5656  crm_trace("%s validation failed", known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>");
5657  }
5658 
5659  if (valid && transform) {
5660  xmlNode *upgrade = NULL;
5661  int next = known_schemas[lpc].after_transform;
5662 
5663  if (next < 0) {
5664  crm_trace("Stopping at %s", known_schemas[lpc].name);
5665  break;
5666 
5667  } else if (max > 0 && lpc == max) {
5668  crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
5669  known_schemas[lpc].name, lpc, next, max);
5670  break;
5671 
5672  } else if (max > 0 && next > max) {
5673  crm_debug("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
5674  known_schemas[lpc].name, lpc, next, max);
5675  break;
5676 
5677  } else if (known_schemas[lpc].transform == NULL) {
5678  crm_debug("%s-style configuration is also valid for %s",
5679  known_schemas[lpc].name, known_schemas[next].name);
5680 
5681  if (validate_with(xml, next, to_logs)) {
5682  crm_debug("Configuration valid for schema: %s", known_schemas[next].name);
5683  lpc = next;
5684  *best = next;
5685  rc = pcmk_ok;
5686 
5687  } else {
5688  crm_info("Configuration not valid for schema: %s", known_schemas[next].name);
5689  }
5690 
5691  } else {
5692  crm_debug("Upgrading %s-style configuration to %s with %s",
5693  known_schemas[lpc].name, known_schemas[next].name,
5694  known_schemas[lpc].transform ? known_schemas[lpc].transform : "no-op");
5695 
5696 #if HAVE_LIBXSLT
5697  upgrade = apply_transformation(xml, known_schemas[lpc].transform);
5698 #endif
5699  if (upgrade == NULL) {
5700  crm_err("Transformation %s failed", known_schemas[lpc].transform);
5702 
5703  } else if (validate_with(upgrade, next, to_logs)) {
5704  crm_info("Transformation %s successful", known_schemas[lpc].transform);
5705  lpc = next;
5706  *best = next;
5707  free_xml(xml);
5708  xml = upgrade;
5709  rc = pcmk_ok;
5710 
5711  } else {
5712  crm_err("Transformation %s did not produce a valid configuration",
5713  known_schemas[lpc].transform);
5714  crm_log_xml_info(upgrade, "transform:bad");
5715  free_xml(upgrade);
5717  }
5718  }
5719  }
5720  }
5721 
5722  if (*best > match) {
5723  crm_info("%s the configuration from %s to %s",
5724  transform?"Transformed":"Upgraded",
5725  value ? value : "<none>", known_schemas[*best].name);
5726  crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
5727  }
5728 
5729  *xml_blob = xml;
5730  free(value);
5731  return rc;
5732 }
5733 
5734 gboolean
5735 cli_config_update(xmlNode ** xml, int *best_version, gboolean to_logs)
5736 {
5737  gboolean rc = TRUE;
5738  const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
5739 
5740  int version = get_schema_version(value);
5741  int min_version = xml_minimum_schema_index();
5742 
5743  if (version < min_version) {
5744  xmlNode *converted = NULL;
5745 
5746  converted = copy_xml(*xml);
5747  update_validation(&converted, &version, 0, TRUE, to_logs);
5748 
5749  value = crm_element_value(converted, XML_ATTR_VALIDATION);
5750  if (version < min_version) {
5751  if (to_logs) {
5752  crm_config_err("Your current configuration could only be upgraded to %s... "
5753  "the minimum requirement is %s.\n", crm_str(value),
5754  get_schema_name(min_version));
5755  } else {
5756  fprintf(stderr, "Your current configuration could only be upgraded to %s... "
5757  "the minimum requirement is %s.\n",
5758  crm_str(value), get_schema_name(min_version));
5759  }
5760 
5761  free_xml(converted);
5762  converted = NULL;
5763  rc = FALSE;
5764 
5765  } else {
5766  free_xml(*xml);
5767  *xml = converted;
5768 
5769  if (version < xml_latest_schema_index()) {
5770  crm_config_warn("Your configuration was internally updated to %s... "
5771  "which is acceptable but not the most recent",
5772  get_schema_name(version));
5773 
5774  } else if (to_logs) {
5775  crm_info("Your configuration was internally updated to the latest version (%s)",
5776  get_schema_name(version));
5777  }
5778  }
5779 
5780  } else if (version >= get_schema_version("none")) {
5781  if (to_logs) {
5782  crm_config_warn("Configuration validation is currently disabled."
5783  " It is highly encouraged and prevents many common cluster issues.");
5784 
5785  } else {
5786  fprintf(stderr, "Configuration validation is currently disabled."
5787  " It is highly encouraged and prevents many common cluster issues.\n");
5788  }
5789  }
5790 
5791  if (best_version) {
5792  *best_version = version;
5793  }
5794 
5795  return rc;
5796 }
5797 
5798 xmlNode *
5799 expand_idref(xmlNode * input, xmlNode * top)
5800 {
5801  const char *tag = NULL;
5802  const char *ref = NULL;
5803  xmlNode *result = input;
5804  char *xpath_string = NULL;
5805 
5806  if (result == NULL) {
5807  return NULL;
5808 
5809  } else if (top == NULL) {
5810  top = input;
5811  }
5812 
5813  tag = crm_element_name(result);
5814  ref = crm_element_value(result, XML_ATTR_IDREF);
5815 
5816  if (ref != NULL) {
5817  int xpath_max = 512, offset = 0;
5818 
5819  xpath_string = calloc(1, xpath_max);
5820 
5821  offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s[@id='%s']", tag, ref);
5822  CRM_LOG_ASSERT(offset > 0);
5823 
5824  result = get_xpath_object(xpath_string, top, LOG_ERR);
5825  if (result == NULL) {
5826  char *nodePath = (char *)xmlGetNodePath(top);
5827 
5828  crm_err("No match for %s found in %s: Invalid configuration", xpath_string,
5829  crm_str(nodePath));
5830  free(nodePath);
5831  }
5832  }
5833 
5834  free(xpath_string);
5835  return result;
5836 }
5837 
5838 const char *
5839 crm_element_value(xmlNode * data, const char *name)
5840 {
5841  xmlAttr *attr = NULL;
5842 
5843  if (data == NULL) {
5844  crm_err("Couldn't find %s in NULL", name ? name : "<null>");
5845  CRM_LOG_ASSERT(data != NULL);
5846  return NULL;
5847 
5848  } else if (name == NULL) {
5849  crm_err("Couldn't find NULL in %s", crm_element_name(data));
5850  return NULL;
5851  }
5852 
5853  attr = xmlHasProp(data, (const xmlChar *)name);
5854  if (attr == NULL || attr->children == NULL) {
5855  return NULL;
5856  }
5857  return (const char *)attr->children->content;
5858 }
5859 
#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:398
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:368
#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:391
xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
Definition: xml.c:4360
gboolean safe_str_neq(const char *a, const char *b)
Definition: utils.c:668
#define INFINITY
Definition: crm.h:77
char * crm_generate_uuid(void)
Definition: utils.c:2314
int add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update, gboolean as_diff)
Definition: xml.c:4743
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:3658
#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:2695
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:269
#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:225
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Definition: xml.c:3304
void fix_plus_plus_recursive(xmlNode *target)
Definition: xml.c:2515
void crm_xml_init(void)
Definition: xml.c:5336
#define XML_TAG_ATTRS
Definition: msg_xml.h:175
#define buffer_print(buffer, max, offset, fmt, args...)
Definition: xml.c:196
#define XML_ACL_TAG_WRITE
Definition: msg_xml.h:366
#define XML_ATTR_IDREF
Definition: msg_xml.h:101
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:333
void purge_diff_markers(xmlNode *a_node)
Definition: xml.c:4064
xmlNode * stdin2xml(void)
Definition: xml.c:3022
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:587
#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:2363
#define clear_bit(word, bit)
Definition: crm_internal.h:199
unsigned int crm_trace_nonlog
Definition: logging.c:48
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:170
char * strerror(int errnum)
void hash2field(gpointer key, gpointer value, gpointer user_data)
Definition: xml.c:5008
xmlNode * first_named_child(xmlNode *parent, const char *name)
Definition: xml.c:5207
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:225
char * xml_get_path(xmlNode *xml)
Definition: xml.c:2836
char * crm_meta_name(const char *field)
Definition: utils.c:1435
gboolean validate_xml_verbose(xmlNode *xml_blob)
Definition: xml.c:5473
#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:4025
xmlNode * filename2xml(const char *filename)
Definition: xml.c:3141
#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:4854
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Definition: xml.c:2533
#define XML_ACL_ATTR_REFv1
Definition: msg_xml.h:369
#define CHUNK_SIZE
Definition: xml.c:185
#define XML_ACL_TAG_ROLE
Definition: msg_xml.h:360
Definition: xml.c:104
#define XML_ACL_ATTR_KIND
Definition: msg_xml.h:364
#define pcmk_err_diff_failed
Definition: error.h:50
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition: xml.c:5499
#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:198
xmlNode * copy_xml(xmlNode *src)
Definition: xml.c:2894
bool pcmk_acl_required(const char *user)
Definition: utils.c:2105
#define XML_DIFF_OP
Definition: msg_xml.h:399
void diff_filter_context(int context, int upper_bound, int lower_bound, xmlNode *xml_node, xmlNode *parent)
Definition: xml.c:4426
uint64_t flags
Definition: remote.c:121
#define crm_debug(fmt, args...)
Definition: logging.h:253
#define XML_DIFF_ATTR
Definition: msg_xml.h:397
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
Definition: xml.c:3332
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:5799
#define XML_DIFF_VERSION
Definition: msg_xml.h:392
#define XML_ATTR_ID
Definition: msg_xml.h:100
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:2793
#define XML_ACL_ATTR_XPATH
Definition: msg_xml.h:372
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:361
void free_xml(xmlNode *child)
Definition: xml.c:2848
#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:5583
#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:4077
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:358
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Definition: xml.c:4985
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:3858
#define crm_log_xml_warn(xml, text)
Definition: logging.h:258
#define XML_ACL_TAG_DENY
Definition: msg_xml.h:367
#define XML_ATTR_VALIDATION
Definition: msg_xml.h:85
#define XML_ACL_ATTR_ATTRIBUTE
Definition: msg_xml.h:373
#define XML_DIFF_POSITION
Definition: msg_xml.h:401
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:180
void crm_xml_cleanup(void)
Definition: xml.c:5358
xmlNode * add_node_copy(xmlNode *parent, xmlNode *src_node)
Definition: xml.c:2610
int crm_element_value_int(xmlNode *data, const char *name, int *dest)
Definition: xml.c:4006
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:2594
char * dump_xml_formatted(xmlNode *an_xml_node)
Definition: xml.c:3977
void xml_calculate_changes(xmlNode *old, xmlNode *new)
Definition: xml.c:4347
const char * crm_xml_add_last_written(xmlNode *xml_node)
Definition: xml.c:3213
#define EOS
Definition: crm.h:40
xmlNode * string2xml(const char *input)
Definition: xml.c:2957
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:3961
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Definition: xml.c:2783
int crm_element_value_const_int(const xmlNode *data, const char *name, int *dest)
Definition: xml.c:4019
#define XML_ACL_ATTR_TAGv1
Definition: msg_xml.h:371
char * dump_xml_formatted_with_text(xmlNode *an_xml_node)
Definition: xml.c:3967
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:393
#define crm_config_warn(fmt...)
Definition: crm_internal.h:270
xmlNode * get_message_xml(xmlNode *msg, const char *field)
Definition: xml.c:3324
char * dump_xml_unformatted(xmlNode *an_xml_node)
Definition: xml.c:3987
#define XML_TAG_CIB
Definition: msg_xml.h:80
#define XML_DIFF_CHANGE
Definition: msg_xml.h:395
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Definition: xml.c:3314
#define XML_DIFF_PATH
Definition: msg_xml.h:400
#define XML_DIFF_VTARGET
Definition: msg_xml.h:394
void copy_in_properties(xmlNode *target, xmlNode *src)
Definition: xml.c:2492
#define crm_log_xml_err(xml, text)
Definition: logging.h:257
char * crm_element_value_copy(xmlNode *data, const char *name)
Definition: xml.c:4031
#define XML_DIFF_LIST
Definition: msg_xml.h:396
int update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform, gboolean to_logs)
Definition: xml.c:5610
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:226
void strip_text_nodes(xmlNode *xml)
Definition: xml.c:3112
#define XML_DIFF_MARKER
Definition: msg_xml.h:78
gboolean xml_has_children(const xmlNode *xml_root)
Definition: xml.c:3997
#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:363
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:2741
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:2440
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:508
#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:334
#define uint32_t
Definition: stdint.in.h:158
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Definition: xml.c:5024
#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:365
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:3358
gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
Definition: xml.c:5735
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:4820
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:4528
#define XML_CIB_TAG_OBJ_REF
Definition: msg_xml.h:380
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:5121
#define crm_log_xml_trace(xml, text)
Definition: logging.h:262
GHashTable * xml2list(xmlNode *parent)
Definition: xml.c:5042
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:1113
gboolean crm_is_true(const char *s)
Definition: utils.c:683
#define XML_ACL_TAG_ROLE_REF
Definition: msg_xml.h:362
const char * xml_latest_schema(void)
Definition: xml.c:180
void hash2nvpair(gpointer key, gpointer value, gpointer user_data)
Definition: xml.c:4969
gboolean apply_xml_diff(xmlNode *old, xmlNode *diff, xmlNode **new)
Definition: xml.c:4095
#define XML_CIB_TAG_CONFIGURATION
Definition: msg_xml.h:155
#define ID(x)
Definition: msg_xml.h:408
char * crm_itoa(int an_int)
Definition: utils.c:441
#define XML_ACL_TAG_USER
Definition: msg_xml.h:357
#define safe_str_eq(a, b)
Definition: util.h:74
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:2624
int in_upper_context(int depth, int context, xmlNode *xml_node)
Definition: xml.c:4467
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define crm_str_hash
Definition: crm.h:196
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:1126
struct xml_private_s xml_private_t
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:4886
const char * crm_element_value(xmlNode *data, const char *name)
Definition: xml.c:5839
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:45
#define XML_ACL_ATTR_TAG
Definition: msg_xml.h:370
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:4043
GList * GListPtr
Definition: crm.h:190
#define XML_TAG_PARAM
Definition: msg_xml.h:177
long long crm_int_helper(const char *text, char **end_text)
Definition: utils.c:597
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:2473
#define crm_info(fmt, args...)
Definition: logging.h:251
int get_schema_version(const char *name)
Definition: xml.c:5592
gboolean can_prune_leaf(xmlNode *xml_node)
Definition: xml.c:4388
#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