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