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