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