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