pacemaker  2.0.2-debe490
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
xml.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <time.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 
20 #include <libxml/parser.h>
21 #include <libxml/tree.h>
22 
23 #include <crm/crm.h>
24 #include <crm/msg_xml.h>
25 #include <crm/common/xml.h>
26 #include <crm/common/xml_internal.h> /* CRM_XML_LOG_BASE */
27 #include "crmcommon_private.h"
28 
29 #if HAVE_BZLIB_H
30 # include <bzlib.h>
31 #endif
32 
33 #define XML_BUFFER_SIZE 4096
34 #define XML_PARSER_DEBUG 0
35 
36 typedef struct {
37  int found;
38  const char *string;
39 } filter_t;
40 
41 typedef struct xml_deleted_obj_s {
42  char *path;
43  int position;
45 
46 /* *INDENT-OFF* */
47 
48 static filter_t filter[] = {
49  { 0, XML_ATTR_ORIGIN },
50  { 0, XML_CIB_ATTR_WRITTEN },
51  { 0, XML_ATTR_UPDATE_ORIG },
53  { 0, XML_ATTR_UPDATE_USER },
54 };
55 /* *INDENT-ON* */
56 
57 static xmlNode *subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right, gboolean * changed);
58 static xmlNode *find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact);
59 static int add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update);
60 
61 #define CHUNK_SIZE 1024
62 
63 bool
64 pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
65 {
66  if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
67  return FALSE;
68  } else if(is_not_set(((xml_private_t *)xml->doc->_private)->flags, xpf_tracking)) {
69  return FALSE;
70  } else if (lazy && is_not_set(((xml_private_t *)xml->doc->_private)->flags,
71  xpf_lazy)) {
72  return FALSE;
73  }
74  return TRUE;
75 }
76 
77 #define buffer_print(buffer, max, offset, fmt, args...) do { \
78  int rc = (max); \
79  if(buffer) { \
80  rc = snprintf((buffer) + (offset), (max) - (offset), fmt, ##args); \
81  } \
82  if(buffer && rc < 0) { \
83  crm_perror(LOG_ERR, "snprintf failed at offset %d", offset); \
84  (buffer)[(offset)] = 0; \
85  break; \
86  } else if(rc >= ((max) - (offset))) { \
87  char *tmp = NULL; \
88  (max) = QB_MAX(CHUNK_SIZE, (max) * 2); \
89  tmp = realloc_safe((buffer), (max)); \
90  CRM_ASSERT(tmp); \
91  (buffer) = tmp; \
92  } else { \
93  offset += rc; \
94  break; \
95  } \
96  } while(1);
97 
98 static void
99 insert_prefix(int options, char **buffer, int *offset, int *max, int depth)
100 {
101  if (options & xml_log_option_formatted) {
102  size_t spaces = 2 * depth;
103 
104  if ((*buffer) == NULL || spaces >= ((*max) - (*offset))) {
105  (*max) = QB_MAX(CHUNK_SIZE, (*max) * 2);
106  (*buffer) = realloc_safe((*buffer), (*max));
107  }
108  memset((*buffer) + (*offset), ' ', spaces);
109  (*offset) += spaces;
110  }
111 }
112 
113 static void
114 set_parent_flag(xmlNode *xml, long flag)
115 {
116 
117  for(; xml; xml = xml->parent) {
118  xml_private_t *p = xml->_private;
119 
120  if(p == NULL) {
121  /* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
122  } else {
123  p->flags |= flag;
124  /* crm_trace("Setting flag %x due to %s[@id=%s]", flag, xml->name, ID(xml)); */
125  }
126  }
127 }
128 
129 void
130 pcmk__set_xml_flag(xmlNode *xml, enum xml_private_flags flag)
131 {
132 
133  if(xml && xml->doc && xml->doc->_private){
134  /* During calls to xmlDocCopyNode(), xml->doc may be unset */
135  xml_private_t *p = xml->doc->_private;
136 
137  p->flags |= flag;
138  /* crm_trace("Setting flag %x due to %s[@id=%s]", flag, xml->name, ID(xml)); */
139  }
140 }
141 
142 static void
143 __xml_node_dirty(xmlNode *xml)
144 {
146  set_parent_flag(xml, xpf_dirty);
147 }
148 
149 static void
150 __xml_node_clean(xmlNode *xml)
151 {
152  xmlNode *cIter = NULL;
153  xml_private_t *p = xml->_private;
154 
155  if(p) {
156  p->flags = 0;
157  }
158 
159  for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
160  __xml_node_clean(cIter);
161  }
162 }
163 
164 static void
165 crm_node_created(xmlNode *xml)
166 {
167  xmlNode *cIter = NULL;
168  xml_private_t *p = xml->_private;
169 
170  if(p && pcmk__tracking_xml_changes(xml, FALSE)) {
171  if(is_not_set(p->flags, xpf_created)) {
172  p->flags |= xpf_created;
173  __xml_node_dirty(xml);
174  }
175 
176  for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
177  crm_node_created(cIter);
178  }
179  }
180 }
181 
182 void
184 {
185  xmlNode *parent = a->parent;
186  xml_private_t *p = NULL;
187 
188  p = a->_private;
189  p->flags |= (xpf_dirty|xpf_modified);
190  p->flags = (p->flags & ~xpf_deleted);
191  /* crm_trace("Setting flag %x due to %s[@id=%s, @%s=%s]", */
192  /* xpf_dirty, parent?parent->name:NULL, ID(parent), a->name, a->children->content); */
193 
194  __xml_node_dirty(parent);
195 }
196 
197 int get_tag_name(const char *input, size_t offset, size_t max);
198 int get_attr_name(const char *input, size_t offset, size_t max);
199 int get_attr_value(const char *input, size_t offset, size_t max);
200 gboolean can_prune_leaf(xmlNode * xml_node);
201 
202 static int add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff);
203 
204 #define XML_PRIVATE_MAGIC (long) 0x81726354
205 
206 static void
207 __xml_deleted_obj_free(void *data)
208 {
209  if(data) {
210  xml_deleted_obj_t *deleted_obj = data;
211 
212  free(deleted_obj->path);
213  free(deleted_obj);
214  }
215 }
216 
217 static void
218 __xml_private_clean(xml_private_t *p)
219 {
220  if(p) {
222 
223  free(p->user);
224  p->user = NULL;
225 
226  if(p->acls) {
227  pcmk__free_acls(p->acls);
228  p->acls = NULL;
229  }
230 
231  if(p->deleted_objs) {
232  g_list_free_full(p->deleted_objs, __xml_deleted_obj_free);
233  p->deleted_objs = NULL;
234  }
235  }
236 }
237 
238 
239 static void
240 __xml_private_free(xml_private_t *p)
241 {
242  __xml_private_clean(p);
243  free(p);
244 }
245 
246 static void
247 pcmkDeregisterNode(xmlNodePtr node)
248 {
249  /* need to explicitly avoid our custom _private field cleanup when
250  called from internal XSLT cleanup (xsltApplyStylesheetInternal
251  -> xsltFreeTransformContext -> xsltFreeRVTs -> xmlFreeDoc)
252  onto result tree fragments, represented as standalone documents
253  with otherwise infeasible space-prefixed name (xsltInternals.h:
254  XSLT_MARK_RES_TREE_FRAG) and carrying it's own load at _private
255  field -- later assert on the XML_PRIVATE_MAGIC would explode */
256  if (node->type != XML_DOCUMENT_NODE || node->name == NULL
257  || node->name[0] != ' ') {
258  __xml_private_free(node->_private);
259  }
260 }
261 
262 static void
263 pcmkRegisterNode(xmlNodePtr node)
264 {
265  xml_private_t *p = NULL;
266 
267  switch(node->type) {
268  case XML_ELEMENT_NODE:
269  case XML_DOCUMENT_NODE:
270  case XML_ATTRIBUTE_NODE:
271  case XML_COMMENT_NODE:
272  p = calloc(1, sizeof(xml_private_t));
274  /* Flags will be reset if necessary when tracking is enabled */
275  p->flags |= (xpf_dirty|xpf_created);
276  node->_private = p;
277  break;
278  case XML_TEXT_NODE:
279  case XML_DTD_NODE:
280  case XML_CDATA_SECTION_NODE:
281  break;
282  default:
283  /* Ignore */
284  crm_trace("Ignoring %p %d", node, node->type);
285  CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
286  break;
287  }
288 
289  if(p && pcmk__tracking_xml_changes(node, FALSE)) {
290  /* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
291  * not hooked up at the point we are called
292  */
294  __xml_node_dirty(node);
295  }
296 }
297 
298 void
299 xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
300 {
301  xml_accept_changes(xml);
302  crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
304  if(enforce_acls) {
305  if(acl_source == NULL) {
306  acl_source = xml;
307  }
309  pcmk__unpack_acl(acl_source, xml, user);
310  pcmk__apply_acl(xml);
311  }
312 }
313 
314 bool xml_tracking_changes(xmlNode * xml)
315 {
316  if(xml == NULL) {
317  return FALSE;
318 
319  } else if(is_set(((xml_private_t *)xml->doc->_private)->flags, xpf_tracking)) {
320  return TRUE;
321  }
322  return FALSE;
323 }
324 
325 bool xml_document_dirty(xmlNode *xml)
326 {
327  if(xml != NULL && xml->doc && xml->doc->_private) {
328  xml_private_t *doc = xml->doc->_private;
329 
330  return is_set(doc->flags, xpf_dirty);
331  }
332  return FALSE;
333 }
334 
335 /*
336 <diff format="2.0">
337  <version>
338  <source admin_epoch="1" epoch="2" num_updates="3"/>
339  <target admin_epoch="1" epoch="3" num_updates="0"/>
340  </version>
341  <change operation="add" xpath="/cib/configuration/nodes">
342  <node id="node2" uname="node2" description="foo"/>
343  </change>
344  <change operation="add" xpath="/cib/configuration/nodes/node[node2]">
345  <instance_attributes id="nodes-node"><!-- NOTE: can be a full tree -->
346  <nvpair id="nodes-node2-ram" name="ram" value="1024M"/>
347  </instance_attributes>
348  </change>
349  <change operation="update" xpath="/cib/configuration/nodes[@id='node2']">
350  <change-list>
351  <change-attr operation="set" name="type" value="member"/>
352  <change-attr operation="unset" name="description"/>
353  </change-list>
354  <change-result>
355  <node id="node2" uname="node2" type="member"/><!-- NOTE: not recursive -->
356  </change-result>
357  </change>
358  <change operation="delete" xpath="/cib/configuration/nodes/node[@id='node3'] /">
359  <change operation="update" xpath="/cib/configuration/resources/group[@id='g1']">
360  <change-list>
361  <change-attr operation="set" name="description" value="some grabage here"/>
362  </change-list>
363  <change-result>
364  <group id="g1" description="some grabage here"/><!-- NOTE: not recursive -->
365  </change-result>
366  </change>
367  <change operation="update" xpath="/cib/status/node_state[@id='node2]/lrm[@id='node2']/lrm_resources/lrm_resource[@id='Fence']">
368  <change-list>
369  <change-attr operation="set" name="oper" value="member"/>
370  <change-attr operation="set" name="operation_key" value="Fence_start_0"/>
371  <change-attr operation="set" name="operation" value="start"/>
372  <change-attr operation="set" name="transition-key" value="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"/>
373  <change-attr operation="set" name="transition-magic" value="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"/>
374  <change-attr operation="set" name="call-id" value="2"/>
375  <change-attr operation="set" name="rc-code" value="0"/>
376  </change-list>
377  <change-result>
378  <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"/>
379  </change-result>
380  </change>
381 </diff>
382  */
383 static int __xml_offset(xmlNode *xml)
384 {
385  int position = 0;
386  xmlNode *cIter = NULL;
387 
388  for(cIter = xml; cIter->prev; cIter = cIter->prev) {
389  xml_private_t *p = ((xmlNode*)cIter->prev)->_private;
390 
391  if(is_not_set(p->flags, xpf_skip)) {
392  position++;
393  }
394  }
395 
396  return position;
397 }
398 
399 static int __xml_offset_no_deletions(xmlNode *xml)
400 {
401  int position = 0;
402  xmlNode *cIter = NULL;
403 
404  for(cIter = xml; cIter->prev; cIter = cIter->prev) {
405  xml_private_t *p = ((xmlNode*)cIter->prev)->_private;
406 
407  if(is_not_set(p->flags, xpf_deleted)) {
408  position++;
409  }
410  }
411 
412  return position;
413 }
414 
415 static void
416 __xml_build_changes(xmlNode * xml, xmlNode *patchset)
417 {
418  xmlNode *cIter = NULL;
419  xmlAttr *pIter = NULL;
420  xmlNode *change = NULL;
421  xml_private_t *p = xml->_private;
422 
423  if(patchset && is_set(p->flags, xpf_created)) {
424  int offset = 0;
425  char buffer[XML_BUFFER_SIZE];
426 
427  if (pcmk__element_xpath(NULL, xml->parent, buffer, offset,
428  sizeof(buffer)) > 0) {
429  int position = __xml_offset_no_deletions(xml);
430 
431  change = create_xml_node(patchset, XML_DIFF_CHANGE);
432 
433  crm_xml_add(change, XML_DIFF_OP, "create");
434  crm_xml_add(change, XML_DIFF_PATH, buffer);
435  crm_xml_add_int(change, XML_DIFF_POSITION, position);
436  add_node_copy(change, xml);
437  }
438 
439  return;
440  }
441 
442  for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
443  xmlNode *attr = NULL;
444 
445  p = pIter->_private;
446  if(is_not_set(p->flags, xpf_deleted) && is_not_set(p->flags, xpf_dirty)) {
447  continue;
448  }
449 
450  if(change == NULL) {
451  int offset = 0;
452  char buffer[XML_BUFFER_SIZE];
453 
454  if (pcmk__element_xpath(NULL, xml, buffer, offset,
455  sizeof(buffer)) > 0) {
456  change = create_xml_node(patchset, XML_DIFF_CHANGE);
457 
458  crm_xml_add(change, XML_DIFF_OP, "modify");
459  crm_xml_add(change, XML_DIFF_PATH, buffer);
460 
461  change = create_xml_node(change, XML_DIFF_LIST);
462  }
463  }
464 
465  attr = create_xml_node(change, XML_DIFF_ATTR);
466 
467  crm_xml_add(attr, XML_NVPAIR_ATTR_NAME, (const char *)pIter->name);
468  if(p->flags & xpf_deleted) {
469  crm_xml_add(attr, XML_DIFF_OP, "unset");
470 
471  } else {
472  const char *value = crm_element_value(xml, (const char *)pIter->name);
473 
474  crm_xml_add(attr, XML_DIFF_OP, "set");
475  crm_xml_add(attr, XML_NVPAIR_ATTR_VALUE, value);
476  }
477  }
478 
479  if(change) {
480  xmlNode *result = NULL;
481 
482  change = create_xml_node(change->parent, XML_DIFF_RESULT);
483  result = create_xml_node(change, (const char *)xml->name);
484 
485  for (pIter = pcmk__first_xml_attr(xml); pIter != NULL; pIter = pIter->next) {
486  const char *value = crm_element_value(xml, (const char *)pIter->name);
487 
488  p = pIter->_private;
489  if (is_not_set(p->flags, xpf_deleted)) {
490  crm_xml_add(result, (const char *)pIter->name, value);
491  }
492  }
493  }
494 
495  for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
496  __xml_build_changes(cIter, patchset);
497  }
498 
499  p = xml->_private;
500  if(patchset && is_set(p->flags, xpf_moved)) {
501  int offset = 0;
502  char buffer[XML_BUFFER_SIZE];
503 
504  crm_trace("%s.%s moved to position %d", xml->name, ID(xml), __xml_offset(xml));
505  if (pcmk__element_xpath(NULL, xml, buffer, offset,
506  sizeof(buffer)) > 0) {
507  change = create_xml_node(patchset, XML_DIFF_CHANGE);
508 
509  crm_xml_add(change, XML_DIFF_OP, "move");
510  crm_xml_add(change, XML_DIFF_PATH, buffer);
511  crm_xml_add_int(change, XML_DIFF_POSITION, __xml_offset_no_deletions(xml));
512  }
513  }
514 }
515 
516 static void
517 __xml_accept_changes(xmlNode * xml)
518 {
519  xmlNode *cIter = NULL;
520  xmlAttr *pIter = NULL;
521  xml_private_t *p = xml->_private;
522 
523  p->flags = xpf_none;
524  pIter = pcmk__first_xml_attr(xml);
525 
526  while (pIter != NULL) {
527  const xmlChar *name = pIter->name;
528 
529  p = pIter->_private;
530  pIter = pIter->next;
531 
532  if(p->flags & xpf_deleted) {
533  xml_remove_prop(xml, (const char *)name);
534 
535  } else {
536  p->flags = xpf_none;
537  }
538  }
539 
540  for (cIter = __xml_first_child(xml); cIter != NULL; cIter = __xml_next(cIter)) {
541  __xml_accept_changes(cIter);
542  }
543 }
544 
545 static bool
546 is_config_change(xmlNode *xml)
547 {
548  GListPtr gIter = NULL;
549  xml_private_t *p = NULL;
550  xmlNode *config = first_named_child(xml, XML_CIB_TAG_CONFIGURATION);
551 
552  if(config) {
553  p = config->_private;
554  }
555  if(p && is_set(p->flags, xpf_dirty)) {
556  return TRUE;
557  }
558 
559  if(xml->doc && xml->doc->_private) {
560  p = xml->doc->_private;
561  for(gIter = p->deleted_objs; gIter; gIter = gIter->next) {
562  xml_deleted_obj_t *deleted_obj = gIter->data;
563 
564  if(strstr(deleted_obj->path, "/"XML_TAG_CIB"/"XML_CIB_TAG_CONFIGURATION) != NULL) {
565  return TRUE;
566  }
567  }
568  }
569 
570  return FALSE;
571 }
572 
573 static void
574 xml_repair_v1_diff(xmlNode * last, xmlNode * next, xmlNode * local_diff, gboolean changed)
575 {
576  int lpc = 0;
577  xmlNode *cib = NULL;
578  xmlNode *diff_child = NULL;
579 
580  const char *tag = NULL;
581 
582  const char *vfields[] = {
586  };
587 
588  if (local_diff == NULL) {
589  crm_trace("Nothing to do");
590  return;
591  }
592 
593  tag = "diff-removed";
594  diff_child = find_xml_node(local_diff, tag, FALSE);
595  if (diff_child == NULL) {
596  diff_child = create_xml_node(local_diff, tag);
597  }
598 
599  tag = XML_TAG_CIB;
600  cib = find_xml_node(diff_child, tag, FALSE);
601  if (cib == NULL) {
602  cib = create_xml_node(diff_child, tag);
603  }
604 
605  for(lpc = 0; last && lpc < DIMOF(vfields); lpc++){
606  const char *value = crm_element_value(last, vfields[lpc]);
607 
608  crm_xml_add(diff_child, vfields[lpc], value);
609  if(changed || lpc == 2) {
610  crm_xml_add(cib, vfields[lpc], value);
611  }
612  }
613 
614  tag = "diff-added";
615  diff_child = find_xml_node(local_diff, tag, FALSE);
616  if (diff_child == NULL) {
617  diff_child = create_xml_node(local_diff, tag);
618  }
619 
620  tag = XML_TAG_CIB;
621  cib = find_xml_node(diff_child, tag, FALSE);
622  if (cib == NULL) {
623  cib = create_xml_node(diff_child, tag);
624  }
625 
626  for(lpc = 0; next && lpc < DIMOF(vfields); lpc++){
627  const char *value = crm_element_value(next, vfields[lpc]);
628 
629  crm_xml_add(diff_child, vfields[lpc], value);
630  }
631 
632  if (next) {
633  xmlAttrPtr xIter = NULL;
634 
635  for (xIter = next->properties; xIter; xIter = xIter->next) {
636  const char *p_name = (const char *)xIter->name;
637  const char *p_value = crm_element_value(next, p_name);
638 
639  xmlSetProp(cib, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
640  }
641  }
642 
643  crm_log_xml_explicit(local_diff, "Repaired-diff");
644 }
645 
646 static xmlNode *
647 xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config, bool suppress)
648 {
649  xmlNode *patchset = diff_xml_object(source, target, suppress);
650 
651  if(patchset) {
653  xml_repair_v1_diff(source, target, patchset, config);
654  crm_xml_add(patchset, "format", "1");
655  }
656  return patchset;
657 }
658 
659 static xmlNode *
660 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
661 {
662  int lpc = 0;
663  GListPtr gIter = NULL;
664  xml_private_t *doc = NULL;
665 
666  xmlNode *v = NULL;
667  xmlNode *version = NULL;
668  xmlNode *patchset = NULL;
669  const char *vfields[] = {
673  };
674 
675  CRM_ASSERT(target);
676  if(xml_document_dirty(target) == FALSE) {
677  return NULL;
678  }
679 
680  CRM_ASSERT(target->doc);
681  doc = target->doc->_private;
682 
683  patchset = create_xml_node(NULL, XML_TAG_DIFF);
684  crm_xml_add_int(patchset, "format", 2);
685 
686  version = create_xml_node(patchset, XML_DIFF_VERSION);
687 
688  v = create_xml_node(version, XML_DIFF_VSOURCE);
689  for(lpc = 0; lpc < DIMOF(vfields); lpc++){
690  const char *value = crm_element_value(source, vfields[lpc]);
691 
692  if(value == NULL) {
693  value = "1";
694  }
695  crm_xml_add(v, vfields[lpc], value);
696  }
697 
698  v = create_xml_node(version, XML_DIFF_VTARGET);
699  for(lpc = 0; lpc < DIMOF(vfields); lpc++){
700  const char *value = crm_element_value(target, vfields[lpc]);
701 
702  if(value == NULL) {
703  value = "1";
704  }
705  crm_xml_add(v, vfields[lpc], value);
706  }
707 
708  for(gIter = doc->deleted_objs; gIter; gIter = gIter->next) {
709  xml_deleted_obj_t *deleted_obj = gIter->data;
710  xmlNode *change = create_xml_node(patchset, XML_DIFF_CHANGE);
711 
712  crm_xml_add(change, XML_DIFF_OP, "delete");
713  crm_xml_add(change, XML_DIFF_PATH, deleted_obj->path);
714  if (deleted_obj->position >= 0) {
715  crm_xml_add_int(change, XML_DIFF_POSITION, deleted_obj->position);
716  }
717  }
718 
719  __xml_build_changes(target, patchset);
720  return patchset;
721 }
722 
723 xmlNode *
724 xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version)
725 {
726  int counter = 0;
727  bool config = FALSE;
728  xmlNode *patch = NULL;
729  const char *version = crm_element_value(source, XML_ATTR_CRM_VERSION);
730 
731  xml_acl_disable(target);
732  if(xml_document_dirty(target) == FALSE) {
733  crm_trace("No change %d", format);
734  return NULL; /* No change */
735  }
736 
737  config = is_config_change(target);
738  if(config_changed) {
739  *config_changed = config;
740  }
741 
742  if(manage_version && config) {
743  crm_trace("Config changed %d", format);
744  crm_xml_add(target, XML_ATTR_NUMUPDATES, "0");
745 
746  crm_element_value_int(target, XML_ATTR_GENERATION, &counter);
747  crm_xml_add_int(target, XML_ATTR_GENERATION, counter+1);
748 
749  } else if(manage_version) {
750  crm_element_value_int(target, XML_ATTR_NUMUPDATES, &counter);
751  crm_trace("Status changed %d - %d %s", format, counter, crm_element_value(source, XML_ATTR_NUMUPDATES));
752  crm_xml_add_int(target, XML_ATTR_NUMUPDATES, counter+1);
753  }
754 
755  if(format == 0) {
756  if (compare_version("3.0.8", version) < 0) {
757  format = 2;
758 
759  } else {
760  format = 1;
761  }
762  crm_trace("Using patch format %d for version: %s", format, version);
763  }
764 
765  switch(format) {
766  case 1:
767  patch = xml_create_patchset_v1(source, target, config, FALSE);
768  break;
769  case 2:
770  patch = xml_create_patchset_v2(source, target);
771  break;
772  default:
773  crm_err("Unknown patch format: %d", format);
774  return NULL;
775  }
776 
777  return patch;
778 }
779 
780 void
781 patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
782 {
783  int format = 1;
784  const char *version = NULL;
785  char *digest = NULL;
786 
787  if (patch == NULL || source == NULL || target == NULL) {
788  return;
789  }
790 
791  /* NOTE: We should always call xml_accept_changes() before calculating digest. */
792  /* Otherwise, with an on-tracking dirty target, we could get a wrong digest. */
793  CRM_LOG_ASSERT(xml_document_dirty(target) == FALSE);
794 
795  crm_element_value_int(patch, "format", &format);
796  if (format > 1 && with_digest == FALSE) {
797  return;
798  }
799 
800  version = crm_element_value(source, XML_ATTR_CRM_VERSION);
801  digest = calculate_xml_versioned_digest(target, FALSE, TRUE, version);
802 
803  crm_xml_add(patch, XML_ATTR_DIGEST, digest);
804  free(digest);
805 
806  return;
807 }
808 
809 static void
810 __xml_log_element(int log_level, const char *file, const char *function, int line,
811  const char *prefix, xmlNode * data, int depth, int options);
812 
813 void
814 xml_log_patchset(uint8_t log_level, const char *function, xmlNode * patchset)
815 {
816  int format = 1;
817  xmlNode *child = NULL;
818  xmlNode *added = NULL;
819  xmlNode *removed = NULL;
820  gboolean is_first = TRUE;
821 
822  int add[] = { 0, 0, 0 };
823  int del[] = { 0, 0, 0 };
824 
825  const char *fmt = NULL;
826  const char *digest = NULL;
827  int options = xml_log_option_formatted;
828 
829  static struct qb_log_callsite *patchset_cs = NULL;
830 
831  if (patchset_cs == NULL) {
832  patchset_cs = qb_log_callsite_get(function, __FILE__, "xml-patchset", log_level, __LINE__, 0);
833  }
834 
835  if (patchset == NULL) {
836  crm_trace("Empty patch");
837  return;
838 
839  } else if (log_level == 0) {
840  /* Log to stdout */
841  } else if (crm_is_callsite_active(patchset_cs, log_level, 0) == FALSE) {
842  return;
843  }
844 
845  xml_patch_versions(patchset, add, del);
846  fmt = crm_element_value(patchset, "format");
847  digest = crm_element_value(patchset, XML_ATTR_DIGEST);
848 
849  if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
850  do_crm_log_alias(log_level, __FILE__, function, __LINE__,
851  "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
852  do_crm_log_alias(log_level, __FILE__, function, __LINE__,
853  "Diff: +++ %d.%d.%d %s", add[0], add[1], add[2], digest);
854 
855  } else if (patchset != NULL && (add[0] || add[1] || add[2])) {
856  do_crm_log_alias(log_level, __FILE__, function, __LINE__,
857  "%s: Local-only Change: %d.%d.%d", function ? function : "",
858  add[0], add[1], add[2]);
859  }
860 
861  crm_element_value_int(patchset, "format", &format);
862  if(format == 2) {
863  xmlNode *change = NULL;
864 
865  for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
866  const char *op = crm_element_value(change, XML_DIFF_OP);
867  const char *xpath = crm_element_value(change, XML_DIFF_PATH);
868 
869  if(op == NULL) {
870  } else if(strcmp(op, "create") == 0) {
871  int lpc = 0, max = 0;
872  char *prefix = crm_strdup_printf("++ %s: ", xpath);
873 
874  max = strlen(prefix);
875  __xml_log_element(log_level, __FILE__, function, __LINE__, prefix, change->children,
877 
878  for(lpc = 2; lpc < max; lpc++) {
879  prefix[lpc] = ' ';
880  }
881 
882  __xml_log_element(log_level, __FILE__, function, __LINE__, prefix, change->children,
884  free(prefix);
885 
886  } else if(strcmp(op, "move") == 0) {
887  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "+~ %s moved to offset %s", xpath, crm_element_value(change, XML_DIFF_POSITION));
888 
889  } else if(strcmp(op, "modify") == 0) {
890  xmlNode *clist = first_named_child(change, XML_DIFF_LIST);
891  char buffer_set[XML_BUFFER_SIZE];
892  char buffer_unset[XML_BUFFER_SIZE];
893  int o_set = 0;
894  int o_unset = 0;
895 
896  buffer_set[0] = 0;
897  buffer_unset[0] = 0;
898  for (child = __xml_first_child(clist); child != NULL; child = __xml_next(child)) {
899  const char *name = crm_element_value(child, "name");
900 
901  op = crm_element_value(child, XML_DIFF_OP);
902  if(op == NULL) {
903  } else if(strcmp(op, "set") == 0) {
904  const char *value = crm_element_value(child, "value");
905 
906  if(o_set > 0) {
907  o_set += snprintf(buffer_set + o_set, XML_BUFFER_SIZE - o_set, ", ");
908  }
909  o_set += snprintf(buffer_set + o_set, XML_BUFFER_SIZE - o_set, "@%s=%s", name, value);
910 
911  } else if(strcmp(op, "unset") == 0) {
912  if(o_unset > 0) {
913  o_unset += snprintf(buffer_unset + o_unset, XML_BUFFER_SIZE - o_unset, ", ");
914  }
915  o_unset += snprintf(buffer_unset + o_unset, XML_BUFFER_SIZE - o_unset, "@%s", name);
916  }
917  }
918  if(o_set) {
919  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "+ %s: %s", xpath, buffer_set);
920  }
921  if(o_unset) {
922  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s: %s", xpath, buffer_unset);
923  }
924 
925  } else if(strcmp(op, "delete") == 0) {
926  int position = -1;
927 
928  crm_element_value_int(change, XML_DIFF_POSITION, &position);
929  if (position >= 0) {
930  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)", xpath, position);
931 
932  } else {
933  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s", xpath);
934  }
935  }
936  }
937  return;
938  }
939 
940  if (log_level < LOG_DEBUG || function == NULL) {
941  options |= xml_log_option_diff_short;
942  }
943 
944  removed = find_xml_node(patchset, "diff-removed", FALSE);
945  for (child = __xml_first_child(removed); child != NULL; child = __xml_next(child)) {
946  log_data_element(log_level, __FILE__, function, __LINE__, "- ", child, 0,
947  options | xml_log_option_diff_minus);
948  if (is_first) {
949  is_first = FALSE;
950  } else {
951  do_crm_log_alias(log_level, __FILE__, function, __LINE__, " --- ");
952  }
953  }
954 
955  is_first = TRUE;
956  added = find_xml_node(patchset, "diff-added", FALSE);
957  for (child = __xml_first_child(added); child != NULL; child = __xml_next(child)) {
958  log_data_element(log_level, __FILE__, function, __LINE__, "+ ", child, 0,
959  options | xml_log_option_diff_plus);
960  if (is_first) {
961  is_first = FALSE;
962  } else {
963  do_crm_log_alias(log_level, __FILE__, function, __LINE__, " +++ ");
964  }
965  }
966 }
967 
968 void
969 xml_log_changes(uint8_t log_level, const char *function, xmlNode * xml)
970 {
971  GListPtr gIter = NULL;
972  xml_private_t *doc = NULL;
973 
974  CRM_ASSERT(xml);
975  CRM_ASSERT(xml->doc);
976 
977  doc = xml->doc->_private;
978  if(is_not_set(doc->flags, xpf_dirty)) {
979  return;
980  }
981 
982  for(gIter = doc->deleted_objs; gIter; gIter = gIter->next) {
983  xml_deleted_obj_t *deleted_obj = gIter->data;
984 
985  if (deleted_obj->position >= 0) {
986  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s (%d)",
987  deleted_obj->path, deleted_obj->position);
988 
989  } else {
990  do_crm_log_alias(log_level, __FILE__, function, __LINE__, "-- %s",
991  deleted_obj->path);
992  }
993  }
994 
995  log_data_element(log_level, __FILE__, function, __LINE__, "+ ", xml, 0,
997 }
998 
999 void
1000 xml_accept_changes(xmlNode * xml)
1001 {
1002  xmlNode *top = NULL;
1003  xml_private_t *doc = NULL;
1004 
1005  if(xml == NULL) {
1006  return;
1007  }
1008 
1009  crm_trace("Accepting changes to %p", xml);
1010  doc = xml->doc->_private;
1011  top = xmlDocGetRootElement(xml->doc);
1012 
1013  __xml_private_clean(xml->doc->_private);
1014 
1015  if(is_not_set(doc->flags, xpf_dirty)) {
1016  doc->flags = xpf_none;
1017  return;
1018  }
1019 
1020  doc->flags = xpf_none;
1021  __xml_accept_changes(top);
1022 }
1023 
1024 static xmlNode *
1025 find_element(xmlNode *haystack, xmlNode *needle, gboolean exact)
1026 {
1027  CRM_CHECK(needle != NULL, return NULL);
1028  return (needle->type == XML_COMMENT_NODE)?
1029  find_xml_comment(haystack, needle, exact)
1030  : find_entity(haystack, crm_element_name(needle), ID(needle));
1031 }
1032 
1033 /* Simplified version for applying v1-style XML patches */
1034 static void
1035 __subtract_xml_object(xmlNode * target, xmlNode * patch)
1036 {
1037  xmlNode *patch_child = NULL;
1038  xmlNode *cIter = NULL;
1039  xmlAttrPtr xIter = NULL;
1040 
1041  char *id = NULL;
1042  const char *name = NULL;
1043  const char *value = NULL;
1044 
1045  if (target == NULL || patch == NULL) {
1046  return;
1047  }
1048 
1049  if (target->type == XML_COMMENT_NODE) {
1050  gboolean dummy;
1051 
1052  subtract_xml_comment(target->parent, target, patch, &dummy);
1053  }
1054 
1055  name = crm_element_name(target);
1056  CRM_CHECK(name != NULL, return);
1057  CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(patch)), return);
1058  CRM_CHECK(safe_str_eq(ID(target), ID(patch)), return);
1059 
1060  /* check for XML_DIFF_MARKER in a child */
1061  id = crm_element_value_copy(target, XML_ATTR_ID);
1062  value = crm_element_value(patch, XML_DIFF_MARKER);
1063  if (value != NULL && strcmp(value, "removed:top") == 0) {
1064  crm_trace("We are the root of the deletion: %s.id=%s", name, id);
1065  free_xml(target);
1066  free(id);
1067  return;
1068  }
1069 
1070  for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1071  const char *p_name = (const char *)xIter->name;
1072 
1073  /* Removing and then restoring the id field would change the ordering of properties */
1074  if (safe_str_neq(p_name, XML_ATTR_ID)) {
1075  xml_remove_prop(target, p_name);
1076  }
1077  }
1078 
1079  /* changes to child objects */
1080  cIter = __xml_first_child(target);
1081  while (cIter) {
1082  xmlNode *target_child = cIter;
1083 
1084  cIter = __xml_next(cIter);
1085  patch_child = find_element(patch, target_child, FALSE);
1086  __subtract_xml_object(target_child, patch_child);
1087  }
1088  free(id);
1089 }
1090 
1091 static void
1092 __add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * patch)
1093 {
1094  xmlNode *patch_child = NULL;
1095  xmlNode *target_child = NULL;
1096  xmlAttrPtr xIter = NULL;
1097 
1098  const char *id = NULL;
1099  const char *name = NULL;
1100  const char *value = NULL;
1101 
1102  if (patch == NULL) {
1103  return;
1104  } else if (parent == NULL && target == NULL) {
1105  return;
1106  }
1107 
1108  /* check for XML_DIFF_MARKER in a child */
1109  value = crm_element_value(patch, XML_DIFF_MARKER);
1110  if (target == NULL
1111  && value != NULL
1112  && strcmp(value, "added:top") == 0) {
1113  id = ID(patch);
1114  name = crm_element_name(patch);
1115  crm_trace("We are the root of the addition: %s.id=%s", name, id);
1116  add_node_copy(parent, patch);
1117  return;
1118 
1119  } else if(target == NULL) {
1120  id = ID(patch);
1121  name = crm_element_name(patch);
1122  crm_err("Could not locate: %s.id=%s", name, id);
1123  return;
1124  }
1125 
1126  if (target->type == XML_COMMENT_NODE) {
1127  add_xml_comment(parent, target, patch);
1128  }
1129 
1130  name = crm_element_name(target);
1131  CRM_CHECK(name != NULL, return);
1132  CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(patch)), return);
1133  CRM_CHECK(safe_str_eq(ID(target), ID(patch)), return);
1134 
1135  for (xIter = pcmk__first_xml_attr(patch); xIter != NULL; xIter = xIter->next) {
1136  const char *p_name = (const char *)xIter->name;
1137  const char *p_value = crm_element_value(patch, p_name);
1138 
1139  xml_remove_prop(target, p_name); /* Preserve the patch order */
1140  crm_xml_add(target, p_name, p_value);
1141  }
1142 
1143  /* changes to child objects */
1144  for (patch_child = __xml_first_child(patch); patch_child != NULL;
1145  patch_child = __xml_next(patch_child)) {
1146 
1147  target_child = find_element(target, patch_child, FALSE);
1148  __add_xml_object(target, target_child, patch_child);
1149  }
1150 }
1151 
1163 static bool
1164 find_patch_xml_node(xmlNode *patchset, int format, bool added,
1165  xmlNode **patch_node)
1166 {
1167  xmlNode *cib_node;
1168  const char *label;
1169 
1170  switch(format) {
1171  case 1:
1172  label = added? "diff-added" : "diff-removed";
1173  *patch_node = find_xml_node(patchset, label, FALSE);
1174  cib_node = find_xml_node(*patch_node, "cib", FALSE);
1175  if (cib_node != NULL) {
1176  *patch_node = cib_node;
1177  }
1178  break;
1179  case 2:
1180  label = added? "target" : "source";
1181  *patch_node = find_xml_node(patchset, "version", FALSE);
1182  *patch_node = find_xml_node(*patch_node, label, FALSE);
1183  break;
1184  default:
1185  crm_warn("Unknown patch format: %d", format);
1186  *patch_node = NULL;
1187  return FALSE;
1188  }
1189  return TRUE;
1190 }
1191 
1192 bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3])
1193 {
1194  int lpc = 0;
1195  int format = 1;
1196  xmlNode *tmp = NULL;
1197 
1198  const char *vfields[] = {
1202  };
1203 
1204 
1205  crm_element_value_int(patchset, "format", &format);
1206 
1207  /* Process removals */
1208  if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
1209  return -EINVAL;
1210  }
1211  if (tmp) {
1212  for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1213  crm_element_value_int(tmp, vfields[lpc], &(del[lpc]));
1214  crm_trace("Got %d for del[%s]", del[lpc], vfields[lpc]);
1215  }
1216  }
1217 
1218  /* Process additions */
1219  if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
1220  return -EINVAL;
1221  }
1222  if (tmp) {
1223  for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1224  crm_element_value_int(tmp, vfields[lpc], &(add[lpc]));
1225  crm_trace("Got %d for add[%s]", add[lpc], vfields[lpc]);
1226  }
1227  }
1228 
1229  return pcmk_ok;
1230 }
1231 
1232 static int
1233 xml_patch_version_check(xmlNode *xml, xmlNode *patchset, int format)
1234 {
1235  int lpc = 0;
1236  bool changed = FALSE;
1237 
1238  int this[] = { 0, 0, 0 };
1239  int add[] = { 0, 0, 0 };
1240  int del[] = { 0, 0, 0 };
1241 
1242  const char *vfields[] = {
1246  };
1247 
1248  for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1249  crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
1250  crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
1251  if (this[lpc] < 0) {
1252  this[lpc] = 0;
1253  }
1254  }
1255 
1256  /* Set some defaults in case nothing is present */
1257  add[0] = this[0];
1258  add[1] = this[1];
1259  add[2] = this[2] + 1;
1260  for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1261  del[lpc] = this[lpc];
1262  }
1263 
1264  xml_patch_versions(patchset, add, del);
1265 
1266  for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1267  if(this[lpc] < del[lpc]) {
1268  crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)", vfields[lpc],
1269  this[0], this[1], this[2], del[0], del[1], del[2], add[0], add[1], add[2]);
1270  return -pcmk_err_diff_resync;
1271 
1272  } else if(this[lpc] > del[lpc]) {
1273  crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p", vfields[lpc],
1274  this[0], this[1], this[2], del[0], del[1], del[2], add[0], add[1], add[2], patchset);
1275  crm_log_xml_info(patchset, "OldPatch");
1276  return -pcmk_err_old_data;
1277  }
1278  }
1279 
1280  for(lpc = 0; lpc < DIMOF(vfields); lpc++) {
1281  if(add[lpc] > del[lpc]) {
1282  changed = TRUE;
1283  }
1284  }
1285 
1286  if(changed == FALSE) {
1287  crm_notice("Versions did not change in patch %d.%d.%d", add[0], add[1], add[2]);
1288  return -pcmk_err_old_data;
1289  }
1290 
1291  crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
1292  add[0], add[1], add[2], this[0], this[1], this[2]);
1293  return pcmk_ok;
1294 }
1295 
1296 static int
1297 xml_apply_patchset_v1(xmlNode *xml, xmlNode *patchset)
1298 {
1299  int rc = pcmk_ok;
1300  int root_nodes_seen = 0;
1301 
1302  xmlNode *child_diff = NULL;
1303  xmlNode *added = find_xml_node(patchset, "diff-added", FALSE);
1304  xmlNode *removed = find_xml_node(patchset, "diff-removed", FALSE);
1305  xmlNode *old = copy_xml(xml);
1306 
1307  crm_trace("Subtraction Phase");
1308  for (child_diff = __xml_first_child(removed); child_diff != NULL;
1309  child_diff = __xml_next(child_diff)) {
1310  CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1311  if (root_nodes_seen == 0) {
1312  __subtract_xml_object(xml, child_diff);
1313  }
1314  root_nodes_seen++;
1315  }
1316 
1317  if (root_nodes_seen > 1) {
1318  crm_err("(-) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1319  rc = -ENOTUNIQ;
1320  }
1321 
1322  root_nodes_seen = 0;
1323  crm_trace("Addition Phase");
1324  if (rc == pcmk_ok) {
1325  xmlNode *child_diff = NULL;
1326 
1327  for (child_diff = __xml_first_child(added); child_diff != NULL;
1328  child_diff = __xml_next(child_diff)) {
1329  CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
1330  if (root_nodes_seen == 0) {
1331  __add_xml_object(NULL, xml, child_diff);
1332  }
1333  root_nodes_seen++;
1334  }
1335  }
1336 
1337  if (root_nodes_seen > 1) {
1338  crm_err("(+) Diffs cannot contain more than one change set... saw %d", root_nodes_seen);
1339  rc = -ENOTUNIQ;
1340  }
1341 
1342  purge_diff_markers(xml); /* Purge prior to checking the digest */
1343 
1344  free_xml(old);
1345  return rc;
1346 }
1347 
1348 static xmlNode *
1349 __first_xml_child_match(xmlNode *parent, const char *name, const char *id, int position)
1350 {
1351  xmlNode *cIter = NULL;
1352 
1353  for (cIter = __xml_first_child(parent); cIter != NULL; cIter = __xml_next(cIter)) {
1354  if(strcmp((const char *)cIter->name, name) != 0) {
1355  continue;
1356  } else if(id) {
1357  const char *cid = ID(cIter);
1358  if(cid == NULL || strcmp(cid, id) != 0) {
1359  continue;
1360  }
1361  }
1362 
1363  /* The "position" makes sense only for XML comments for now */
1364  if (cIter->type == XML_COMMENT_NODE
1365  && position >= 0
1366  && __xml_offset(cIter) != position) {
1367  continue;
1368  }
1369 
1370  return cIter;
1371  }
1372  return NULL;
1373 }
1374 
1388 static xmlNode *
1389 __xml_find_path(xmlNode *top, const char *key, int target_position)
1390 {
1391  xmlNode *target = (xmlNode*) top->doc;
1392  const char *current = key;
1393  char *section;
1394  char *remainder;
1395  char *id;
1396  char *tag;
1397  char *path = NULL;
1398  int rc;
1399  size_t key_len;
1400 
1401  CRM_CHECK(key != NULL, return NULL);
1402  key_len = strlen(key);
1403 
1404  /* These are scanned from key after a slash, so they can't be bigger
1405  * than key_len - 1 characters plus a null terminator.
1406  */
1407 
1408  remainder = calloc(key_len, sizeof(char));
1409  CRM_ASSERT(remainder != NULL);
1410 
1411  section = calloc(key_len, sizeof(char));
1412  CRM_ASSERT(section != NULL);
1413 
1414  id = calloc(key_len, sizeof(char));
1415  CRM_ASSERT(id != NULL);
1416 
1417  tag = calloc(key_len, sizeof(char));
1418  CRM_ASSERT(tag != NULL);
1419 
1420  do {
1421  // Look for /NEXT_COMPONENT/REMAINING_COMPONENTS
1422  rc = sscanf(current, "/%[^/]%s", section, remainder);
1423  if (rc > 0) {
1424  // Separate FIRST_COMPONENT into TAG[@id='ID']
1425  int f = sscanf(section, "%[^[][@id='%[^']", tag, id);
1426  int current_position = -1;
1427 
1428  /* The target position is for the final component tag, so only use
1429  * it if there is nothing left to search after this component.
1430  */
1431  if ((rc == 1) && (target_position >= 0)) {
1432  current_position = target_position;
1433  }
1434 
1435  switch (f) {
1436  case 1:
1437  target = __first_xml_child_match(target, tag, NULL, current_position);
1438  break;
1439  case 2:
1440  target = __first_xml_child_match(target, tag, id, current_position);
1441  break;
1442  default:
1443  // This should not be possible
1444  target = NULL;
1445  break;
1446  }
1447  current = remainder;
1448  }
1449 
1450  // Continue if something remains to search, and we've matched so far
1451  } while ((rc == 2) && target);
1452 
1453  if (target) {
1454  crm_trace("Found %s for %s",
1455  (path = (char *) xmlGetNodePath(target)), key);
1456  free(path);
1457  } else {
1458  crm_debug("No match for %s", key);
1459  }
1460 
1461  free(remainder);
1462  free(section);
1463  free(tag);
1464  free(id);
1465  return target;
1466 }
1467 
1468 static int
1469 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
1470 {
1471  int rc = pcmk_ok;
1472  xmlNode *change = NULL;
1473  for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
1474  xmlNode *match = NULL;
1475  const char *op = crm_element_value(change, XML_DIFF_OP);
1476  const char *xpath = crm_element_value(change, XML_DIFF_PATH);
1477  int position = -1;
1478 
1479  crm_trace("Processing %s %s", change->name, op);
1480  if(op == NULL) {
1481  continue;
1482  }
1483 
1484  if(strcmp(op, "delete") == 0) {
1485  crm_element_value_int(change, XML_DIFF_POSITION, &position);
1486  }
1487  match = __xml_find_path(xml, xpath, position);
1488  crm_trace("Performing %s on %s with %p", op, xpath, match);
1489 
1490  if(match == NULL && strcmp(op, "delete") == 0) {
1491  crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
1492  continue;
1493 
1494  } else if(match == NULL) {
1495  crm_err("No %s match for %s in %p", op, xpath, xml->doc);
1496  rc = -pcmk_err_diff_failed;
1497  continue;
1498 
1499  } else if(strcmp(op, "create") == 0) {
1500  int position = 0;
1501  xmlNode *child = NULL;
1502  xmlNode *match_child = NULL;
1503 
1504  match_child = match->children;
1505  crm_element_value_int(change, XML_DIFF_POSITION, &position);
1506 
1507  while(match_child && position != __xml_offset(match_child)) {
1508  match_child = match_child->next;
1509  }
1510 
1511  child = xmlDocCopyNode(change->children, match->doc, 1);
1512  if(match_child) {
1513  crm_trace("Adding %s at position %d", child->name, position);
1514  xmlAddPrevSibling(match_child, child);
1515 
1516  } else if(match->last) { /* Add to the end */
1517  crm_trace("Adding %s at position %d (end)", child->name, position);
1518  xmlAddNextSibling(match->last, child);
1519 
1520  } else {
1521  crm_trace("Adding %s at position %d (first)", child->name, position);
1522  CRM_LOG_ASSERT(position == 0);
1523  xmlAddChild(match, child);
1524  }
1525  crm_node_created(child);
1526 
1527  } else if(strcmp(op, "move") == 0) {
1528  int position = 0;
1529 
1530  crm_element_value_int(change, XML_DIFF_POSITION, &position);
1531  if(position != __xml_offset(match)) {
1532  xmlNode *match_child = NULL;
1533  int p = position;
1534 
1535  if(p > __xml_offset(match)) {
1536  p++; /* Skip ourselves */
1537  }
1538 
1539  CRM_ASSERT(match->parent != NULL);
1540  match_child = match->parent->children;
1541 
1542  while(match_child && p != __xml_offset(match_child)) {
1543  match_child = match_child->next;
1544  }
1545 
1546  crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
1547  match->name, position, __xml_offset(match), match->prev,
1548  match_child?"next":"last", match_child?match_child:match->parent->last);
1549 
1550  if(match_child) {
1551  xmlAddPrevSibling(match_child, match);
1552 
1553  } else {
1554  CRM_ASSERT(match->parent->last != NULL);
1555  xmlAddNextSibling(match->parent->last, match);
1556  }
1557 
1558  } else {
1559  crm_trace("%s is already in position %d", match->name, position);
1560  }
1561 
1562  if(position != __xml_offset(match)) {
1563  crm_err("Moved %s.%s to position %d instead of %d (%p)",
1564  match->name, ID(match), __xml_offset(match), position, match->prev);
1565  rc = -pcmk_err_diff_failed;
1566  }
1567 
1568  } else if(strcmp(op, "delete") == 0) {
1569  free_xml(match);
1570 
1571  } else if(strcmp(op, "modify") == 0) {
1572  xmlAttr *pIter = pcmk__first_xml_attr(match);
1573  xmlNode *attrs = __xml_first_child(first_named_child(change, XML_DIFF_RESULT));
1574 
1575  if(attrs == NULL) {
1576  rc = -ENOMSG;
1577  continue;
1578  }
1579  while(pIter != NULL) {
1580  const char *name = (const char *)pIter->name;
1581 
1582  pIter = pIter->next;
1583  xml_remove_prop(match, name);
1584  }
1585 
1586  for (pIter = pcmk__first_xml_attr(attrs); pIter != NULL; pIter = pIter->next) {
1587  const char *name = (const char *)pIter->name;
1588  const char *value = crm_element_value(attrs, name);
1589 
1590  crm_xml_add(match, name, value);
1591  }
1592 
1593  } else {
1594  crm_err("Unknown operation: %s", op);
1595  }
1596  }
1597  return rc;
1598 }
1599 
1600 int
1601 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
1602 {
1603  int format = 1;
1604  int rc = pcmk_ok;
1605  xmlNode *old = NULL;
1606  const char *digest = crm_element_value(patchset, XML_ATTR_DIGEST);
1607 
1608  if(patchset == NULL) {
1609  return rc;
1610  }
1611 
1612  xml_log_patchset(LOG_TRACE, __FUNCTION__, patchset);
1613 
1614  crm_element_value_int(patchset, "format", &format);
1615  if(check_version) {
1616  rc = xml_patch_version_check(xml, patchset, format);
1617  if(rc != pcmk_ok) {
1618  return rc;
1619  }
1620  }
1621 
1622  if(digest) {
1623  /* Make it available for logging if the result doesn't have the expected digest */
1624  old = copy_xml(xml);
1625  }
1626 
1627  if(rc == pcmk_ok) {
1628  switch(format) {
1629  case 1:
1630  rc = xml_apply_patchset_v1(xml, patchset);
1631  break;
1632  case 2:
1633  rc = xml_apply_patchset_v2(xml, patchset);
1634  break;
1635  default:
1636  crm_err("Unknown patch format: %d", format);
1637  rc = -EINVAL;
1638  }
1639  }
1640 
1641  if(rc == pcmk_ok && digest) {
1642  static struct qb_log_callsite *digest_cs = NULL;
1643 
1644  char *new_digest = NULL;
1645  char *version = crm_element_value_copy(xml, XML_ATTR_CRM_VERSION);
1646 
1647  if (digest_cs == NULL) {
1648  digest_cs =
1649  qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
1651  }
1652 
1653  new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
1654  if (safe_str_neq(new_digest, digest)) {
1655  crm_info("v%d digest mis-match: expected %s, calculated %s", format, digest, new_digest);
1656  rc = -pcmk_err_diff_failed;
1657 
1658  if (digest_cs && digest_cs->targets) {
1659  save_xml_to_file(old, "PatchDigest:input", NULL);
1660  save_xml_to_file(xml, "PatchDigest:result", NULL);
1661  save_xml_to_file(patchset,"PatchDigest:diff", NULL);
1662 
1663  } else {
1664  crm_trace("%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
1665  }
1666 
1667  } else {
1668  crm_trace("v%d digest matched: expected %s, calculated %s", format, digest, new_digest);
1669  }
1670  free(new_digest);
1671  free(version);
1672  }
1673  free_xml(old);
1674  return rc;
1675 }
1676 
1677 xmlNode *
1678 find_xml_node(xmlNode * root, const char *search_path, gboolean must_find)
1679 {
1680  xmlNode *a_child = NULL;
1681  const char *name = "NULL";
1682 
1683  if (root != NULL) {
1684  name = crm_element_name(root);
1685  }
1686 
1687  if (search_path == NULL) {
1688  crm_warn("Will never find <NULL>");
1689  return NULL;
1690  }
1691 
1692  for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
1693  if (strcmp((const char *)a_child->name, search_path) == 0) {
1694 /* crm_trace("returning node (%s).", crm_element_name(a_child)); */
1695  return a_child;
1696  }
1697  }
1698 
1699  if (must_find) {
1700  crm_warn("Could not find %s in %s.", search_path, name);
1701  } else if (root != NULL) {
1702  crm_trace("Could not find %s in %s.", search_path, name);
1703  } else {
1704  crm_trace("Could not find %s in <NULL>.", search_path);
1705  }
1706 
1707  return NULL;
1708 }
1709 
1710 /* As the name suggests, the perfect match is required for both node
1711  name and fully specified attribute, otherwise, when attribute not
1712  specified, the outcome is the first node matching on the name. */
1713 static xmlNode *
1714 find_entity_by_attr_or_just_name(xmlNode *parent, const char *node_name,
1715  const char *attr_n, const char *attr_v)
1716 {
1717  xmlNode *child;
1718 
1719  /* ensure attr_v specified when attr_n is */
1720  CRM_CHECK(attr_n == NULL || attr_v != NULL, return NULL);
1721 
1722  for (child = __xml_first_child(parent); child != NULL; child = __xml_next(child)) {
1723  /* XXX uncertain if the first check is strictly necessary here */
1724  if (node_name == NULL || !strcmp((const char *) child->name, node_name)) {
1725  if (attr_n == NULL
1726  || crm_str_eq(crm_element_value(child, attr_n), attr_v, TRUE)) {
1727  return child;
1728  }
1729  }
1730  }
1731 
1732  crm_trace("node <%s%s%s%s%s> not found in %s", crm_str(node_name),
1733  attr_n ? " " : "",
1734  attr_n ? attr_n : "",
1735  attr_n ? "=" : "",
1736  attr_n ? attr_v : "",
1737  crm_element_name(parent));
1738 
1739  return NULL;
1740 }
1741 
1742 xmlNode *
1743 find_entity(xmlNode *parent, const char *node_name, const char *id)
1744 {
1745  return find_entity_by_attr_or_just_name(parent, node_name,
1746  (id == NULL) ? id : XML_ATTR_ID, id);
1747 }
1748 
1749 void
1750 copy_in_properties(xmlNode * target, xmlNode * src)
1751 {
1752  if (src == NULL) {
1753  crm_warn("No node to copy properties from");
1754 
1755  } else if (target == NULL) {
1756  crm_err("No node to copy properties into");
1757 
1758  } else {
1759  xmlAttrPtr pIter = NULL;
1760 
1761  for (pIter = pcmk__first_xml_attr(src); pIter != NULL; pIter = pIter->next) {
1762  const char *p_name = (const char *)pIter->name;
1763  const char *p_value = pcmk__xml_attr_value(pIter);
1764 
1765  expand_plus_plus(target, p_name, p_value);
1766  }
1767  }
1768 
1769  return;
1770 }
1771 
1772 void
1773 fix_plus_plus_recursive(xmlNode * target)
1774 {
1775  /* TODO: Remove recursion and use xpath searches for value++ */
1776  xmlNode *child = NULL;
1777  xmlAttrPtr pIter = NULL;
1778 
1779  for (pIter = pcmk__first_xml_attr(target); pIter != NULL; pIter = pIter->next) {
1780  const char *p_name = (const char *)pIter->name;
1781  const char *p_value = pcmk__xml_attr_value(pIter);
1782 
1783  expand_plus_plus(target, p_name, p_value);
1784  }
1785  for (child = __xml_first_child(target); child != NULL; child = __xml_next(child)) {
1786  fix_plus_plus_recursive(child);
1787  }
1788 }
1789 
1790 void
1791 expand_plus_plus(xmlNode * target, const char *name, const char *value)
1792 {
1793  int offset = 1;
1794  int name_len = 0;
1795  int int_value = 0;
1796  int value_len = 0;
1797 
1798  const char *old_value = NULL;
1799 
1800  if (value == NULL || name == NULL) {
1801  return;
1802  }
1803 
1804  old_value = crm_element_value(target, name);
1805 
1806  if (old_value == NULL) {
1807  /* if no previous value, set unexpanded */
1808  goto set_unexpanded;
1809 
1810  } else if (strstr(value, name) != value) {
1811  goto set_unexpanded;
1812  }
1813 
1814  name_len = strlen(name);
1815  value_len = strlen(value);
1816  if (value_len < (name_len + 2)
1817  || value[name_len] != '+' || (value[name_len + 1] != '+' && value[name_len + 1] != '=')) {
1818  goto set_unexpanded;
1819  }
1820 
1821  /* if we are expanding ourselves,
1822  * then no previous value was set and leave int_value as 0
1823  */
1824  if (old_value != value) {
1825  int_value = char2score(old_value);
1826  }
1827 
1828  if (value[name_len + 1] != '+') {
1829  const char *offset_s = value + (name_len + 2);
1830 
1831  offset = char2score(offset_s);
1832  }
1833  int_value += offset;
1834 
1835  if (int_value > INFINITY) {
1836  int_value = (int)INFINITY;
1837  }
1838 
1839  crm_xml_add_int(target, name, int_value);
1840  return;
1841 
1842  set_unexpanded:
1843  if (old_value == value) {
1844  /* the old value is already set, nothing to do */
1845  return;
1846  }
1847  crm_xml_add(target, name, value);
1848  return;
1849 }
1850 
1851 xmlDoc *
1852 getDocPtr(xmlNode * node)
1853 {
1854  xmlDoc *doc = NULL;
1855 
1856  CRM_CHECK(node != NULL, return NULL);
1857 
1858  doc = node->doc;
1859  if (doc == NULL) {
1860  doc = xmlNewDoc((pcmkXmlStr) "1.0");
1861  xmlDocSetRootElement(doc, node);
1862  xmlSetTreeDoc(node, doc);
1863  }
1864  return doc;
1865 }
1866 
1867 xmlNode *
1868 add_node_copy(xmlNode * parent, xmlNode * src_node)
1869 {
1870  xmlNode *child = NULL;
1871  xmlDoc *doc = getDocPtr(parent);
1872 
1873  CRM_CHECK(src_node != NULL, return NULL);
1874 
1875  child = xmlDocCopyNode(src_node, doc, 1);
1876  xmlAddChild(parent, child);
1877  crm_node_created(child);
1878  return child;
1879 }
1880 
1881 int
1882 add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child)
1883 {
1884  add_node_copy(parent, child);
1885  free_xml(child);
1886  return 1;
1887 }
1888 
1889 xmlNode *
1890 create_xml_node(xmlNode * parent, const char *name)
1891 {
1892  xmlDoc *doc = NULL;
1893  xmlNode *node = NULL;
1894 
1895  if (name == NULL || name[0] == 0) {
1896  CRM_CHECK(name != NULL && name[0] == 0, return NULL);
1897  return NULL;
1898  }
1899 
1900  if (parent == NULL) {
1901  doc = xmlNewDoc((pcmkXmlStr) "1.0");
1902  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
1903  xmlDocSetRootElement(doc, node);
1904 
1905  } else {
1906  doc = getDocPtr(parent);
1907  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
1908  xmlAddChild(parent, node);
1909  }
1910  crm_node_created(node);
1911  return node;
1912 }
1913 
1914 int
1915 pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer,
1916  int offset, size_t buffer_size)
1917 {
1918  const char *id = ID(xml);
1919 
1920  if(offset == 0 && prefix == NULL && xml->parent) {
1921  offset = pcmk__element_xpath(NULL, xml->parent, buffer, offset,
1922  buffer_size);
1923  }
1924 
1925  if(id) {
1926  offset += snprintf(buffer + offset, buffer_size - offset,
1927  "/%s[@id='%s']", (const char *) xml->name, id);
1928  } else if(xml->name) {
1929  offset += snprintf(buffer + offset, buffer_size - offset,
1930  "/%s", (const char *) xml->name);
1931  }
1932 
1933  return offset;
1934 }
1935 
1936 char *
1937 xml_get_path(xmlNode *xml)
1938 {
1939  int offset = 0;
1940  char buffer[XML_BUFFER_SIZE];
1941 
1942  if (pcmk__element_xpath(NULL, xml, buffer, offset, sizeof(buffer)) > 0) {
1943  return strdup(buffer);
1944  }
1945  return NULL;
1946 }
1947 
1948 static void
1949 free_xml_with_position(xmlNode * child, int position)
1950 {
1951  if (child != NULL) {
1952  xmlNode *top = NULL;
1953  xmlDoc *doc = child->doc;
1954  xml_private_t *p = child->_private;
1955 
1956  if (doc != NULL) {
1957  top = xmlDocGetRootElement(doc);
1958  }
1959 
1960  if (doc != NULL && top == child) {
1961  /* Free everything */
1962  xmlFreeDoc(doc);
1963 
1964  } else if (pcmk__check_acl(child, NULL, xpf_acl_write) == FALSE) {
1965  int offset = 0;
1966  char buffer[XML_BUFFER_SIZE];
1967 
1968  pcmk__element_xpath(NULL, child, buffer, offset, sizeof(buffer));
1969  crm_trace("Cannot remove %s %x", buffer, p->flags);
1970  return;
1971 
1972  } else {
1973  if (doc && pcmk__tracking_xml_changes(child, FALSE)
1974  && is_not_set(p->flags, xpf_created)) {
1975  int offset = 0;
1976  char buffer[XML_BUFFER_SIZE];
1977 
1978  if (pcmk__element_xpath(NULL, child, buffer, offset,
1979  sizeof(buffer)) > 0) {
1980  xml_deleted_obj_t *deleted_obj = calloc(1, sizeof(xml_deleted_obj_t));
1981 
1982  crm_trace("Deleting %s %p from %p", buffer, child, doc);
1983 
1984  deleted_obj->path = strdup(buffer);
1985 
1986  deleted_obj->position = -1;
1987  /* Record the "position" only for XML comments for now */
1988  if (child->type == XML_COMMENT_NODE) {
1989  if (position >= 0) {
1990  deleted_obj->position = position;
1991 
1992  } else {
1993  deleted_obj->position = __xml_offset(child);
1994  }
1995  }
1996 
1997  p = doc->_private;
1998  p->deleted_objs = g_list_append(p->deleted_objs, deleted_obj);
1999  pcmk__set_xml_flag(child, xpf_dirty);
2000  }
2001  }
2002 
2003  /* Free this particular subtree
2004  * Make sure to unlink it from the parent first
2005  */
2006  xmlUnlinkNode(child);
2007  xmlFreeNode(child);
2008  }
2009  }
2010 }
2011 
2012 
2013 void
2014 free_xml(xmlNode * child)
2015 {
2016  free_xml_with_position(child, -1);
2017 }
2018 
2019 xmlNode *
2020 copy_xml(xmlNode * src)
2021 {
2022  xmlDoc *doc = xmlNewDoc((pcmkXmlStr) "1.0");
2023  xmlNode *copy = xmlDocCopyNode(src, doc, 1);
2024 
2025  xmlDocSetRootElement(doc, copy);
2026  xmlSetTreeDoc(copy, doc);
2027  return copy;
2028 }
2029 
2030 static void
2031 crm_xml_err(void *ctx, const char *fmt, ...)
2032 G_GNUC_PRINTF(2, 3);
2033 
2034 static void
2035 crm_xml_err(void *ctx, const char *fmt, ...)
2036 {
2037  va_list ap;
2038  static struct qb_log_callsite *xml_error_cs = NULL;
2039 
2040  if (xml_error_cs == NULL) {
2041  xml_error_cs = qb_log_callsite_get(
2042  __func__, __FILE__, "xml library error", LOG_TRACE, __LINE__, crm_trace_nonlog);
2043  }
2044 
2045  va_start(ap, fmt);
2046  if (xml_error_cs && xml_error_cs->targets) {
2047  CRM_XML_LOG_BASE(LOG_ERR, TRUE,
2048  crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, "xml library error",
2049  TRUE, TRUE),
2050  "XML Error: ", fmt, ap);
2051  } else {
2052  CRM_XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
2053  }
2054  va_end(ap);
2055 }
2056 
2057 xmlNode *
2058 string2xml(const char *input)
2059 {
2060  xmlNode *xml = NULL;
2061  xmlDocPtr output = NULL;
2062  xmlParserCtxtPtr ctxt = NULL;
2063  xmlErrorPtr last_error = NULL;
2064 
2065  if (input == NULL) {
2066  crm_err("Can't parse NULL input");
2067  return NULL;
2068  }
2069 
2070  /* create a parser context */
2071  ctxt = xmlNewParserCtxt();
2072  CRM_CHECK(ctxt != NULL, return NULL);
2073 
2074  /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
2075 
2076  xmlCtxtResetLastError(ctxt);
2077  xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2078  /* initGenericErrorDefaultFunc(crm_xml_err); */
2079  output =
2080  xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
2081  XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
2082  if (output) {
2083  xml = xmlDocGetRootElement(output);
2084  }
2085  last_error = xmlCtxtGetLastError(ctxt);
2086  if (last_error && last_error->code != XML_ERR_OK) {
2087  /* crm_abort(__FILE__,__FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
2088  /*
2089  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
2090  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
2091  */
2092  crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
2093  last_error->domain, last_error->level, last_error->code, last_error->message);
2094 
2095  if (last_error->code == XML_ERR_DOCUMENT_EMPTY) {
2096  CRM_LOG_ASSERT("Cannot parse an empty string");
2097 
2098  } else if (last_error->code != XML_ERR_DOCUMENT_END) {
2099  crm_err("Couldn't%s parse %d chars: %s", xml ? " fully" : "", (int)strlen(input),
2100  input);
2101  if (xml != NULL) {
2102  crm_log_xml_err(xml, "Partial");
2103  }
2104 
2105  } else {
2106  int len = strlen(input);
2107  int lpc = 0;
2108 
2109  while(lpc < len) {
2110  crm_warn("Parse error[+%.3d]: %.80s", lpc, input+lpc);
2111  lpc += 80;
2112  }
2113 
2114  CRM_LOG_ASSERT("String parsing error");
2115  }
2116  }
2117 
2118  xmlFreeParserCtxt(ctxt);
2119  return xml;
2120 }
2121 
2122 xmlNode *
2124 {
2125  size_t data_length = 0;
2126  size_t read_chars = 0;
2127 
2128  char *xml_buffer = NULL;
2129  xmlNode *xml_obj = NULL;
2130 
2131  do {
2132  xml_buffer = realloc_safe(xml_buffer, data_length + XML_BUFFER_SIZE);
2133  read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin);
2134  data_length += read_chars;
2135  } while (read_chars == XML_BUFFER_SIZE);
2136 
2137  if (data_length == 0) {
2138  crm_warn("No XML supplied on stdin");
2139  free(xml_buffer);
2140  return NULL;
2141  }
2142 
2143  xml_buffer[data_length] = '\0';
2144  xml_obj = string2xml(xml_buffer);
2145  free(xml_buffer);
2146 
2147  crm_log_xml_trace(xml_obj, "Created fragment");
2148  return xml_obj;
2149 }
2150 
2151 static char *
2152 decompress_file(const char *filename)
2153 {
2154  char *buffer = NULL;
2155 
2156 #if HAVE_BZLIB_H
2157  int rc = 0;
2158  size_t length = 0, read_len = 0;
2159 
2160  BZFILE *bz_file = NULL;
2161  FILE *input = fopen(filename, "r");
2162 
2163  if (input == NULL) {
2164  crm_perror(LOG_ERR, "Could not open %s for reading", filename);
2165  return NULL;
2166  }
2167 
2168  bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
2169  if (rc != BZ_OK) {
2170  crm_err("Could not prepare to read compressed %s: %s "
2171  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
2172  BZ2_bzReadClose(&rc, bz_file);
2173  return NULL;
2174  }
2175 
2176  rc = BZ_OK;
2177  while (rc == BZ_OK) {
2178  buffer = realloc_safe(buffer, XML_BUFFER_SIZE + length + 1);
2179  read_len = BZ2_bzRead(&rc, bz_file, buffer + length, XML_BUFFER_SIZE);
2180 
2181  crm_trace("Read %ld bytes from file: %d", (long)read_len, rc);
2182 
2183  if (rc == BZ_OK || rc == BZ_STREAM_END) {
2184  length += read_len;
2185  }
2186  }
2187 
2188  buffer[length] = '\0';
2189 
2190  if (rc != BZ_STREAM_END) {
2191  crm_err("Could not read compressed %s: %s "
2192  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
2193  free(buffer);
2194  buffer = NULL;
2195  }
2196 
2197  BZ2_bzReadClose(&rc, bz_file);
2198  fclose(input);
2199 
2200 #else
2201  crm_err("Could not read compressed %s: not built with bzlib support",
2202  filename);
2203 #endif
2204  return buffer;
2205 }
2206 
2207 void
2208 strip_text_nodes(xmlNode * xml)
2209 {
2210  xmlNode *iter = xml->children;
2211 
2212  while (iter) {
2213  xmlNode *next = iter->next;
2214 
2215  switch (iter->type) {
2216  case XML_TEXT_NODE:
2217  /* Remove it */
2218  xmlUnlinkNode(iter);
2219  xmlFreeNode(iter);
2220  break;
2221 
2222  case XML_ELEMENT_NODE:
2223  /* Search it */
2224  strip_text_nodes(iter);
2225  break;
2226 
2227  default:
2228  /* Leave it */
2229  break;
2230  }
2231 
2232  iter = next;
2233  }
2234 }
2235 
2236 xmlNode *
2237 filename2xml(const char *filename)
2238 {
2239  xmlNode *xml = NULL;
2240  xmlDocPtr output = NULL;
2241  gboolean uncompressed = TRUE;
2242  xmlParserCtxtPtr ctxt = NULL;
2243  xmlErrorPtr last_error = NULL;
2244  static int xml_options = XML_PARSE_NOBLANKS | XML_PARSE_RECOVER;
2245 
2246  /* create a parser context */
2247  ctxt = xmlNewParserCtxt();
2248  CRM_CHECK(ctxt != NULL, return NULL);
2249 
2250  /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
2251 
2252  xmlCtxtResetLastError(ctxt);
2253  xmlSetGenericErrorFunc(ctxt, crm_xml_err);
2254  /* initGenericErrorDefaultFunc(crm_xml_err); */
2255 
2256  if (filename) {
2257  uncompressed = !crm_ends_with_ext(filename, ".bz2");
2258  }
2259 
2260  if (filename == NULL) {
2261  /* STDIN_FILENO == fileno(stdin) */
2262  output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, xml_options);
2263 
2264  } else if (uncompressed) {
2265  output = xmlCtxtReadFile(ctxt, filename, NULL, xml_options);
2266 
2267  } else {
2268  char *input = decompress_file(filename);
2269 
2270  output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
2271  xml_options);
2272  free(input);
2273  }
2274 
2275  if (output && (xml = xmlDocGetRootElement(output))) {
2276  strip_text_nodes(xml);
2277  }
2278 
2279  last_error = xmlCtxtGetLastError(ctxt);
2280  if (last_error && last_error->code != XML_ERR_OK) {
2281  /* crm_abort(__FILE__,__FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
2282  /*
2283  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
2284  * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
2285  */
2286  crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
2287  last_error->domain, last_error->level, last_error->code, last_error->message);
2288 
2289  if (last_error && last_error->code != XML_ERR_OK) {
2290  crm_err("Couldn't%s parse %s", xml ? " fully" : "", filename);
2291  if (xml != NULL) {
2292  crm_log_xml_err(xml, "Partial");
2293  }
2294  }
2295  }
2296 
2297  xmlFreeParserCtxt(ctxt);
2298  return xml;
2299 }
2300 
2309 const char *
2310 crm_xml_add_last_written(xmlNode *xml_node)
2311 {
2312  time_t now = time(NULL);
2313  char *now_str = ctime(&now);
2314 
2315  now_str[24] = EOS; /* replace the newline */
2316  return crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str);
2317 }
2318 
2324 void
2326 {
2327  char *c;
2328 
2329  for (c = id; *c; ++c) {
2330  /* @TODO Sanitize more comprehensively */
2331  switch (*c) {
2332  case ':':
2333  case '#':
2334  *c = '.';
2335  }
2336  }
2337 }
2338 
2346 void
2347 crm_xml_set_id(xmlNode *xml, const char *format, ...)
2348 {
2349  va_list ap;
2350  int len = 0;
2351  char *id = NULL;
2352 
2353  /* equivalent to crm_strdup_printf() */
2354  va_start(ap, format);
2355  len = vasprintf(&id, format, ap);
2356  va_end(ap);
2357  CRM_ASSERT(len > 0);
2358 
2359  crm_xml_sanitize_id(id);
2360  crm_xml_add(xml, XML_ATTR_ID, id);
2361  free(id);
2362 }
2363 
2375 static int
2376 write_xml_stream(xmlNode * xml_node, const char *filename, FILE * stream, gboolean compress)
2377 {
2378  int res = 0;
2379  char *buffer = NULL;
2380  unsigned int out = 0;
2381 
2382  crm_log_xml_trace(xml_node, "writing");
2383 
2384  buffer = dump_xml_formatted(xml_node);
2385  CRM_CHECK(buffer && strlen(buffer),
2386  crm_log_xml_warn(xml_node, "formatting failed");
2387  res = -pcmk_err_generic;
2388  goto bail);
2389 
2390  if (compress) {
2391 #if HAVE_BZLIB_H
2392  int rc = BZ_OK;
2393  unsigned int in = 0;
2394  BZFILE *bz_file = NULL;
2395 
2396  bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
2397  if (rc != BZ_OK) {
2398  crm_warn("Not compressing %s: could not prepare file stream: %s "
2399  CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
2400  } else {
2401  BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
2402  if (rc != BZ_OK) {
2403  crm_warn("Not compressing %s: could not compress data: %s "
2404  CRM_XS " bzerror=%d errno=%d",
2405  filename, bz2_strerror(rc), rc, errno);
2406  }
2407  }
2408 
2409  if (rc == BZ_OK) {
2410  BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
2411  if (rc != BZ_OK) {
2412  crm_warn("Not compressing %s: could not write compressed data: %s "
2413  CRM_XS " bzerror=%d errno=%d",
2414  filename, bz2_strerror(rc), rc, errno);
2415  out = 0; // retry without compression
2416  } else {
2417  res = (int) out;
2418  crm_trace("Compressed XML for %s from %u bytes to %u",
2419  filename, in, out);
2420  }
2421  }
2422 #else
2423  crm_warn("Not compressing %s: not built with bzlib support", filename);
2424 #endif
2425  }
2426 
2427  if (out == 0) {
2428  res = fprintf(stream, "%s", buffer);
2429  if (res < 0) {
2430  res = -errno;
2431  crm_perror(LOG_ERR, "writing %s", filename);
2432  goto bail;
2433  }
2434  }
2435 
2436  bail:
2437 
2438  if (fflush(stream) != 0) {
2439  res = -errno;
2440  crm_perror(LOG_ERR, "flushing %s", filename);
2441  }
2442 
2443  /* Don't report error if the file does not support synchronization */
2444  if (fsync(fileno(stream)) < 0 && errno != EROFS && errno != EINVAL) {
2445  res = -errno;
2446  crm_perror(LOG_ERR, "synchronizing %s", filename);
2447  }
2448 
2449  fclose(stream);
2450 
2451  crm_trace("Saved %d bytes%s to %s as XML",
2452  res, ((out > 0)? " (compressed)" : ""), filename);
2453  free(buffer);
2454 
2455  return res;
2456 }
2457 
2468 int
2469 write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
2470 {
2471  FILE *stream = NULL;
2472 
2473  CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
2474  stream = fdopen(fd, "w");
2475  if (stream == NULL) {
2476  return -errno;
2477  }
2478  return write_xml_stream(xml_node, filename, stream, compress);
2479 }
2480 
2490 int
2491 write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
2492 {
2493  FILE *stream = NULL;
2494 
2495  CRM_CHECK(xml_node && filename, return -EINVAL);
2496  stream = fopen(filename, "w");
2497  if (stream == NULL) {
2498  return -errno;
2499  }
2500  return write_xml_stream(xml_node, filename, stream, compress);
2501 }
2502 
2503 xmlNode *
2504 get_message_xml(xmlNode * msg, const char *field)
2505 {
2506  xmlNode *tmp = first_named_child(msg, field);
2507 
2508  return __xml_first_child(tmp);
2509 }
2510 
2511 gboolean
2512 add_message_xml(xmlNode * msg, const char *field, xmlNode * xml)
2513 {
2514  xmlNode *holder = create_xml_node(msg, field);
2515 
2516  add_node_copy(holder, xml);
2517  return TRUE;
2518 }
2519 
2520 static char *
2521 crm_xml_escape_shuffle(char *text, int start, int *length, const char *replace)
2522 {
2523  int lpc;
2524  int offset = strlen(replace) - 1; /* We have space for 1 char already */
2525 
2526  *length += offset;
2527  text = realloc_safe(text, *length);
2528 
2529  for (lpc = (*length) - 1; lpc > (start + offset); lpc--) {
2530  text[lpc] = text[lpc - offset];
2531  }
2532 
2533  memcpy(text + start, replace, offset + 1);
2534  return text;
2535 }
2536 
2537 char *
2538 crm_xml_escape(const char *text)
2539 {
2540  int index;
2541  int changes = 0;
2542  int length = 1 + strlen(text);
2543  char *copy = strdup(text);
2544 
2545  /*
2546  * When xmlCtxtReadDoc() parses &lt; and friends in a
2547  * value, it converts them to their human readable
2548  * form.
2549  *
2550  * If one uses xmlNodeDump() to convert it back to a
2551  * string, all is well, because special characters are
2552  * converted back to their escape sequences.
2553  *
2554  * However xmlNodeDump() is randomly dog slow, even with the same
2555  * input. So we need to replicate the escaping in our custom
2556  * version so that the result can be re-parsed by xmlCtxtReadDoc()
2557  * when necessary.
2558  */
2559 
2560  for (index = 0; index < length; index++) {
2561  switch (copy[index]) {
2562  case 0:
2563  break;
2564  case '<':
2565  copy = crm_xml_escape_shuffle(copy, index, &length, "&lt;");
2566  changes++;
2567  break;
2568  case '>':
2569  copy = crm_xml_escape_shuffle(copy, index, &length, "&gt;");
2570  changes++;
2571  break;
2572  case '"':
2573  copy = crm_xml_escape_shuffle(copy, index, &length, "&quot;");
2574  changes++;
2575  break;
2576  case '\'':
2577  copy = crm_xml_escape_shuffle(copy, index, &length, "&apos;");
2578  changes++;
2579  break;
2580  case '&':
2581  copy = crm_xml_escape_shuffle(copy, index, &length, "&amp;");
2582  changes++;
2583  break;
2584  case '\t':
2585  /* Might as well just expand to a few spaces... */
2586  copy = crm_xml_escape_shuffle(copy, index, &length, " ");
2587  changes++;
2588  break;
2589  case '\n':
2590  /* crm_trace("Convert: \\%.3o", copy[index]); */
2591  copy = crm_xml_escape_shuffle(copy, index, &length, "\\n");
2592  changes++;
2593  break;
2594  case '\r':
2595  copy = crm_xml_escape_shuffle(copy, index, &length, "\\r");
2596  changes++;
2597  break;
2598  /* For debugging...
2599  case '\\':
2600  crm_trace("Passthrough: \\%c", copy[index+1]);
2601  break;
2602  */
2603  default:
2604  /* Check for and replace non-printing characters with their octal equivalent */
2605  if(copy[index] < ' ' || copy[index] > '~') {
2606  char *replace = crm_strdup_printf("\\%.3o", copy[index]);
2607 
2608  /* crm_trace("Convert to octal: \\%.3o", copy[index]); */
2609  copy = crm_xml_escape_shuffle(copy, index, &length, replace);
2610  free(replace);
2611  changes++;
2612  }
2613  }
2614  }
2615 
2616  if (changes) {
2617  crm_trace("Dumped '%s'", copy);
2618  }
2619  return copy;
2620 }
2621 
2622 static inline void
2623 dump_xml_attr(xmlAttrPtr attr, int options, char **buffer, int *offset, int *max)
2624 {
2625  char *p_value = NULL;
2626  const char *p_name = NULL;
2627  xml_private_t *p = NULL;
2628 
2629  CRM_ASSERT(buffer != NULL);
2630  if (attr == NULL || attr->children == NULL) {
2631  return;
2632  }
2633 
2634  p = attr->_private;
2635  if (p && is_set(p->flags, xpf_deleted)) {
2636  return;
2637  }
2638 
2639  p_name = (const char *)attr->name;
2640  p_value = crm_xml_escape((const char *)attr->children->content);
2641  buffer_print(*buffer, *max, *offset, " %s=\"%s\"", p_name, p_value);
2642  free(p_value);
2643 }
2644 
2645 static void
2646 __xml_log_element(int log_level, const char *file, const char *function, int line,
2647  const char *prefix, xmlNode * data, int depth, int options)
2648 {
2649  int max = 0;
2650  int offset = 0;
2651  const char *name = NULL;
2652  const char *hidden = NULL;
2653 
2654  xmlNode *child = NULL;
2655  xmlAttrPtr pIter = NULL;
2656 
2657  if(data == NULL) {
2658  return;
2659  }
2660 
2661  name = crm_element_name(data);
2662 
2663  if(is_set(options, xml_log_option_open)) {
2664  char *buffer = NULL;
2665 
2666  insert_prefix(options, &buffer, &offset, &max, depth);
2667 
2668  if (data->type == XML_COMMENT_NODE) {
2669  buffer_print(buffer, max, offset, "<!--%s-->", data->content);
2670 
2671  } else {
2672  buffer_print(buffer, max, offset, "<%s", name);
2673 
2674  hidden = crm_element_value(data, "hidden");
2675  for (pIter = pcmk__first_xml_attr(data); pIter != NULL; pIter = pIter->next) {
2676  xml_private_t *p = pIter->_private;
2677  const char *p_name = (const char *)pIter->name;
2678  const char *p_value = pcmk__xml_attr_value(pIter);
2679  char *p_copy = NULL;
2680 
2681  if(is_set(p->flags, xpf_deleted)) {
2682  continue;
2683  } else if ((is_set(options, xml_log_option_diff_plus)
2684  || is_set(options, xml_log_option_diff_minus))
2685  && strcmp(XML_DIFF_MARKER, p_name) == 0) {
2686  continue;
2687 
2688  } else if (hidden != NULL && p_name[0] != 0 && strstr(hidden, p_name) != NULL) {
2689  p_copy = strdup("*****");
2690 
2691  } else {
2692  p_copy = crm_xml_escape(p_value);
2693  }
2694 
2695  buffer_print(buffer, max, offset, " %s=\"%s\"", p_name, p_copy);
2696  free(p_copy);
2697  }
2698 
2699  if(xml_has_children(data) == FALSE) {
2700  buffer_print(buffer, max, offset, "/>");
2701 
2702  } else if(is_set(options, xml_log_option_children)) {
2703  buffer_print(buffer, max, offset, ">");
2704 
2705  } else {
2706  buffer_print(buffer, max, offset, "/>");
2707  }
2708  }
2709 
2710  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
2711  free(buffer);
2712  }
2713 
2714  if(data->type == XML_COMMENT_NODE) {
2715  return;
2716 
2717  } else if(xml_has_children(data) == FALSE) {
2718  return;
2719 
2720  } else if(is_set(options, xml_log_option_children)) {
2721  offset = 0;
2722  max = 0;
2723 
2724  for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
2725  __xml_log_element(log_level, file, function, line, prefix, child, depth + 1, options|xml_log_option_open|xml_log_option_close);
2726  }
2727  }
2728 
2729  if(is_set(options, xml_log_option_close)) {
2730  char *buffer = NULL;
2731 
2732  insert_prefix(options, &buffer, &offset, &max, depth);
2733  buffer_print(buffer, max, offset, "</%s>", name);
2734 
2735  do_crm_log_alias(log_level, file, function, line, "%s %s", prefix, buffer);
2736  free(buffer);
2737  }
2738 }
2739 
2740 static void
2741 __xml_log_change_element(int log_level, const char *file, const char *function, int line,
2742  const char *prefix, xmlNode * data, int depth, int options)
2743 {
2744  xml_private_t *p;
2745  char *prefix_m = NULL;
2746  xmlNode *child = NULL;
2747  xmlAttrPtr pIter = NULL;
2748 
2749  if(data == NULL) {
2750  return;
2751  }
2752 
2753  p = data->_private;
2754 
2755  prefix_m = strdup(prefix);
2756  prefix_m[1] = '+';
2757 
2758  if(is_set(p->flags, xpf_dirty) && is_set(p->flags, xpf_created)) {
2759  /* Continue and log full subtree */
2760  __xml_log_element(log_level, file, function, line,
2761  prefix_m, data, depth, options|xml_log_option_open|xml_log_option_close|xml_log_option_children);
2762 
2763  } else if(is_set(p->flags, xpf_dirty)) {
2764  char *spaces = calloc(80, 1);
2765  int s_count = 0, s_max = 80;
2766  char *prefix_del = NULL;
2767  char *prefix_moved = NULL;
2768  const char *flags = prefix;
2769 
2770  insert_prefix(options, &spaces, &s_count, &s_max, depth);
2771  prefix_del = strdup(prefix);
2772  prefix_del[0] = '-';
2773  prefix_del[1] = '-';
2774  prefix_moved = strdup(prefix);
2775  prefix_moved[1] = '~';
2776 
2777  if(is_set(p->flags, xpf_moved)) {
2778  flags = prefix_moved;
2779  } else {
2780  flags = prefix;
2781  }
2782 
2783  __xml_log_element(log_level, file, function, line,
2784  flags, data, depth, options|xml_log_option_open);
2785 
2786  for (pIter = pcmk__first_xml_attr(data); pIter != NULL; pIter = pIter->next) {
2787  const char *aname = (const char*)pIter->name;
2788 
2789  p = pIter->_private;
2790  if(is_set(p->flags, xpf_deleted)) {
2791  const char *value = crm_element_value(data, aname);
2792  flags = prefix_del;
2793  do_crm_log_alias(log_level, file, function, line,
2794  "%s %s @%s=%s", flags, spaces, aname, value);
2795 
2796  } else if(is_set(p->flags, xpf_dirty)) {
2797  const char *value = crm_element_value(data, aname);
2798 
2799  if(is_set(p->flags, xpf_created)) {
2800  flags = prefix_m;
2801 
2802  } else if(is_set(p->flags, xpf_modified)) {
2803  flags = prefix;
2804 
2805  } else if(is_set(p->flags, xpf_moved)) {
2806  flags = prefix_moved;
2807 
2808  } else {
2809  flags = prefix;
2810  }
2811  do_crm_log_alias(log_level, file, function, line,
2812  "%s %s @%s=%s", flags, spaces, aname, value);
2813  }
2814  }
2815  free(prefix_moved);
2816  free(prefix_del);
2817  free(spaces);
2818 
2819  for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
2820  __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
2821  }
2822 
2823  __xml_log_element(log_level, file, function, line,
2824  prefix, data, depth, options|xml_log_option_close);
2825 
2826  } else {
2827  for (child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
2828  __xml_log_change_element(log_level, file, function, line, prefix, child, depth + 1, options);
2829  }
2830  }
2831 
2832  free(prefix_m);
2833 
2834 }
2835 
2836 void
2837 log_data_element(int log_level, const char *file, const char *function, int line,
2838  const char *prefix, xmlNode * data, int depth, int options)
2839 {
2840  xmlNode *a_child = NULL;
2841 
2842  char *prefix_m = NULL;
2843 
2844  if (prefix == NULL) {
2845  prefix = "";
2846  }
2847 
2848  /* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
2849  if (data == NULL) {
2850  do_crm_log_alias(log_level, file, function, line, "%s: %s", prefix,
2851  "No data to dump as XML");
2852  return;
2853  }
2854 
2855  if(is_set(options, xml_log_option_dirty_add) || is_set(options, xml_log_option_dirty_add)) {
2856  __xml_log_change_element(log_level, file, function, line, prefix, data, depth, options);
2857  return;
2858  }
2859 
2860  if (is_set(options, xml_log_option_formatted)) {
2861  if (is_set(options, xml_log_option_diff_plus)
2862  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
2863  options |= xml_log_option_diff_all;
2864  prefix_m = strdup(prefix);
2865  prefix_m[1] = '+';
2866  prefix = prefix_m;
2867 
2868  } else if (is_set(options, xml_log_option_diff_minus)
2869  && (data->children == NULL || crm_element_value(data, XML_DIFF_MARKER))) {
2870  options |= xml_log_option_diff_all;
2871  prefix_m = strdup(prefix);
2872  prefix_m[1] = '-';
2873  prefix = prefix_m;
2874  }
2875  }
2876 
2877  if (is_set(options, xml_log_option_diff_short)
2878  && is_not_set(options, xml_log_option_diff_all)) {
2879  /* Still searching for the actual change */
2880  for (a_child = __xml_first_child(data); a_child != NULL; a_child = __xml_next(a_child)) {
2881  log_data_element(log_level, file, function, line, prefix, a_child, depth + 1, options);
2882  }
2883  } else {
2884  __xml_log_element(log_level, file, function, line, prefix, data, depth,
2886  }
2887  free(prefix_m);
2888 }
2889 
2890 static void
2891 dump_filtered_xml(xmlNode * data, int options, char **buffer, int *offset, int *max)
2892 {
2893  int lpc;
2894  xmlAttrPtr xIter = NULL;
2895  static int filter_len = DIMOF(filter);
2896 
2897  for (lpc = 0; options && lpc < filter_len; lpc++) {
2898  filter[lpc].found = FALSE;
2899  }
2900 
2901  for (xIter = pcmk__first_xml_attr(data); xIter != NULL; xIter = xIter->next) {
2902  bool skip = FALSE;
2903  const char *p_name = (const char *)xIter->name;
2904 
2905  for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
2906  if (filter[lpc].found == FALSE && strcmp(p_name, filter[lpc].string) == 0) {
2907  filter[lpc].found = TRUE;
2908  skip = TRUE;
2909  break;
2910  }
2911  }
2912 
2913  if (skip == FALSE) {
2914  dump_xml_attr(xIter, options, buffer, offset, max);
2915  }
2916  }
2917 }
2918 
2919 static void
2920 dump_xml_element(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
2921 {
2922  const char *name = NULL;
2923 
2924  CRM_ASSERT(max != NULL);
2925  CRM_ASSERT(offset != NULL);
2926  CRM_ASSERT(buffer != NULL);
2927 
2928  if (data == NULL) {
2929  crm_trace("Nothing to dump");
2930  return;
2931  }
2932 
2933  if (*buffer == NULL) {
2934  *offset = 0;
2935  *max = 0;
2936  }
2937 
2938  name = crm_element_name(data);
2939  CRM_ASSERT(name != NULL);
2940 
2941  insert_prefix(options, buffer, offset, max, depth);
2942  buffer_print(*buffer, *max, *offset, "<%s", name);
2943 
2944  if (options & xml_log_option_filtered) {
2945  dump_filtered_xml(data, options, buffer, offset, max);
2946 
2947  } else {
2948  xmlAttrPtr xIter = NULL;
2949 
2950  for (xIter = pcmk__first_xml_attr(data); xIter != NULL; xIter = xIter->next) {
2951  dump_xml_attr(xIter, options, buffer, offset, max);
2952  }
2953  }
2954 
2955  if (data->children == NULL) {
2956  buffer_print(*buffer, *max, *offset, "/>");
2957 
2958  } else {
2959  buffer_print(*buffer, *max, *offset, ">");
2960  }
2961 
2962  if (options & xml_log_option_formatted) {
2963  buffer_print(*buffer, *max, *offset, "\n");
2964  }
2965 
2966  if (data->children) {
2967  xmlNode *xChild = NULL;
2968  for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
2969  crm_xml_dump(xChild, options, buffer, offset, max, depth + 1);
2970  }
2971 
2972  insert_prefix(options, buffer, offset, max, depth);
2973  buffer_print(*buffer, *max, *offset, "</%s>", name);
2974 
2975  if (options & xml_log_option_formatted) {
2976  buffer_print(*buffer, *max, *offset, "\n");
2977  }
2978  }
2979 }
2980 
2981 static void
2982 dump_xml_text(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
2983 {
2984  CRM_ASSERT(max != NULL);
2985  CRM_ASSERT(offset != NULL);
2986  CRM_ASSERT(buffer != NULL);
2987 
2988  if (data == NULL) {
2989  crm_trace("Nothing to dump");
2990  return;
2991  }
2992 
2993  if (*buffer == NULL) {
2994  *offset = 0;
2995  *max = 0;
2996  }
2997 
2998  insert_prefix(options, buffer, offset, max, depth);
2999 
3000  buffer_print(*buffer, *max, *offset, "%s", data->content);
3001 
3002  if (options & xml_log_option_formatted) {
3003  buffer_print(*buffer, *max, *offset, "\n");
3004  }
3005 }
3006 
3007 static void
3008 dump_xml_cdata(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3009 {
3010  CRM_ASSERT(max != NULL);
3011  CRM_ASSERT(offset != NULL);
3012  CRM_ASSERT(buffer != NULL);
3013 
3014  if (data == NULL) {
3015  crm_trace("Nothing to dump");
3016  return;
3017  }
3018 
3019  if (*buffer == NULL) {
3020  *offset = 0;
3021  *max = 0;
3022  }
3023 
3024  insert_prefix(options, buffer, offset, max, depth);
3025 
3026  buffer_print(*buffer, *max, *offset, "<![CDATA[");
3027  buffer_print(*buffer, *max, *offset, "%s", data->content);
3028  buffer_print(*buffer, *max, *offset, "]]>");
3029 
3030  if (options & xml_log_option_formatted) {
3031  buffer_print(*buffer, *max, *offset, "\n");
3032  }
3033 }
3034 
3035 
3036 static void
3037 dump_xml_comment(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3038 {
3039  CRM_ASSERT(max != NULL);
3040  CRM_ASSERT(offset != NULL);
3041  CRM_ASSERT(buffer != NULL);
3042 
3043  if (data == NULL) {
3044  crm_trace("Nothing to dump");
3045  return;
3046  }
3047 
3048  if (*buffer == NULL) {
3049  *offset = 0;
3050  *max = 0;
3051  }
3052 
3053  insert_prefix(options, buffer, offset, max, depth);
3054 
3055  buffer_print(*buffer, *max, *offset, "<!--");
3056  buffer_print(*buffer, *max, *offset, "%s", data->content);
3057  buffer_print(*buffer, *max, *offset, "-->");
3058 
3059  if (options & xml_log_option_formatted) {
3060  buffer_print(*buffer, *max, *offset, "\n");
3061  }
3062 }
3063 
3064 void
3065 crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth)
3066 {
3067  if(data == NULL) {
3068  *offset = 0;
3069  *max = 0;
3070  return;
3071  }
3072 #if 0
3073  if (is_not_set(options, xml_log_option_filtered)) {
3074  /* Turning this code on also changes the scheduler tests for some reason
3075  * (not just newlines). Figure out why before considering to
3076  * enable this permanently.
3077  *
3078  * It exists to help debug slowness in xmlNodeDump() and
3079  * potentially if we ever want to go back to it.
3080  *
3081  * In theory it's a good idea (reuse) but our custom version does
3082  * better for the filtered case and avoids the final strdup() for
3083  * everything
3084  */
3085 
3086  time_t now, next;
3087  xmlDoc *doc = NULL;
3088  xmlBuffer *xml_buffer = NULL;
3089 
3090  *buffer = NULL;
3091  doc = getDocPtr(data);
3092  /* doc will only be NULL if data is */
3093  CRM_CHECK(doc != NULL, return);
3094 
3095  now = time(NULL);
3096  xml_buffer = xmlBufferCreate();
3097  CRM_ASSERT(xml_buffer != NULL);
3098 
3099  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
3100  * realloc()s and it can take upwards of 18 seconds (yes, seconds)
3101  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
3102  * less than 1 second.
3103  *
3104  * We could also use xmlBufferCreateSize() to start with a
3105  * sane-ish initial size and avoid the first few doubles.
3106  */
3107  xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_DOUBLEIT);
3108 
3109  *max = xmlNodeDump(xml_buffer, doc, data, 0, (options & xml_log_option_formatted));
3110  if (*max > 0) {
3111  *buffer = strdup((char *)xml_buffer->content);
3112  }
3113 
3114  next = time(NULL);
3115  if ((now + 1) < next) {
3116  crm_log_xml_trace(data, "Long time");
3117  crm_err("xmlNodeDump() -> %dbytes took %ds", *max, next - now);
3118  }
3119 
3120  xmlBufferFree(xml_buffer);
3121  return;
3122  }
3123 #endif
3124 
3125  switch(data->type) {
3126  case XML_ELEMENT_NODE:
3127  /* Handle below */
3128  dump_xml_element(data, options, buffer, offset, max, depth);
3129  break;
3130  case XML_TEXT_NODE:
3131  /* if option xml_log_option_text is enabled, then dump XML_TEXT_NODE */
3132  if (options & xml_log_option_text) {
3133  dump_xml_text(data, options, buffer, offset, max, depth);
3134  }
3135  return;
3136  case XML_COMMENT_NODE:
3137  dump_xml_comment(data, options, buffer, offset, max, depth);
3138  break;
3139  case XML_CDATA_SECTION_NODE:
3140  dump_xml_cdata(data, options, buffer, offset, max, depth);
3141  break;
3142  default:
3143  crm_warn("Unhandled type: %d", data->type);
3144  return;
3145 
3146  /*
3147  XML_ATTRIBUTE_NODE = 2
3148  XML_ENTITY_REF_NODE = 5
3149  XML_ENTITY_NODE = 6
3150  XML_PI_NODE = 7
3151  XML_DOCUMENT_NODE = 9
3152  XML_DOCUMENT_TYPE_NODE = 10
3153  XML_DOCUMENT_FRAG_NODE = 11
3154  XML_NOTATION_NODE = 12
3155  XML_HTML_DOCUMENT_NODE = 13
3156  XML_DTD_NODE = 14
3157  XML_ELEMENT_DECL = 15
3158  XML_ATTRIBUTE_DECL = 16
3159  XML_ENTITY_DECL = 17
3160  XML_NAMESPACE_DECL = 18
3161  XML_XINCLUDE_START = 19
3162  XML_XINCLUDE_END = 20
3163  XML_DOCB_DOCUMENT_NODE = 21
3164  */
3165  }
3166 
3167 }
3168 
3169 void
3170 crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
3171 {
3172  buffer_print(*buffer, *max, *offset, "%c", c);
3173 }
3174 
3175 char *
3176 dump_xml_formatted_with_text(xmlNode * an_xml_node)
3177 {
3178  char *buffer = NULL;
3179  int offset = 0, max = 0;
3180 
3181  crm_xml_dump(an_xml_node, xml_log_option_formatted|xml_log_option_text, &buffer, &offset, &max, 0);
3182  return buffer;
3183 }
3184 
3185 char *
3186 dump_xml_formatted(xmlNode * an_xml_node)
3187 {
3188  char *buffer = NULL;
3189  int offset = 0, max = 0;
3190 
3191  crm_xml_dump(an_xml_node, xml_log_option_formatted, &buffer, &offset, &max, 0);
3192  return buffer;
3193 }
3194 
3195 char *
3196 dump_xml_unformatted(xmlNode * an_xml_node)
3197 {
3198  char *buffer = NULL;
3199  int offset = 0, max = 0;
3200 
3201  crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
3202  return buffer;
3203 }
3204 
3205 gboolean
3206 xml_has_children(const xmlNode * xml_root)
3207 {
3208  if (xml_root != NULL && xml_root->children != NULL) {
3209  return TRUE;
3210  }
3211  return FALSE;
3212 }
3213 
3214 void
3215 xml_remove_prop(xmlNode * obj, const char *name)
3216 {
3217  if (pcmk__check_acl(obj, NULL, xpf_acl_write) == FALSE) {
3218  crm_trace("Cannot remove %s from %s", name, obj->name);
3219 
3220  } else if (pcmk__tracking_xml_changes(obj, FALSE)) {
3221  /* Leave in place (marked for removal) until after the diff is calculated */
3222  xml_private_t *p = NULL;
3223  xmlAttr *attr = xmlHasProp(obj, (pcmkXmlStr) name);
3224 
3225  p = attr->_private;
3226  set_parent_flag(obj, xpf_dirty);
3227  p->flags |= xpf_deleted;
3228  /* crm_trace("Setting flag %x due to %s[@id=%s].%s", xpf_dirty, obj->name, ID(obj), name); */
3229 
3230  } else {
3231  xmlUnsetProp(obj, (pcmkXmlStr) name);
3232  }
3233 }
3234 
3235 void
3236 purge_diff_markers(xmlNode * a_node)
3237 {
3238  xmlNode *child = NULL;
3239 
3240  CRM_CHECK(a_node != NULL, return);
3241 
3243  for (child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
3244  purge_diff_markers(child);
3245  }
3246 }
3247 
3248 void
3249 save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
3250 {
3251  char *f = NULL;
3252 
3253  if (filename == NULL) {
3254  char *uuid = crm_generate_uuid();
3255 
3256  f = crm_strdup_printf("%s/%s", crm_get_tmpdir(), uuid);
3257  filename = f;
3258  free(uuid);
3259  }
3260 
3261  crm_info("Saving %s to %s", desc, filename);
3262  write_xml_file(xml, filename, FALSE);
3263  free(f);
3264 }
3265 
3266 gboolean
3267 apply_xml_diff(xmlNode *old_xml, xmlNode * diff, xmlNode **new_xml)
3268 {
3269  gboolean result = TRUE;
3270  int root_nodes_seen = 0;
3271  static struct qb_log_callsite *digest_cs = NULL;
3272  const char *digest = crm_element_value(diff, XML_ATTR_DIGEST);
3273  const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION);
3274 
3275  xmlNode *child_diff = NULL;
3276  xmlNode *added = find_xml_node(diff, "diff-added", FALSE);
3277  xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE);
3278 
3279  CRM_CHECK(new_xml != NULL, return FALSE);
3280  if (digest_cs == NULL) {
3281  digest_cs =
3282  qb_log_callsite_get(__func__, __FILE__, "diff-digest", LOG_TRACE, __LINE__,
3284  }
3285 
3286  crm_trace("Subtraction Phase");
3287  for (child_diff = __xml_first_child(removed); child_diff != NULL;
3288  child_diff = __xml_next(child_diff)) {
3289  CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3290  if (root_nodes_seen == 0) {
3291  *new_xml = subtract_xml_object(NULL, old_xml, child_diff, FALSE, NULL, NULL);
3292  }
3293  root_nodes_seen++;
3294  }
3295 
3296  if (root_nodes_seen == 0) {
3297  *new_xml = copy_xml(old_xml);
3298 
3299  } else if (root_nodes_seen > 1) {
3300  crm_err("(-) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3301  result = FALSE;
3302  }
3303 
3304  root_nodes_seen = 0;
3305  crm_trace("Addition Phase");
3306  if (result) {
3307  xmlNode *child_diff = NULL;
3308 
3309  for (child_diff = __xml_first_child(added); child_diff != NULL;
3310  child_diff = __xml_next(child_diff)) {
3311  CRM_CHECK(root_nodes_seen == 0, result = FALSE);
3312  if (root_nodes_seen == 0) {
3313  add_xml_object(NULL, *new_xml, child_diff, TRUE);
3314  }
3315  root_nodes_seen++;
3316  }
3317  }
3318 
3319  if (root_nodes_seen > 1) {
3320  crm_err("(+) Diffs cannot contain more than one change set..." " saw %d", root_nodes_seen);
3321  result = FALSE;
3322 
3323  } else if (result && digest) {
3324  char *new_digest = NULL;
3325 
3326  purge_diff_markers(*new_xml); /* Purge now so the diff is ok */
3327  new_digest = calculate_xml_versioned_digest(*new_xml, FALSE, TRUE, version);
3328  if (safe_str_neq(new_digest, digest)) {
3329  crm_info("Digest mis-match: expected %s, calculated %s", digest, new_digest);
3330  result = FALSE;
3331 
3332  crm_trace("%p %.6x", digest_cs, digest_cs ? digest_cs->targets : 0);
3333  if (digest_cs && digest_cs->targets) {
3334  save_xml_to_file(old_xml, "diff:original", NULL);
3335  save_xml_to_file(diff, "diff:input", NULL);
3336  save_xml_to_file(*new_xml, "diff:new", NULL);
3337  }
3338 
3339  } else {
3340  crm_trace("Digest matched: expected %s, calculated %s", digest, new_digest);
3341  }
3342  free(new_digest);
3343 
3344  } else if (result) {
3345  purge_diff_markers(*new_xml); /* Purge now so the diff is ok */
3346  }
3347 
3348  return result;
3349 }
3350 
3351 static void
3352 __xml_diff_object(xmlNode *old_xml, xmlNode *new_xml)
3353 {
3354  xmlNode *cIter = NULL;
3355  xmlAttr *pIter = NULL;
3356 
3357  CRM_CHECK(new_xml != NULL, return);
3358  if (old_xml == NULL) {
3359  crm_node_created(new_xml);
3360  pcmk__post_process_acl(new_xml); // Check creation is allowed
3361  return;
3362 
3363  } else {
3364  xml_private_t *p = new_xml->_private;
3365 
3366  if(p->flags & xpf_processed) {
3367  /* Avoid re-comparing nodes */
3368  return;
3369  }
3370  p->flags |= xpf_processed;
3371  }
3372 
3373  for (pIter = pcmk__first_xml_attr(new_xml); pIter != NULL; pIter = pIter->next) {
3374  xml_private_t *p = pIter->_private;
3375 
3376  /* Assume everything was just created and take it from there */
3377  p->flags |= xpf_created;
3378  }
3379 
3380  for (pIter = pcmk__first_xml_attr(old_xml); pIter != NULL; ) {
3381  xmlAttr *prop = pIter;
3382  xml_private_t *p = NULL;
3383  const char *name = (const char *)pIter->name;
3384  const char *old_value = crm_element_value(old_xml, name);
3385  xmlAttr *exists = xmlHasProp(new_xml, pIter->name);
3386 
3387  pIter = pIter->next;
3388  if(exists == NULL) {
3389  p = new_xml->doc->_private;
3390 
3391  /* Prevent the dirty flag being set recursively upwards */
3393  exists = xmlSetProp(new_xml, (pcmkXmlStr) name,
3394  (pcmkXmlStr) old_value);
3395  set_bit(p->flags, xpf_tracking);
3396 
3397  p = exists->_private;
3398  p->flags = 0;
3399 
3400  crm_trace("Lost %s@%s=%s", old_xml->name, name, old_value);
3401  xml_remove_prop(new_xml, name);
3402 
3403  } else {
3404  int p_new = __xml_offset((xmlNode*)exists);
3405  int p_old = __xml_offset((xmlNode*)prop);
3406  const char *value = crm_element_value(new_xml, name);
3407 
3408  p = exists->_private;
3409  p->flags = (p->flags & ~xpf_created);
3410 
3411  if(strcmp(value, old_value) != 0) {
3412  /* Restore the original value, so we can call crm_xml_add(),
3413  * which checks ACLs
3414  */
3415  char *vcopy = crm_element_value_copy(new_xml, name);
3416 
3417  crm_trace("Modified %s@%s %s->%s",
3418  old_xml->name, name, old_value, vcopy);
3419  xmlSetProp(new_xml, prop->name, (pcmkXmlStr) old_value);
3420  crm_xml_add(new_xml, name, vcopy);
3421  free(vcopy);
3422 
3423  } else if ((p_old != p_new)
3424  && !pcmk__tracking_xml_changes(new_xml, TRUE)) {
3425  crm_info("Moved %s@%s (%d -> %d)",
3426  old_xml->name, name, p_old, p_new);
3427  __xml_node_dirty(new_xml);
3428  p->flags |= xpf_dirty|xpf_moved;
3429 
3430  if(p_old > p_new) {
3431  p = prop->_private;
3432  p->flags |= xpf_skip;
3433 
3434  } else {
3435  p = exists->_private;
3436  p->flags |= xpf_skip;
3437  }
3438  }
3439  }
3440  }
3441 
3442  for (pIter = pcmk__first_xml_attr(new_xml); pIter != NULL; ) {
3443  xmlAttr *prop = pIter;
3444  xml_private_t *p = pIter->_private;
3445 
3446  pIter = pIter->next;
3447  if(is_set(p->flags, xpf_created)) {
3448  char *name = strdup((const char *)prop->name);
3449  char *value = crm_element_value_copy(new_xml, name);
3450 
3451  crm_trace("Created %s@%s=%s", new_xml->name, name, value);
3452  /* Remove plus create won't work as it will modify the relative attribute ordering */
3453  if (pcmk__check_acl(new_xml, name, xpf_acl_write)) {
3455  } else {
3456  xmlUnsetProp(new_xml, prop->name); /* Remove - change not allowed */
3457  }
3458 
3459  free(value);
3460  free(name);
3461  }
3462  }
3463 
3464  for (cIter = __xml_first_child(old_xml); cIter != NULL; ) {
3465  xmlNode *old_child = cIter;
3466  xmlNode *new_child = find_element(new_xml, cIter, TRUE);
3467 
3468  cIter = __xml_next(cIter);
3469  if(new_child) {
3470  __xml_diff_object(old_child, new_child);
3471 
3472  } else {
3473  /* Create then free (which will check the acls if necessary) */
3474  xmlNode *candidate = add_node_copy(new_xml, old_child);
3475  xmlNode *top = xmlDocGetRootElement(candidate->doc);
3476 
3477  __xml_node_clean(candidate);
3478  pcmk__apply_acl(top); /* Make sure any ACLs are applied to 'candidate' */
3479  /* Record the old position */
3480  free_xml_with_position(candidate, __xml_offset(old_child));
3481 
3482  if (find_element(new_xml, old_child, TRUE) == NULL) {
3483  xml_private_t *p = old_child->_private;
3484 
3485  p->flags |= xpf_skip;
3486  }
3487  }
3488  }
3489 
3490  for (cIter = __xml_first_child(new_xml); cIter != NULL; ) {
3491  xmlNode *new_child = cIter;
3492  xmlNode *old_child = find_element(old_xml, cIter, TRUE);
3493 
3494  cIter = __xml_next(cIter);
3495  if(old_child == NULL) {
3496  xml_private_t *p = new_child->_private;
3497  p->flags |= xpf_skip;
3498  __xml_diff_object(old_child, new_child);
3499 
3500  } else {
3501  /* Check for movement, we already checked for differences */
3502  int p_new = __xml_offset(new_child);
3503  int p_old = __xml_offset(old_child);
3504 
3505  if(p_old != p_new) {
3506  xml_private_t *p = new_child->_private;
3507 
3508  crm_info("%s.%s moved from %d to %d",
3509  new_child->name, ID(new_child), p_old, p_new);
3510  __xml_node_dirty(new_xml);
3511  p->flags |= xpf_moved;
3512 
3513  if(p_old > p_new) {
3514  p = old_child->_private;
3515  } else {
3516  p = new_child->_private;
3517  }
3518  p->flags |= xpf_skip;
3519  }
3520  }
3521  }
3522 }
3523 
3524 void
3525 xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
3526 {
3527  pcmk__set_xml_flag(new_xml, xpf_lazy);
3528  xml_calculate_changes(old_xml, new_xml);
3529 }
3530 
3531 void
3532 xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
3533 {
3534  CRM_CHECK(safe_str_eq(crm_element_name(old_xml),
3535  crm_element_name(new_xml)),
3536  return);
3537  CRM_CHECK(safe_str_eq(ID(old_xml), ID(new_xml)), return);
3538 
3539  if(xml_tracking_changes(new_xml) == FALSE) {
3540  xml_track_changes(new_xml, NULL, NULL, FALSE);
3541  }
3542 
3543  __xml_diff_object(old_xml, new_xml);
3544 }
3545 
3546 xmlNode *
3547 diff_xml_object(xmlNode * old, xmlNode * new, gboolean suppress)
3548 {
3549  xmlNode *tmp1 = NULL;
3550  xmlNode *diff = create_xml_node(NULL, "diff");
3551  xmlNode *removed = create_xml_node(diff, "diff-removed");
3552  xmlNode *added = create_xml_node(diff, "diff-added");
3553 
3555 
3556  tmp1 = subtract_xml_object(removed, old, new, FALSE, NULL, "removed:top");
3557  if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
3558  free_xml(tmp1);
3559  }
3560 
3561  tmp1 = subtract_xml_object(added, new, old, TRUE, NULL, "added:top");
3562  if (suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
3563  free_xml(tmp1);
3564  }
3565 
3566  if (added->children == NULL && removed->children == NULL) {
3567  free_xml(diff);
3568  diff = NULL;
3569  }
3570 
3571  return diff;
3572 }
3573 
3574 gboolean
3575 can_prune_leaf(xmlNode * xml_node)
3576 {
3577  xmlNode *cIter = NULL;
3578  xmlAttrPtr pIter = NULL;
3579  gboolean can_prune = TRUE;
3580  const char *name = crm_element_name(xml_node);
3581 
3585  || safe_str_eq(name, XML_ACL_TAG_ROLE_REFv1)) {
3586  return FALSE;
3587  }
3588 
3589  for (pIter = pcmk__first_xml_attr(xml_node); pIter != NULL; pIter = pIter->next) {
3590  const char *p_name = (const char *)pIter->name;
3591 
3592  if (strcmp(p_name, XML_ATTR_ID) == 0) {
3593  continue;
3594  }
3595  can_prune = FALSE;
3596  }
3597 
3598  cIter = __xml_first_child(xml_node);
3599  while (cIter) {
3600  xmlNode *child = cIter;
3601 
3602  cIter = __xml_next(cIter);
3603  if (can_prune_leaf(child)) {
3604  free_xml(child);
3605  } else {
3606  can_prune = FALSE;
3607  }
3608  }
3609  return can_prune;
3610 }
3611 
3612 static xmlNode *
3613 find_xml_comment(xmlNode * root, xmlNode * search_comment, gboolean exact)
3614 {
3615  xmlNode *a_child = NULL;
3616  int search_offset = __xml_offset(search_comment);
3617 
3618  CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
3619 
3620  for (a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
3621  if (exact) {
3622  int offset = __xml_offset(a_child);
3623  xml_private_t *p = a_child->_private;
3624 
3625  if (offset < search_offset) {
3626  continue;
3627 
3628  } else if (offset > search_offset) {
3629  return NULL;
3630  }
3631 
3632  if (is_set(p->flags, xpf_skip)) {
3633  continue;
3634  }
3635  }
3636 
3637  if (a_child->type == XML_COMMENT_NODE
3638  && safe_str_eq((const char *)a_child->content, (const char *)search_comment->content)) {
3639  return a_child;
3640 
3641  } else if (exact) {
3642  return NULL;
3643  }
3644  }
3645 
3646  return NULL;
3647 }
3648 
3649 static xmlNode *
3650 subtract_xml_comment(xmlNode * parent, xmlNode * left, xmlNode * right,
3651  gboolean * changed)
3652 {
3653  CRM_CHECK(left != NULL, return NULL);
3654  CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL);
3655 
3656  if (right == NULL
3657  || safe_str_neq((const char *)left->content, (const char *)right->content)) {
3658  xmlNode *deleted = NULL;
3659 
3660  deleted = add_node_copy(parent, left);
3661  *changed = TRUE;
3662 
3663  return deleted;
3664  }
3665 
3666  return NULL;
3667 }
3668 
3669 xmlNode *
3670 subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right,
3671  gboolean full, gboolean * changed, const char *marker)
3672 {
3673  gboolean dummy = FALSE;
3674  gboolean skip = FALSE;
3675  xmlNode *diff = NULL;
3676  xmlNode *right_child = NULL;
3677  xmlNode *left_child = NULL;
3678  xmlAttrPtr xIter = NULL;
3679 
3680  const char *id = NULL;
3681  const char *name = NULL;
3682  const char *value = NULL;
3683  const char *right_val = NULL;
3684 
3685  int lpc = 0;
3686  static int filter_len = DIMOF(filter);
3687 
3688  if (changed == NULL) {
3689  changed = &dummy;
3690  }
3691 
3692  if (left == NULL) {
3693  return NULL;
3694  }
3695 
3696  if (left->type == XML_COMMENT_NODE) {
3697  return subtract_xml_comment(parent, left, right, changed);
3698  }
3699 
3700  id = ID(left);
3701  if (right == NULL) {
3702  xmlNode *deleted = NULL;
3703 
3704  crm_trace("Processing <%s id=%s> (complete copy)", crm_element_name(left), id);
3705  deleted = add_node_copy(parent, left);
3706  crm_xml_add(deleted, XML_DIFF_MARKER, marker);
3707 
3708  *changed = TRUE;
3709  return deleted;
3710  }
3711 
3712  name = crm_element_name(left);
3713  CRM_CHECK(name != NULL, return NULL);
3714  CRM_CHECK(safe_str_eq(crm_element_name(left), crm_element_name(right)), return NULL);
3715 
3716  /* check for XML_DIFF_MARKER in a child */
3717  value = crm_element_value(right, XML_DIFF_MARKER);
3718  if (value != NULL && strcmp(value, "removed:top") == 0) {
3719  crm_trace("We are the root of the deletion: %s.id=%s", name, id);
3720  *changed = TRUE;
3721  return NULL;
3722  }
3723 
3724  /* Avoiding creating the full heirarchy would save even more work here */
3725  diff = create_xml_node(parent, name);
3726 
3727  /* Reset filter */
3728  for (lpc = 0; lpc < filter_len; lpc++) {
3729  filter[lpc].found = FALSE;
3730  }
3731 
3732  /* changes to child objects */
3733  for (left_child = __xml_first_child(left); left_child != NULL;
3734  left_child = __xml_next(left_child)) {
3735  gboolean child_changed = FALSE;
3736 
3737  right_child = find_element(right, left_child, FALSE);
3738  subtract_xml_object(diff, left_child, right_child, full, &child_changed, marker);
3739  if (child_changed) {
3740  *changed = TRUE;
3741  }
3742  }
3743 
3744  if (*changed == FALSE) {
3745  /* Nothing to do */
3746 
3747  } else if (full) {
3748  xmlAttrPtr pIter = NULL;
3749 
3750  for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3751  const char *p_name = (const char *)pIter->name;
3752  const char *p_value = pcmk__xml_attr_value(pIter);
3753 
3754  xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
3755  }
3756 
3757  /* We already have everything we need... */
3758  goto done;
3759  }
3760 
3761  /* changes to name/value pairs */
3762  for (xIter = pcmk__first_xml_attr(left); xIter != NULL; xIter = xIter->next) {
3763  const char *prop_name = (const char *)xIter->name;
3764  xmlAttrPtr right_attr = NULL;
3765  xml_private_t *p = NULL;
3766 
3767  if (strcmp(prop_name, XML_ATTR_ID) == 0) {
3768  /* id already obtained when present ~ this case, so just reuse */
3769  xmlSetProp(diff, (pcmkXmlStr) XML_ATTR_ID, (pcmkXmlStr) id);
3770  continue;
3771  }
3772 
3773  skip = FALSE;
3774  for (lpc = 0; skip == FALSE && lpc < filter_len; lpc++) {
3775  if (filter[lpc].found == FALSE && strcmp(prop_name, filter[lpc].string) == 0) {
3776  filter[lpc].found = TRUE;
3777  skip = TRUE;
3778  break;
3779  }
3780  }
3781 
3782  if (skip) {
3783  continue;
3784  }
3785 
3786  right_attr = xmlHasProp(right, (pcmkXmlStr) prop_name);
3787  if (right_attr) {
3788  p = right_attr->_private;
3789  }
3790 
3791  right_val = crm_element_value(right, prop_name);
3792  if (right_val == NULL || (p && is_set(p->flags, xpf_deleted))) {
3793  /* new */
3794  *changed = TRUE;
3795  if (full) {
3796  xmlAttrPtr pIter = NULL;
3797 
3798  for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3799  const char *p_name = (const char *)pIter->name;
3800  const char *p_value = pcmk__xml_attr_value(pIter);
3801 
3802  xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
3803  }
3804  break;
3805 
3806  } else {
3807  const char *left_value = crm_element_value(left, prop_name);
3808 
3809  xmlSetProp(diff, (pcmkXmlStr) prop_name, (pcmkXmlStr) value);
3810  crm_xml_add(diff, prop_name, left_value);
3811  }
3812 
3813  } else {
3814  /* Only now do we need the left value */
3815  const char *left_value = crm_element_value(left, prop_name);
3816 
3817  if (strcmp(left_value, right_val) == 0) {
3818  /* unchanged */
3819 
3820  } else {
3821  *changed = TRUE;
3822  if (full) {
3823  xmlAttrPtr pIter = NULL;
3824 
3825  crm_trace("Changes detected to %s in <%s id=%s>", prop_name,
3826  crm_element_name(left), id);
3827  for (pIter = pcmk__first_xml_attr(left); pIter != NULL; pIter = pIter->next) {
3828  const char *p_name = (const char *)pIter->name;
3829  const char *p_value = pcmk__xml_attr_value(pIter);
3830 
3831  xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
3832  }
3833  break;
3834 
3835  } else {
3836  crm_trace("Changes detected to %s (%s -> %s) in <%s id=%s>",
3837  prop_name, left_value, right_val, crm_element_name(left), id);
3838  crm_xml_add(diff, prop_name, left_value);
3839  }
3840  }
3841  }
3842  }
3843 
3844  if (*changed == FALSE) {
3845  free_xml(diff);
3846  return NULL;
3847 
3848  } else if (full == FALSE && id) {
3849  crm_xml_add(diff, XML_ATTR_ID, id);
3850  }
3851  done:
3852  return diff;
3853 }
3854 
3855 static int
3856 add_xml_comment(xmlNode * parent, xmlNode * target, xmlNode * update)
3857 {
3858  CRM_CHECK(update != NULL, return 0);
3859  CRM_CHECK(update->type == XML_COMMENT_NODE, return 0);
3860 
3861  if (target == NULL) {
3862  target = find_xml_comment(parent, update, FALSE);
3863  }
3864 
3865  if (target == NULL) {
3866  add_node_copy(parent, update);
3867 
3868  /* We won't reach here currently */
3869  } else if (safe_str_neq((const char *)target->content, (const char *)update->content)) {
3870  xmlFree(target->content);
3871  target->content = xmlStrdup(update->content);
3872  }
3873 
3874  return 0;
3875 }
3876 
3877 static int
3878 add_xml_object(xmlNode * parent, xmlNode * target, xmlNode * update, gboolean as_diff)
3879 {
3880  xmlNode *a_child = NULL;
3881  const char *object_name = NULL,
3882  *object_href = NULL,
3883  *object_href_val = NULL;
3884 
3885 #if XML_PARSE_DEBUG
3886  crm_log_xml_trace("update:", update);
3887  crm_log_xml_trace("target:", target);
3888 #endif
3889 
3890  CRM_CHECK(update != NULL, return 0);
3891 
3892  if (update->type == XML_COMMENT_NODE) {
3893  return add_xml_comment(parent, target, update);
3894  }
3895 
3896  object_name = crm_element_name(update);
3897  object_href_val = ID(update);
3898  if (object_href_val != NULL) {
3899  object_href = XML_ATTR_ID;
3900  } else {
3901  object_href_val = crm_element_value(update, XML_ATTR_IDREF);
3902  object_href = (object_href_val == NULL) ? NULL : XML_ATTR_IDREF;
3903  }
3904 
3905  CRM_CHECK(object_name != NULL, return 0);
3906  CRM_CHECK(target != NULL || parent != NULL, return 0);
3907 
3908  if (target == NULL) {
3909  target = find_entity_by_attr_or_just_name(parent, object_name,
3910  object_href, object_href_val);
3911  }
3912 
3913  if (target == NULL) {
3914  target = create_xml_node(parent, object_name);
3915  CRM_CHECK(target != NULL, return 0);
3916 #if XML_PARSER_DEBUG
3917  crm_trace("Added <%s%s%s%s%s/>", crm_str(object_name),
3918  object_href ? " " : "",
3919  object_href ? object_href : "",
3920  object_href ? "=" : "",
3921  object_href ? object_href_val : "");
3922 
3923  } else {
3924  crm_trace("Found node <%s%s%s%s%s/> to update", crm_str(object_name),
3925  object_href ? " " : "",
3926  object_href ? object_href : "",
3927  object_href ? "=" : "",
3928  object_href ? object_href_val : "");
3929 #endif
3930  }
3931 
3932  CRM_CHECK(safe_str_eq(crm_element_name(target), crm_element_name(update)), return 0);
3933 
3934  if (as_diff == FALSE) {
3935  /* So that expand_plus_plus() gets called */
3936  copy_in_properties(target, update);
3937 
3938  } else {
3939  /* No need for expand_plus_plus(), just raw speed */
3940  xmlAttrPtr pIter = NULL;
3941 
3942  for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
3943  const char *p_name = (const char *)pIter->name;
3944  const char *p_value = pcmk__xml_attr_value(pIter);
3945 
3946  /* Remove it first so the ordering of the update is preserved */
3947  xmlUnsetProp(target, (pcmkXmlStr) p_name);
3948  xmlSetProp(target, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
3949  }
3950  }
3951 
3952  for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
3953 #if XML_PARSER_DEBUG
3954  crm_trace("Updating child <%s%s%s%s%s/>", crm_str(object_name),
3955  object_href ? " " : "",
3956  object_href ? object_href : "",
3957  object_href ? "=" : "",
3958  object_href ? object_href_val : "");
3959 #endif
3960  add_xml_object(target, NULL, a_child, as_diff);
3961  }
3962 
3963 #if XML_PARSER_DEBUG
3964  crm_trace("Finished with <%s%s%s%s%s/>", crm_str(object_name),
3965  object_href ? " " : "",
3966  object_href ? object_href : "",
3967  object_href ? "=" : "",
3968  object_href ? object_href_val : "");
3969 #endif
3970  return 0;
3971 }
3972 
3973 gboolean
3974 update_xml_child(xmlNode * child, xmlNode * to_update)
3975 {
3976  gboolean can_update = TRUE;
3977  xmlNode *child_of_child = NULL;
3978 
3979  CRM_CHECK(child != NULL, return FALSE);
3980  CRM_CHECK(to_update != NULL, return FALSE);
3981 
3982  if (safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
3983  can_update = FALSE;
3984 
3985  } else if (safe_str_neq(ID(to_update), ID(child))) {
3986  can_update = FALSE;
3987 
3988  } else if (can_update) {
3989 #if XML_PARSER_DEBUG
3990  crm_log_xml_trace(child, "Update match found...");
3991 #endif
3992  add_xml_object(NULL, child, to_update, FALSE);
3993  }
3994 
3995  for (child_of_child = __xml_first_child(child); child_of_child != NULL;
3996  child_of_child = __xml_next(child_of_child)) {
3997  /* only update the first one */
3998  if (can_update) {
3999  break;
4000  }
4001  can_update = update_xml_child(child_of_child, to_update);
4002  }
4003 
4004  return can_update;
4005 }
4006 
4007 int
4008 find_xml_children(xmlNode ** children, xmlNode * root,
4009  const char *tag, const char *field, const char *value, gboolean search_matches)
4010 {
4011  int match_found = 0;
4012 
4013  CRM_CHECK(root != NULL, return FALSE);
4014  CRM_CHECK(children != NULL, return FALSE);
4015 
4016  if (tag != NULL && safe_str_neq(tag, crm_element_name(root))) {
4017 
4018  } else if (value != NULL && safe_str_neq(value, crm_element_value(root, field))) {
4019 
4020  } else {
4021  if (*children == NULL) {
4022  *children = create_xml_node(NULL, __FUNCTION__);
4023  }
4024  add_node_copy(*children, root);
4025  match_found = 1;
4026  }
4027 
4028  if (search_matches || match_found == 0) {
4029  xmlNode *child = NULL;
4030 
4031  for (child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
4032  match_found += find_xml_children(children, child, tag, field, value, search_matches);
4033  }
4034  }
4035 
4036  return match_found;
4037 }
4038 
4039 gboolean
4040 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
4041 {
4042  gboolean can_delete = FALSE;
4043  xmlNode *child_of_child = NULL;
4044 
4045  const char *up_id = NULL;
4046  const char *child_id = NULL;
4047  const char *right_val = NULL;
4048 
4049  CRM_CHECK(child != NULL, return FALSE);
4050  CRM_CHECK(update != NULL, return FALSE);
4051 
4052  up_id = ID(update);
4053  child_id = ID(child);
4054 
4055  if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
4056  can_delete = TRUE;
4057  }
4058  if (safe_str_neq(crm_element_name(update), crm_element_name(child))) {
4059  can_delete = FALSE;
4060  }
4061  if (can_delete && delete_only) {
4062  xmlAttrPtr pIter = NULL;
4063 
4064  for (pIter = pcmk__first_xml_attr(update); pIter != NULL; pIter = pIter->next) {
4065  const char *p_name = (const char *)pIter->name;
4066  const char *p_value = pcmk__xml_attr_value(pIter);
4067 
4068  right_val = crm_element_value(child, p_name);
4069  if (safe_str_neq(p_value, right_val)) {
4070  can_delete = FALSE;
4071  }
4072  }
4073  }
4074 
4075  if (can_delete && parent != NULL) {
4076  crm_log_xml_trace(child, "Delete match found...");
4077  if (delete_only || update == NULL) {
4078  free_xml(child);
4079 
4080  } else {
4081  xmlNode *tmp = copy_xml(update);
4082  xmlDoc *doc = tmp->doc;
4083  xmlNode *old = NULL;
4084 
4085  xml_accept_changes(tmp);
4086  old = xmlReplaceNode(child, tmp);
4087 
4088  if(xml_tracking_changes(tmp)) {
4089  /* Replaced sections may have included relevant ACLs */
4090  pcmk__apply_acl(tmp);
4091  }
4092 
4093  xml_calculate_changes(old, tmp);
4094  xmlDocSetRootElement(doc, old);
4095  free_xml(old);
4096  }
4097  child = NULL;
4098  return TRUE;
4099 
4100  } else if (can_delete) {
4101  crm_log_xml_debug(child, "Cannot delete the search root");
4102  can_delete = FALSE;
4103  }
4104 
4105  child_of_child = __xml_first_child(child);
4106  while (child_of_child) {
4107  xmlNode *next = __xml_next(child_of_child);
4108 
4109  can_delete = replace_xml_child(child, child_of_child, update, delete_only);
4110 
4111  /* only delete the first one */
4112  if (can_delete) {
4113  child_of_child = NULL;
4114  } else {
4115  child_of_child = next;
4116  }
4117  }
4118 
4119  return can_delete;
4120 }
4121 
4122 xmlNode *
4123 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
4124 {
4125  xmlNode *child = NULL;
4126  GSList *nvpairs = NULL;
4127  xmlNode *result = NULL;
4128  const char *name = NULL;
4129 
4130  CRM_CHECK(input != NULL, return NULL);
4131 
4132  name = crm_element_name(input);
4133  CRM_CHECK(name != NULL, return NULL);
4134 
4135  result = create_xml_node(parent, name);
4136  nvpairs = pcmk_xml_attrs2nvpairs(input);
4137  nvpairs = pcmk_sort_nvpairs(nvpairs);
4138  pcmk_nvpairs2xml_attrs(nvpairs, result);
4139  pcmk_free_nvpairs(nvpairs);
4140 
4141  for (child = __xml_first_child(input); child != NULL;
4142  child = __xml_next(child)) {
4143 
4144  if (recursive) {
4145  sorted_xml(child, result, recursive);
4146  } else {
4147  add_node_copy(result, child);
4148  }
4149  }
4150 
4151  return result;
4152 }
4153 
4154 xmlNode *
4155 first_named_child(const xmlNode *parent, const char *name)
4156 {
4157  xmlNode *match = NULL;
4158 
4159  for (match = __xml_first_child(parent); match != NULL; match = __xml_next(match)) {
4160  /*
4161  * name == NULL gives first child regardless of name; this is
4162  * semantically incorrect in this function, but may be necessary
4163  * due to prior use of xml_child_iter_filter
4164  */
4165  if (name == NULL || strcmp((const char *)match->name, name) == 0) {
4166  return match;
4167  }
4168  }
4169  return NULL;
4170 }
4171 
4179 xmlNode *
4180 crm_next_same_xml(const xmlNode *sibling)
4181 {
4182  xmlNode *match = __xml_next(sibling);
4183  const char *name = crm_element_name(sibling);
4184 
4185  while (match != NULL) {
4186  if (!strcmp(crm_element_name(match), name)) {
4187  return match;
4188  }
4189  match = __xml_next(match);
4190  }
4191  return NULL;
4192 }
4193 
4194 void
4196 {
4197  static bool init = TRUE;
4198 
4199  if(init) {
4200  init = FALSE;
4201  /* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
4202  * realloc_safe()s and it can take upwards of 18 seconds (yes, seconds)
4203  * to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
4204  * less than 1 second.
4205  */
4206  xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
4207 
4208  /* Populate and free the _private field when nodes are created and destroyed */
4209  xmlDeregisterNodeDefault(pcmkDeregisterNode);
4210  xmlRegisterNodeDefault(pcmkRegisterNode);
4211 
4212  crm_schema_init();
4213  }
4214 }
4215 
4216 void
4218 {
4219  crm_info("Cleaning up memory from libxml2");
4221  xmlCleanupParser();
4222 }
4223 
4224 #define XPATH_MAX 512
4225 
4226 xmlNode *
4227 expand_idref(xmlNode * input, xmlNode * top)
4228 {
4229  const char *tag = NULL;
4230  const char *ref = NULL;
4231  xmlNode *result = input;
4232 
4233  if (result == NULL) {
4234  return NULL;
4235 
4236  } else if (top == NULL) {
4237  top = input;
4238  }
4239 
4240  tag = crm_element_name(result);
4241  ref = crm_element_value(result, XML_ATTR_IDREF);
4242 
4243  if (ref != NULL) {
4244  char *xpath_string = crm_strdup_printf("//%s[@id='%s']", tag, ref);
4245 
4246  result = get_xpath_object(xpath_string, top, LOG_ERR);
4247  if (result == NULL) {
4248  char *nodePath = (char *)xmlGetNodePath(top);
4249 
4250  crm_err("No match for %s found in %s: Invalid configuration", xpath_string,
4251  crm_str(nodePath));
4252  free(nodePath);
4253  }
4254  free(xpath_string);
4255  }
4256  return result;
4257 }
4258 
4259 void
4260 crm_destroy_xml(gpointer data)
4261 {
4262  free_xml(data);
4263 }
#define pcmk_err_old_data
Definition: results.h:64
#define LOG_TRACE
Definition: logging.h:26
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:156
const char * crm_get_tmpdir(void)
Definition: io.c:500
xmlNode * find_xml_node(xmlNode *cib, const char *node_path, gboolean must_find)
Definition: xml.c:1678
#define XML_ATTR_UPDATE_ORIG
Definition: msg_xml.h:103
#define XML_DIFF_RESULT
Definition: msg_xml.h:407
A dumping ground.
GSList * pcmk_sort_nvpairs(GSList *list)
Sort a list of name/value pairs.
Definition: nvpair.c:143
void crm_schema_init(void)
Definition: schemas.c:374
xmlNode * get_message_xml(xmlNode *msg, const char *field)
Definition: xml.c:2504
#define crm_notice(fmt, args...)
Definition: logging.h:242
#define XML_ATTR_UPDATE_CLIENT
Definition: msg_xml.h:104
#define XML_TAG_DIFF
Definition: msg_xml.h:400
const char * bz2_strerror(int rc)
Definition: results.c:443
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:157
#define INFINITY
Definition: crm.h:73
char * crm_generate_uuid(void)
Definition: utils.c:1078
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:99
#define XML_ATTR_NUMUPDATES
Definition: msg_xml.h:89
void pcmk__free_acls(GList *acls)
Definition: acl.c:43
void crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
Definition: xml.c:3170
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, gboolean formatted)
G_GNUC_INTERNAL int pcmk__element_xpath(const char *prefix, xmlNode *xml, char *buffer, int offset, size_t buffer_size)
Definition: xml.c:1915
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml.c:183
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:4155
void crm_xml_cleanup(void)
Definition: xml.c:4217
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:383
#define CRM_FEATURE_SET
Definition: crm.h:32
#define XML_ATTR_UPDATE_USER
Definition: msg_xml.h:105
int char2score(const char *score)
Definition: utils.c:199
xmlNode * find_entity(xmlNode *parent, const char *node_name, const char *id)
Definition: xml.c:1743
void xml_track_changes(xmlNode *xml, const char *user, xmlNode *acl_source, bool enforce_acls)
Definition: xml.c:299
#define buffer_print(buffer, max, offset, fmt, args...)
Definition: xml.c:77
void crm_schema_cleanup(void)
Definition: schemas.c:546
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:275
void crm_xml_init(void)
Definition: xml.c:4195
#define pcmk_err_generic
Definition: results.h:60
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:2491
#define XML_ATTR_IDREF
Definition: msg_xml.h:97
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:339
int get_attr_name(const char *input, size_t offset, size_t max)
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:142
#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:188
#define clear_bit(word, bit)
Definition: crm_internal.h:168
void copy_in_properties(xmlNode *target, xmlNode *src)
Definition: xml.c:1750
void xml_accept_changes(xmlNode *xml)
Definition: xml.c:1000
xmlNode * filename2xml(const char *filename)
Definition: xml.c:2237
unsigned int crm_trace_nonlog
Definition: logging.c:39
GSList * pcmk_xml_attrs2nvpairs(xmlNode *xml)
Create a list of name/value pairs from an XML node&#39;s attributes.
Definition: nvpair.c:158
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:459
void purge_diff_markers(xmlNode *a_node)
Definition: xml.c:3236
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:220
void pcmk__post_process_acl(xmlNode *xml)
Definition: acl.c:473
#define CRM_XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
Definition: xml_internal.h:77
xmlNode * string2xml(const char *input)
Definition: xml.c:2058
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:2325
#define XML_ATTR_GENERATION
Definition: msg_xml.h:87
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:1852
void expand_plus_plus(xmlNode *target, const char *name, const char *value)
Definition: xml.c:1791
#define XML_ATTR_ORIGIN
Definition: msg_xml.h:91
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:314
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2020
#define CHUNK_SIZE
Definition: xml.c:61
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:557
#define crm_warn(fmt, args...)
Definition: logging.h:241
#define pcmk_err_diff_failed
Definition: results.h:65
#define set_bit(word, bit)
Definition: crm_internal.h:167
xmlNode * stdin2xml(void)
Definition: xml.c:2123
#define XML_DIFF_OP
Definition: msg_xml.h:408
#define crm_debug(fmt, args...)
Definition: logging.h:245
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
Definition: xml.c:1601
#define XML_DIFF_ATTR
Definition: msg_xml.h:406
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:556
#define XML_DIFF_VERSION
Definition: msg_xml.h:401
#define XML_ATTR_ID
Definition: msg_xml.h:96
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:423
gboolean update_xml_child(xmlNode *child, xmlNode *to_update)
Definition: xml.c:3974
#define crm_trace(fmt, args...)
Definition: logging.h:246
#define XML_BUFFER_SIZE
Definition: xml.c:33
#define crm_log_xml_explicit(xml, text)
Definition: logging.h:256
#define XML_PRIVATE_MAGIC
Definition: xml.c:204
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition: xml.c:1868
#define crm_log_xml_debug(xml, text)
Definition: logging.h:253
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:4227
gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
Definition: xml.c:3267
Wrappers for and extensions to libxml2.
bool xml_document_dirty(xmlNode *xml)
Definition: xml.c:325
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1890
#define crm_log_xml_warn(xml, text)
Definition: logging.h:250
char * dump_xml_formatted(xmlNode *msg)
Definition: xml.c:3186
G_GNUC_INTERNAL void pcmk__set_xml_flag(xmlNode *xml, enum xml_private_flags flag)
Definition: xml.c:130
#define XML_DIFF_POSITION
Definition: msg_xml.h:410
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:173
void xml_acl_disable(xmlNode *xml)
Definition: acl.c:533
void void crm_destroy_xml(gpointer data)
xmlNode destructor which can be used in glib collections
Definition: xml.c:4260
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition: acl.c:283
GListPtr deleted_objs
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
Definition: xml.c:2512
void free_xml(xmlNode *child)
Definition: xml.c:2014
gboolean xml_has_children(const xmlNode *root)
Definition: xml.c:3206
#define EOS
Definition: crm.h:34
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:220
xml_private_flags
gboolean crm_ends_with_ext(const char *s, const char *match)
Definition: strings.c:338
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:4123
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
Definition: xml.c:781
const xmlChar * pcmkXmlStr
Definition: xml.h:51
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
Definition: logging.c:624
#define XML_DIFF_VSOURCE
Definition: msg_xml.h:402
int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches)
Definition: xml.c:4008
bool xml_patch_versions(xmlNode *patchset, int add[3], int del[3])
Definition: xml.c:1192
void xml_log_changes(uint8_t level, const char *function, xmlNode *xml)
Definition: xml.c:969
#define CRM_XS
Definition: logging.h:34
#define XML_TAG_CIB
Definition: msg_xml.h:76
#define XML_DIFF_CHANGE
Definition: msg_xml.h:404
#define XML_DIFF_PATH
Definition: msg_xml.h:409
#define XML_DIFF_VTARGET
Definition: msg_xml.h:403
#define pcmk_err_diff_resync
Definition: results.h:66
#define crm_log_xml_err(xml, text)
Definition: logging.h:249
#define XML_DIFF_LIST
Definition: msg_xml.h:405
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:218
#define XML_DIFF_MARKER
Definition: msg_xml.h:75
#define crm_err(fmt, args...)
Definition: logging.h:240
#define CRM_ASSERT(expr)
Definition: results.h:42
#define ENOTUNIQ
Definition: portability.h:134
#define XML_CIB_ATTR_WRITTEN
Definition: msg_xml.h:93
void crm_xml_set_id(xmlNode *xml, const char *format,...) __attribute__((__format__(__printf__
#define XML_ACL_TAG_ROLE_REFv1
Definition: msg_xml.h:372
int get_attr_value(const char *input, size_t offset, size_t max)
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:224
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:3215
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:193
gboolean can_prune_leaf(xmlNode *xml_node)
Definition: xml.c:3575
int compare_version(const char *version1, const char *version2)
Definition: utils.c:461
#define crm_log_xml_info(xml, text)
Definition: logging.h:252
char * xml_get_path(xmlNode *xml)
Definition: xml.c:1937
char * dump_xml_unformatted(xmlNode *msg)
Definition: xml.c:3196
#define DIMOF(a)
Definition: crm.h:35
#define XML_ATTR_GENERATION_ADMIN
Definition: msg_xml.h:88
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:340
char data[0]
Definition: internal.h:92
#define crm_str(x)
Definition: logging.h:266
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:79
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Add XML attributes based on a list of name/value pairs.
Definition: nvpair.c:198
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:3249
void strip_text_nodes(xmlNode *xml)
Definition: xml.c:2208
#define pcmk_ok
Definition: results.h:57
xmlNode * diff_xml_object(xmlNode *left, xmlNode *right, gboolean suppress)
Definition: xml.c:3547
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:1882
int get_tag_name(const char *input, size_t offset, size_t max)
#define XML_CIB_TAG_OBJ_REF
Definition: msg_xml.h:389
#define crm_log_xml_trace(xml, text)
Definition: logging.h:254
#define XML_ACL_TAG_ROLE_REF
Definition: msg_xml.h:371
G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:64
xmlNode * subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, gboolean *changed, const char *marker)
Definition: xml.c:3670
#define XML_CIB_TAG_CONFIGURATION
Definition: msg_xml.h:138
#define ID(x)
Definition: msg_xml.h:414
void xml_log_patchset(uint8_t level, const char *function, xmlNode *xml)
Definition: xml.c:814
#define safe_str_eq(a, b)
Definition: util.h:59
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:3525
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor.
Definition: xml.c:2469
char * crm_xml_escape(const char *text)
Definition: xml.c:2538
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:625
void fix_plus_plus_recursive(xmlNode *target)
Definition: xml.c:1773
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version)
Definition: xml.c:724
GList * GListPtr
Definition: crm.h:192
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:3532
#define crm_info(fmt, args...)
Definition: logging.h:243
const char * crm_xml_add_last_written(xmlNode *xml_node)
Definition: xml.c:2310
char * dump_xml_formatted_with_text(xmlNode *msg)
Definition: xml.c:3176
uint32_t version
Definition: remote.c:146
uint64_t flags
Definition: remote.c:148
#define XML_ATTR_DIGEST
Definition: msg_xml.h:80
gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
Definition: xml.c:4040
void crm_xml_dump(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
Definition: xml.c:3065
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:4180
struct xml_deleted_obj_s xml_deleted_obj_t