pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
xml_element.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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 <stdarg.h> // va_start(), etc.
13 #include <stdint.h> // uint32_t
14 #include <stdio.h> // NULL, etc.
15 #include <stdlib.h> // free(), etc.
16 #include <string.h> // strchr(), etc.
17 #include <sys/types.h> // time_t, etc.
18 
19 #include <libxml/tree.h> // xmlNode, etc.
20 #include <libxml/valid.h> // xmlValidateNameValue()
21 
22 #include <crm/crm.h>
23 #include <crm/common/nvpair.h> // crm_xml_add(), etc.
24 #include <crm/common/results.h> // pcmk_rc_ok, etc.
25 #include <crm/common/xml.h>
26 #include "crmcommon_private.h"
27 
41 xmlNode *
42 pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
43  const char *attr_n, const char *attr_v)
44 {
45  xmlNode *child = NULL;
46  const char *parent_name = "<null>";
47 
48  CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL);
49 
50  if (parent != NULL) {
51  child = parent->children;
52  while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
53  child = child->next;
54  }
55 
56  parent_name = (const char *) parent->name;
57  }
58 
59  for (; child != NULL; child = pcmk__xe_next(child, NULL)) {
60  const char *value = NULL;
61 
62  if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) {
63  // Node name mismatch
64  continue;
65  }
66  if (attr_n == NULL) {
67  // No attribute match needed
68  return child;
69  }
70 
71  value = crm_element_value(child, attr_n);
72 
73  if ((attr_v == NULL) && (value != NULL)) {
74  // attr_v == NULL: Attribute attr_n must be set (to any value)
75  return child;
76  }
77  if ((attr_v != NULL) && (pcmk__str_eq(value, attr_v, pcmk__str_none))) {
78  // attr_v != NULL: Attribute attr_n must be set to value attr_v
79  return child;
80  }
81  }
82 
83  if (node_name == NULL) {
84  node_name = "(any)"; // For logging
85  }
86  if (attr_n != NULL) {
87  crm_trace("XML child node <%s %s=%s> not found in %s",
88  node_name, attr_n, attr_v, parent_name);
89  } else {
90  crm_trace("XML child node <%s> not found in %s",
91  node_name, parent_name);
92  }
93  return NULL;
94 }
95 
105 xmlNode *
106 pcmk__xe_next(const xmlNode *xml, const char *element_name)
107 {
108  for (xmlNode *next = (xml == NULL)? NULL : xml->next;
109  next != NULL; next = next->next) {
110  if ((next->type == XML_ELEMENT_NODE)
111  && ((element_name == NULL) || pcmk__xe_is(next, element_name))) {
112  return next;
113  }
114  }
115  return NULL;
116 }
117 
131 int
132 pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score,
133  int default_score)
134 {
135  const char *value = NULL;
136 
137  CRM_CHECK((xml != NULL) && (name != NULL), return EINVAL);
138  value = crm_element_value(xml, name);
139  return pcmk_parse_score(value, score, default_score);
140 }
141 
173 int
174 pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
175 {
176  const char *old_value = NULL;
177 
178  CRM_CHECK((target != NULL) && (name != NULL), return EINVAL);
179 
180  if (value == NULL) {
181  // @TODO Maybe instead delete the attribute or set it to 0
182  return pcmk_rc_ok;
183  }
184 
185  old_value = crm_element_value(target, name);
186 
187  // If no previous value, skip to default case and set the value unexpanded.
188  if (old_value != NULL) {
189  const char *n = name;
190  const char *v = value;
191 
192  // Stop at first character that differs between name and value
193  for (; (*n == *v) && (*n != '\0'); n++, v++);
194 
195  // If value begins with name followed by a "++" or "+="
196  if ((*n == '\0')
197  && (*v++ == '+')
198  && ((*v == '+') || (*v == '='))) {
199 
200  int add = 1;
201  int old_value_i = 0;
202  int rc = pcmk_rc_ok;
203 
204  // If we're expanding ourselves, no previous value was set; use 0
205  if (old_value != value) {
206  rc = pcmk_parse_score(old_value, &old_value_i, 0);
207  if (rc != pcmk_rc_ok) {
208  // @TODO This is inconsistent with old_value==NULL
209  crm_trace("Using 0 before incrementing %s because '%s' "
210  "is not a score", name, old_value);
211  }
212  }
213 
214  /* value="X++": new value of X is old_value + 1
215  * value="X+=Y": new value of X is old_value + Y (for some number Y)
216  */
217  if (*v != '+') {
218  rc = pcmk_parse_score(++v, &add, 0);
219  if (rc != pcmk_rc_ok) {
220  // @TODO We should probably skip expansion instead
221  crm_trace("Not incrementing %s because '%s' does not have "
222  "a valid increment", name, value);
223  }
224  }
225 
226  crm_xml_add_int(target, name, pcmk__add_scores(old_value_i, add));
227  return pcmk_rc_ok;
228  }
229  }
230 
231  // Default case: set the attribute unexpanded (with value treated literally)
232  if (old_value != value) {
233  crm_xml_add(target, name, value);
234  }
235  return pcmk_rc_ok;
236 }
237 
251 int
252 pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
253 {
254  CRM_CHECK((src != NULL) && (target != NULL), return EINVAL);
255 
256  for (xmlAttr *attr = pcmk__xe_first_attr(src); attr != NULL;
257  attr = attr->next) {
258 
259  const char *name = (const char *) attr->name;
260  const char *value = pcmk__xml_attr_value(attr);
261 
263  && (crm_element_value(target, name) != NULL)) {
264  continue;
265  }
266 
268  pcmk__xe_set_score(target, name, value);
269  } else {
270  crm_xml_add(target, name, value);
271  }
272  }
273 
274  return pcmk_rc_ok;
275 }
276 
290 static gint
291 compare_xml_attr(gconstpointer a, gconstpointer b)
292 {
293  const xmlAttr *attr_a = a;
294  const xmlAttr *attr_b = b;
295 
296  return pcmk__strcmp((const char *) attr_a->name,
297  (const char *) attr_b->name, pcmk__str_none);
298 }
299 
311 void
312 pcmk__xe_sort_attrs(xmlNode *xml)
313 {
314  GSList *attr_list = NULL;
315 
316  for (xmlAttr *iter = pcmk__xe_first_attr(xml); iter != NULL;
317  iter = iter->next) {
318  attr_list = g_slist_prepend(attr_list, iter);
319  }
320  attr_list = g_slist_sort(attr_list, compare_xml_attr);
321 
322  for (GSList *iter = attr_list; iter != NULL; iter = iter->next) {
323  xmlNode *attr = iter->data;
324 
325  xmlUnlinkNode(attr);
326  xmlAddChild(xml, attr);
327  }
328  g_slist_free(attr_list);
329 }
330 
338 void
339 pcmk__xe_remove_attr(xmlNode *element, const char *name)
340 {
341  if (name != NULL) {
342  pcmk__xa_remove(xmlHasProp(element, (pcmkXmlStr) name), false);
343  }
344 }
345 
360 bool
361 pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
362 {
363  const char *name = user_data;
364 
366  return true;
367 }
368 
378 void
380  bool (*match)(xmlAttrPtr, void *),
381  void *user_data)
382 {
383  xmlAttrPtr next = NULL;
384 
385  for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
386  next = a->next; // Grab now because attribute might get removed
387  if ((match == NULL) || match(a, user_data)) {
388  if (pcmk__xa_remove(a, false) != pcmk_rc_ok) {
389  return;
390  }
391  }
392  }
393 }
394 
406 xmlNode *
407 pcmk__xe_create(xmlNode *parent, const char *name)
408 {
409  xmlNode *node = NULL;
410 
411  pcmk__assert(!pcmk__str_empty(name));
412 
413  if (parent == NULL) {
414  xmlDoc *doc = pcmk__xml_new_doc();
415 
416  node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
417  pcmk__mem_assert(node);
418 
419  xmlDocSetRootElement(doc, node);
420 
421  } else {
422  node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
423  pcmk__mem_assert(node);
424  }
425 
427  return node;
428 }
429 
441 G_GNUC_PRINTF(2, 3)
442 void
443 pcmk__xe_set_content(xmlNode *node, const char *format, ...)
444 {
445  if (node != NULL) {
446  const char *content = NULL;
447  char *buf = NULL;
448 
449  /* xmlNodeSetContent() frees node->children and replaces it with new
450  * text. If this function is called for a node that already has a non-
451  * text child, it's a bug.
452  */
453  CRM_CHECK((node->children == NULL)
454  || (node->children->type == XML_TEXT_NODE),
455  return);
456 
457  if (strchr(format, '%') == NULL) {
458  // Nothing to format
459  content = format;
460 
461  } else {
462  va_list ap;
463 
464  va_start(ap, format);
465 
466  if (pcmk__str_eq(format, "%s", pcmk__str_none)) {
467  // No need to make a copy
468  content = va_arg(ap, const char *);
469 
470  } else {
471  pcmk__assert(vasprintf(&buf, format, ap) >= 0);
472  content = buf;
473  }
474  va_end(ap);
475  }
476 
477  xmlNodeSetContent(node, (pcmkXmlStr) content);
478  free(buf);
479  }
480 }
481 
493 G_GNUC_PRINTF(2, 3)
494 void
495 pcmk__xe_set_id(xmlNode *node, const char *format, ...)
496 {
497  char *id = NULL;
498  va_list ap;
499 
500  pcmk__assert(!pcmk__str_empty(format));
501 
502  if (node == NULL) {
503  return;
504  }
505 
506  va_start(ap, format);
507  pcmk__assert(vasprintf(&id, format, ap) >= 0);
508  va_end(ap);
509 
510  if (!xmlValidateNameValue((pcmkXmlStr) id)) {
512  }
513  crm_xml_add(node, PCMK_XA_ID, id);
514  free(id);
515 }
516 
525 const char *
527 {
528  char *now_s = pcmk__epoch2str(NULL, 0);
529  const char *result = NULL;
530 
532  pcmk__s(now_s, "Could not determine current time"));
533  free(now_s);
534  return result;
535 }
536 
569 static void
570 update_xe(xmlNode *parent, xmlNode *target, xmlNode *update, uint32_t flags)
571 {
572  // @TODO Try to refactor further, possibly using pcmk__xml_tree_foreach()
573  const char *update_name = NULL;
574  const char *update_id_attr = NULL;
575  const char *update_id_val = NULL;
576  char *trace_s = NULL;
577 
578  crm_log_xml_trace(update, "update");
579  crm_log_xml_trace(target, "target");
580 
581  CRM_CHECK(update != NULL, goto done);
582 
583  if (update->type == XML_COMMENT_NODE) {
584  pcmk__xc_update(parent, target, update);
585  goto done;
586  }
587 
588  update_name = (const char *) update->name;
589 
590  CRM_CHECK(update_name != NULL, goto done);
591  CRM_CHECK((target != NULL) || (parent != NULL), goto done);
592 
593  update_id_val = pcmk__xe_id(update);
594  if (update_id_val != NULL) {
595  update_id_attr = PCMK_XA_ID;
596 
597  } else {
598  update_id_val = crm_element_value(update, PCMK_XA_ID_REF);
599  if (update_id_val != NULL) {
600  update_id_attr = PCMK_XA_ID_REF;
601  }
602  }
603 
605  {
606  if (update_id_attr != NULL) {
607  trace_s = crm_strdup_printf("<%s %s=%s/>",
608  update_name, update_id_attr,
609  update_id_val);
610  } else {
611  trace_s = crm_strdup_printf("<%s/>", update_name);
612  }
613  },
614  {}
615  );
616 
617  if (target == NULL) {
618  // Recursive call
619  target = pcmk__xe_first_child(parent, update_name, update_id_attr,
620  update_id_val);
621  }
622 
623  if (target == NULL) {
624  // Recursive call with no existing matching child
625  target = pcmk__xe_create(parent, update_name);
626  crm_trace("Added %s", pcmk__s(trace_s, update_name));
627 
628  } else {
629  // Either recursive call with match, or top-level call
630  crm_trace("Found node %s to update", pcmk__s(trace_s, update_name));
631  }
632 
633  CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return);
634 
635  pcmk__xe_copy_attrs(target, update, flags);
636 
637  for (xmlNode *child = pcmk__xml_first_child(update); child != NULL;
638  child = pcmk__xml_next(child)) {
639 
640  crm_trace("Updating child of %s", pcmk__s(trace_s, update_name));
641  update_xe(target, NULL, child, flags);
642  }
643 
644  crm_trace("Finished with %s", pcmk__s(trace_s, update_name));
645 
646 done:
647  free(trace_s);
648 }
649 
668 static bool
669 delete_xe_if_matching(xmlNode *xml, void *user_data)
670 {
671  xmlNode *search = user_data;
672 
673  if (!pcmk__xe_is(search, (const char *) xml->name)) {
674  // No match: either not both elements, or different element types
675  return true;
676  }
677 
678  for (const xmlAttr *attr = pcmk__xe_first_attr(search); attr != NULL;
679  attr = attr->next) {
680 
681  const char *search_val = pcmk__xml_attr_value(attr);
682  const char *xml_val = crm_element_value(xml, (const char *) attr->name);
683 
684  if (!pcmk__str_eq(search_val, xml_val, pcmk__str_casei)) {
685  // No match: an attr in xml doesn't match the attr in search
686  return true;
687  }
688  }
689 
690  crm_log_xml_trace(xml, "delete-match");
691  crm_log_xml_trace(search, "delete-search");
692  pcmk__xml_free(xml);
693 
694  // Found a match and deleted it; stop traversing tree
695  return false;
696 }
697 
716 int
717 pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
718 {
719  // See @COMPAT comment in pcmk__xe_replace_match()
720  CRM_CHECK((xml != NULL) && (search != NULL), return EINVAL);
721 
722  for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
723  xml = pcmk__xe_next(xml, NULL)) {
724 
725  if (!pcmk__xml_tree_foreach(xml, delete_xe_if_matching, search)) {
726  // Found and deleted an element
727  return pcmk_rc_ok;
728  }
729  }
730 
731  // No match found in this subtree
732  return ENXIO;
733 }
734 
746 static void
747 replace_node(xmlNode *old, xmlNode *new)
748 {
749  // Pass old for its doc; it won't remain the parent of new
750  new = pcmk__xml_copy(old, new);
751  old = xmlReplaceNode(old, new);
752 
753  // old == NULL means memory allocation error
754  pcmk__assert(old != NULL);
755 
756  // May be unnecessary but avoids slight changes to some test outputs
758 
759  if (xml_tracking_changes(new)) {
760  // Replaced sections may have included relevant ACLs
761  pcmk__apply_acl(new);
762  }
763  xml_calculate_changes(old, new);
764  pcmk__xml_free_node(old);
765 }
766 
784 static bool
785 replace_xe_if_matching(xmlNode *xml, void *user_data)
786 {
787  xmlNode *replace = user_data;
788  const char *xml_id = NULL;
789  const char *replace_id = NULL;
790 
791  xml_id = pcmk__xe_id(xml);
792  replace_id = pcmk__xe_id(replace);
793 
794  if (!pcmk__xe_is(replace, (const char *) xml->name)) {
795  // No match: either not both elements, or different element types
796  return true;
797  }
798 
799  if ((replace_id != NULL)
800  && !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) {
801 
802  // No match: ID was provided in replace and doesn't match xml's ID
803  return true;
804  }
805 
806  crm_log_xml_trace(xml, "replace-match");
807  crm_log_xml_trace(replace, "replace-with");
808  replace_node(xml, replace);
809 
810  // Found a match and replaced it; stop traversing tree
811  return false;
812 }
813 
831 int
832 pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
833 {
834  /* @COMPAT Some of this behavior (like not matching the tree root, which is
835  * allowed by pcmk__xe_update_match()) is questionable for general use but
836  * required for backward compatibility by cib_process_replace() and
837  * cib_process_delete(). Behavior can change at a major version release if
838  * desired.
839  */
840  CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL);
841 
842  for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
843  xml = pcmk__xe_next(xml, NULL)) {
844 
845  if (!pcmk__xml_tree_foreach(xml, replace_xe_if_matching, replace)) {
846  // Found and replaced an element
847  return pcmk_rc_ok;
848  }
849  }
850 
851  // No match found in this subtree
852  return ENXIO;
853 }
854 
856 struct update_data {
857  xmlNode *update;
858  uint32_t flags;
859 };
860 
882 static bool
883 update_xe_if_matching(xmlNode *xml, void *user_data)
884 {
885  struct update_data *data = user_data;
886  xmlNode *update = data->update;
887 
888  if (!pcmk__xe_is(update, (const char *) xml->name)) {
889  // No match: either not both elements, or different element types
890  return true;
891  }
892 
893  if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) {
894  // No match: ID mismatch
895  return true;
896  }
897 
898  crm_log_xml_trace(xml, "update-match");
899  crm_log_xml_trace(update, "update-with");
900  update_xe(NULL, xml, update, data->flags);
901 
902  // Found a match and replaced it; stop traversing tree
903  return false;
904 }
905 
925 int
926 pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
927 {
928  /* @COMPAT In pcmk__xe_delete_match() and pcmk__xe_replace_match(), we
929  * compare IDs only if the equivalent of the update argument has an ID.
930  * Here, we're stricter: we consider it a mismatch if only one element has
931  * an ID attribute, or if both elements have IDs but they don't match.
932  *
933  * Perhaps we should align the behavior at a major version release.
934  */
935  struct update_data data = {
936  .update = update,
937  .flags = flags,
938  };
939 
940  CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL);
941 
942  if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, &data)) {
943  // Found and updated an element
944  return pcmk_rc_ok;
945  }
946 
947  // No match found in this subtree
948  return ENXIO;
949 }
950 
951 void
952 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
953 {
954  while (true) {
955  const char *name, *value;
956 
957  name = va_arg(pairs, const char *);
958  if (name == NULL) {
959  return;
960  }
961 
962  value = va_arg(pairs, const char *);
963  if (value != NULL) {
964  crm_xml_add(node, name, value);
965  }
966  }
967 }
968 
969 void
970 pcmk__xe_set_props(xmlNodePtr node, ...)
971 {
972  va_list pairs;
973  va_start(pairs, node);
974  pcmk__xe_set_propv(node, pairs);
975  va_end(pairs);
976 }
977 
978 int
979 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
980  int (*handler)(xmlNode *xml, void *userdata),
981  void *userdata)
982 {
983  xmlNode *children = (xml? xml->children : NULL);
984 
985  pcmk__assert(handler != NULL);
986 
987  for (xmlNode *node = children; node != NULL; node = node->next) {
988  if ((node->type == XML_ELEMENT_NODE)
989  && ((child_element_name == NULL)
990  || pcmk__xe_is(node, child_element_name))) {
991  int rc = handler(node, userdata);
992 
993  if (rc != pcmk_rc_ok) {
994  return rc;
995  }
996  }
997  }
998 
999  return pcmk_rc_ok;
1000 }
1001 
1002 // XML attribute handling
1003 
1014 const char *
1015 crm_xml_add(xmlNode *node, const char *name, const char *value)
1016 {
1017  // @TODO Replace with internal function that returns the new attribute
1018  bool dirty = FALSE;
1019  xmlAttr *attr = NULL;
1020 
1021  CRM_CHECK(node != NULL, return NULL);
1022  CRM_CHECK(name != NULL, return NULL);
1023 
1024  if (value == NULL) {
1025  return NULL;
1026  }
1027 
1028  if (pcmk__tracking_xml_changes(node, FALSE)) {
1029  const char *old = crm_element_value(node, name);
1030 
1031  if (old == NULL || value == NULL || strcmp(old, value) != 0) {
1032  dirty = TRUE;
1033  }
1034  }
1035 
1036  if (dirty && (pcmk__check_acl(node, name, pcmk__xf_acl_create) == FALSE)) {
1037  crm_trace("Cannot add %s=%s to %s", name, value, node->name);
1038  return NULL;
1039  }
1040 
1041  attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
1042 
1043  /* If the attribute already exists, this does nothing. Attribute values
1044  * don't get private data.
1045  */
1046  pcmk__xml_new_private_data((xmlNode *) attr);
1047 
1048  if (dirty) {
1050  }
1051 
1052  CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
1053  return (char *)attr->children->content;
1054 }
1055 
1056 
1069 const char *
1070 crm_xml_add_int(xmlNode *node, const char *name, int value)
1071 {
1072  char *number = pcmk__itoa(value);
1073  const char *added = crm_xml_add(node, name, number);
1074 
1075  free(number);
1076  return added;
1077 }
1078 
1091 const char *
1092 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
1093 {
1094  char *number = crm_strdup_printf("%u", ms);
1095  const char *added = crm_xml_add(node, name, number);
1096 
1097  free(number);
1098  return added;
1099 }
1100 
1101 // Maximum size of null-terminated string representation of 64-bit integer
1102 // -9223372036854775808
1103 #define LLSTRSIZE 21
1104 
1119 const char *
1120 crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
1121 {
1122  char s[LLSTRSIZE] = { '\0', };
1123 
1124  if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
1125  return NULL;
1126  }
1127  return crm_xml_add(xml, name, s);
1128 }
1129 
1143 const char *
1144 crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
1145  const struct timeval *value)
1146 {
1147  const char *added = NULL;
1148 
1149  if (xml && name_sec && value) {
1150  added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
1151  if (added && name_usec) {
1152  // Any error is ignored (we successfully added seconds)
1153  crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
1154  }
1155  }
1156  return added;
1157 }
1158 
1167 const char *
1168 crm_element_value(const xmlNode *data, const char *name)
1169 {
1170  xmlAttr *attr = NULL;
1171 
1172  if (data == NULL) {
1173  crm_err("Couldn't find %s in NULL", name ? name : "<null>");
1174  CRM_LOG_ASSERT(data != NULL);
1175  return NULL;
1176 
1177  } else if (name == NULL) {
1178  crm_err("Couldn't find NULL in %s", data->name);
1179  return NULL;
1180  }
1181 
1182  attr = xmlHasProp(data, (pcmkXmlStr) name);
1183  if (!attr || !attr->children) {
1184  return NULL;
1185  }
1186  return (const char *) attr->children->content;
1187 }
1188 
1200 int
1201 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
1202 {
1203  const char *value = NULL;
1204 
1205  CRM_CHECK(dest != NULL, return -1);
1206  value = crm_element_value(data, name);
1207  if (value) {
1208  long long value_ll;
1209  int rc = pcmk__scan_ll(value, &value_ll, 0LL);
1210 
1211  *dest = PCMK__PARSE_INT_DEFAULT;
1212  if (rc != pcmk_rc_ok) {
1213  crm_warn("Using default for %s "
1214  "because '%s' is not a valid integer: %s",
1215  name, value, pcmk_rc_str(rc));
1216  } else if ((value_ll < INT_MIN) || (value_ll > INT_MAX)) {
1217  crm_warn("Using default for %s because '%s' is out of range",
1218  name, value);
1219  } else {
1220  *dest = (int) value_ll;
1221  return 0;
1222  }
1223  }
1224  return -1;
1225 }
1226 
1242 int
1243 pcmk__xe_get_flags(const xmlNode *xml, const char *name, uint32_t *dest,
1244  uint32_t default_value)
1245 {
1246  const char *value = NULL;
1247  long long value_ll = 0LL;
1248  int rc = pcmk_rc_ok;
1249 
1250  if (dest != NULL) {
1251  *dest = default_value;
1252  }
1253 
1254  if (name == NULL) {
1255  return EINVAL;
1256  }
1257  if (xml == NULL) {
1258  return pcmk_rc_ok;
1259  }
1260  value = crm_element_value(xml, name);
1261  if (value == NULL) {
1262  return pcmk_rc_ok;
1263  }
1264 
1265  rc = pcmk__scan_ll(value, &value_ll, default_value);
1266  if ((value_ll < 0) || (value_ll > UINT32_MAX)) {
1267  value_ll = default_value;
1268  if (rc == pcmk_rc_ok) {
1269  rc = pcmk_rc_bad_input;
1270  }
1271  }
1272 
1273  if (dest != NULL) {
1274  *dest = (uint32_t) value_ll;
1275  }
1276  return rc;
1277 }
1278 
1290 int
1291 crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
1292 {
1293  const char *value = NULL;
1294 
1295  CRM_CHECK(dest != NULL, return -1);
1296  value = crm_element_value(data, name);
1297  if (value != NULL) {
1298  int rc = pcmk__scan_ll(value, dest, PCMK__PARSE_INT_DEFAULT);
1299 
1300  if (rc == pcmk_rc_ok) {
1301  return 0;
1302  }
1303  crm_warn("Using default for %s "
1304  "because '%s' is not a valid integer: %s",
1305  name, value, pcmk_rc_str(rc));
1306  }
1307  return -1;
1308 }
1309 
1321 int
1322 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
1323 {
1324  const char *value = NULL;
1325  long long value_ll;
1326  int rc = pcmk_rc_ok;
1327 
1328  CRM_CHECK(dest != NULL, return -1);
1329  *dest = 0;
1330  value = crm_element_value(data, name);
1331  rc = pcmk__scan_ll(value, &value_ll, 0LL);
1332  if (rc != pcmk_rc_ok) {
1333  crm_warn("Using default for %s "
1334  "because '%s' is not valid milliseconds: %s",
1335  name, value, pcmk_rc_str(rc));
1336  return -1;
1337  }
1338  if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
1339  crm_warn("Using default for %s because '%s' is out of range",
1340  name, value);
1341  return -1;
1342  }
1343  *dest = (guint) value_ll;
1344  return pcmk_ok;
1345 }
1346 
1358 int
1359 crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
1360 {
1361  long long value_ll = 0;
1362 
1363  if (crm_element_value_ll(xml, name, &value_ll) < 0) {
1364  return -1;
1365  }
1366 
1367  /* Unfortunately, we can't do any bounds checking, since time_t has neither
1368  * standardized bounds nor constants defined for them.
1369  */
1370  *dest = (time_t) value_ll;
1371  return pcmk_ok;
1372 }
1373 
1387 int
1388 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
1389  const char *name_usec, struct timeval *dest)
1390 {
1391  long long value_i = 0;
1392 
1393  CRM_CHECK(dest != NULL, return -EINVAL);
1394  dest->tv_sec = 0;
1395  dest->tv_usec = 0;
1396 
1397  if (xml == NULL) {
1398  return pcmk_ok;
1399  }
1400 
1401  /* Unfortunately, we can't do any bounds checking, since there are no
1402  * constants provided for the bounds of time_t and suseconds_t, and
1403  * calculating them isn't worth the effort. If there are XML values
1404  * beyond the native sizes, there will probably be worse problems anyway.
1405  */
1406 
1407  // Parse seconds
1408  errno = 0;
1409  if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
1410  return -errno;
1411  }
1412  dest->tv_sec = (time_t) value_i;
1413 
1414  // Parse microseconds
1415  if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
1416  return -errno;
1417  }
1418  dest->tv_usec = (suseconds_t) value_i;
1419 
1420  return pcmk_ok;
1421 }
1422 
1435 int
1436 pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
1437 {
1438  const char *value = NULL;
1439 
1440  if ((t == NULL) || (*t != NULL) || (xml == NULL) || (attr == NULL)) {
1441  return EINVAL;
1442  }
1443 
1444  value = crm_element_value(xml, attr);
1445  if (value != NULL) {
1446  *t = crm_time_new(value);
1447  if (*t == NULL) {
1448  return pcmk_rc_unpack_error;
1449  }
1450  }
1451  return pcmk_rc_ok;
1452 }
1453 
1465 char *
1466 crm_element_value_copy(const xmlNode *data, const char *name)
1467 {
1469 }
1470 
1479 void
1480 pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
1481 {
1482  crm_xml_add(node, name, pcmk__btoa(value));
1483 }
1484 
1500 int
1501 pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
1502 {
1503  const char *xml_value = NULL;
1504  int ret, rc;
1505 
1506  if (node == NULL) {
1507  return ENODATA;
1508  } else if (name == NULL || value == NULL) {
1509  return EINVAL;
1510  }
1511 
1512  xml_value = crm_element_value(node, name);
1513 
1514  if (xml_value == NULL) {
1515  return ENODATA;
1516  }
1517 
1518  rc = crm_str_to_boolean(xml_value, &ret);
1519  if (rc == 1) {
1520  *value = ret;
1521  return pcmk_rc_ok;
1522  } else {
1523  return pcmk_rc_bad_input;
1524  }
1525 }
1526 
1537 bool
1538 pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
1539 {
1540  bool value = false;
1541  int rc;
1542 
1543  rc = pcmk__xe_get_bool_attr(node, name, &value);
1544  return rc == pcmk_rc_ok && value == true;
1545 }
1546 
1547 // Deprecated functions kept only for backward API compatibility
1548 // LCOV_EXCL_START
1549 
1550 #include <glib.h> // gboolean, GSList
1551 
1552 #include <crm/common/nvpair_compat.h> // pcmk_xml_attrs2nvpairs(), etc.
1553 #include <crm/common/xml_compat.h> // crm_xml_sanitize_id()
1555 
1556 xmlNode *
1557 expand_idref(xmlNode *input, xmlNode *top)
1558 {
1559  return pcmk__xe_resolve_idref(input, top);
1560 }
1561 
1562 void
1563 crm_xml_set_id(xmlNode *xml, const char *format, ...)
1564 {
1565  va_list ap;
1566  int len = 0;
1567  char *id = NULL;
1568 
1569  /* equivalent to crm_strdup_printf() */
1570  va_start(ap, format);
1571  len = vasprintf(&id, format, ap);
1572  va_end(ap);
1573  pcmk__assert(len > 0);
1574 
1575  crm_xml_sanitize_id(id);
1576  crm_xml_add(xml, PCMK_XA_ID, id);
1577  free(id);
1578 }
1579 
1580 xmlNode *
1581 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
1582 {
1583  xmlNode *child = NULL;
1584  GSList *nvpairs = NULL;
1585  xmlNode *result = NULL;
1586 
1587  CRM_CHECK(input != NULL, return NULL);
1588 
1589  result = pcmk__xe_create(parent, (const char *) input->name);
1590  nvpairs = pcmk_xml_attrs2nvpairs(input);
1591  nvpairs = pcmk_sort_nvpairs(nvpairs);
1592  pcmk_nvpairs2xml_attrs(nvpairs, result);
1593  pcmk_free_nvpairs(nvpairs);
1594 
1595  for (child = pcmk__xe_first_child(input, NULL, NULL, NULL); child != NULL;
1596  child = pcmk__xe_next(child, NULL)) {
1597 
1598  if (recursive) {
1599  sorted_xml(child, result, recursive);
1600  } else {
1601  pcmk__xml_copy(result, child);
1602  }
1603  }
1604 
1605  return result;
1606 }
1607 
1608 // LCOV_EXCL_STOP
1609 // End deprecated API
int crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
Retrieve the long long integer value of an XML attribute.
Definition: xml_element.c:1291
int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
Definition: xml_element.c:717
Deprecated Pacemaker XML element API.
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
xmlNode * pcmk__xe_resolve_idref(xmlNode *xml, xmlNode *search)
Definition: xml_idref.c:85
void pcmk__xe_set_props(xmlNodePtr node,...)
Definition: xml_element.c:970
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:805
A dumping ground.
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml_element.c:339
G_GNUC_INTERNAL bool pcmk__xml_reset_node_flags(xmlNode *xml, void *user_data)
Definition: xml.c:125
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: xml_element.c:1466
char data[0]
Definition: cpg.c:58
#define pcmk__if_tracing(if_action, else_action)
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition: nvpair.c:103
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: xml_element.c:1480
const char * name
Definition: cib.c:26
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: xml_element.c:1501
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml_element.c:1557
struct crm_time_s crm_time_t
Definition: iso8601.h:32
int pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
Definition: xml_element.c:1436
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
Definition: xml_element.c:952
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition: xml_attr.c:75
GSList * pcmk_xml_attrs2nvpairs(const xmlNode *xml)
Definition: nvpair.c:569
void pcmk__xe_set_id(xmlNode *node, const char *format,...)
Definition: xml_element.c:495
int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
Definition: xml_element.c:252
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml_element.c:1581
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: xml_element.c:1201
int pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score, int default_score)
Definition: xml_element.c:132
const char * crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec, const struct timeval *value)
Create XML attributes for seconds and microseconds.
Definition: xml_element.c:1144
#define PCMK_XA_CIB_LAST_WRITTEN
Definition: xml_names.h:244
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:196
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:609
void pcmk__xe_sort_attrs(xmlNode *xml)
Definition: xml_element.c:312
Deprecated Pacemaker name-value pair API.
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Definition: nvpair.c:593
Deprecated Pacemaker XML API.
void pcmk__xe_set_content(xmlNode *node, const char *format,...)
Definition: xml_element.c:443
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition: xml_element.c:1538
G_GNUC_INTERNAL xmlDoc * pcmk__xml_new_doc(void)
Definition: xml.c:479
bool xml_tracking_changes(xmlNode *xml)
Definition: xml.c:361
void pcmk__xml_free(xmlNode *xml)
Definition: xml.c:789
const char * pcmk__xe_add_last_written(xmlNode *xe)
Definition: xml_element.c:526
#define PCMK__PARSE_INT_DEFAULT
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:92
#define crm_warn(fmt, args...)
Definition: logging.h:362
G_GNUC_INTERNAL void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
Definition: xml_comment.c:99
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: xml_element.c:1070
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml_element.c:379
void crm_xml_sanitize_id(char *id)
Definition: xml.c:1583
G_GNUC_INTERNAL void pcmk__xml_new_private_data(xmlNode *xml)
Definition: xml.c:328
#define PCMK_XA_ID_REF
Definition: xml_names.h:303
G_GNUC_INTERNAL void pcmk__xml_free_node(xmlNode *xml)
Definition: xml.c:696
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml_element.c:407
#define crm_trace(fmt, args...)
Definition: logging.h:372
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
Wrappers for and extensions to libxml2.
int pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
Definition: xml_element.c:174
xmlNode * pcmk__xe_next(const xmlNode *xml, const char *element_name)
Definition: xml_element.c:106
#define PCMK_XA_ID
Definition: xml_names.h:301
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:159
#define pcmk__str_copy(str)
GSList * pcmk_sort_nvpairs(GSList *list)
Definition: nvpair.c:563
Function and executable result codes.
int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
Definition: xml_element.c:832
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: xml_element.c:1322
const xmlChar * pcmkXmlStr
Definition: xml.h:41
Functionality for manipulating name/value pairs.
#define pcmk__assert(expr)
const char * target
Definition: pcmk_fence.c:31
const char * crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
Create an XML attribute with specified name and long long int value.
Definition: xml_element.c:1120
#define ENODATA
Definition: portability.h:61
bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
Definition: xml_element.c:361
pcmk__action_result_t result
Definition: pcmk_fence.c:37
#define crm_err(fmt, args...)
Definition: logging.h:359
int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
Definition: xml_element.c:926
char * pcmk__epoch2str(const time_t *source, uint32_t flags)
Definition: iso8601.c:2147
xmlNode * input
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:112
bool pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
Definition: acl.c:655
void pcmk__apply_acl(xmlNode *xml)
Definition: acl.c:214
#define pcmk__mem_assert(ptr)
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1164
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition: scores.c:34
void crm_xml_set_id(xmlNode *xml, const char *format,...)
Definition: xml_element.c:1563
#define pcmk_ok
Definition: results.h:65
bool pcmk__xml_tree_foreach(xmlNode *xml, bool(*fn)(xmlNode *, void *), void *user_data)
Definition: xml.c:47
int crm_element_value_timeval(const xmlNode *xml, const char *name_sec, const char *name_usec, struct timeval *dest)
Retrieve the value of XML second/microsecond attributes as time.
Definition: xml_element.c:1388
const char * crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
Create an XML attribute with specified name and unsigned value.
Definition: xml_element.c:1092
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:498
#define crm_log_xml_trace(xml, text)
Definition: logging.h:380
void pcmk__xml_sanitize_id(char *id)
Definition: xml.c:654
G_GNUC_INTERNAL bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
Definition: xml.c:69
int pcmk__xe_get_flags(const xmlNode *xml, const char *name, uint32_t *dest, uint32_t default_value)
Definition: xml_element.c:1243
const char * parent
Definition: cib.c:27
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml_element.c:979
int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
Retrieve the seconds-since-epoch value of an XML attribute.
Definition: xml_element.c:1359
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition: xml.c:1407
G_GNUC_INTERNAL int pcmk__xa_remove(xmlAttr *attr, bool force)
Definition: xml_attr.c:45
uint64_t flags
Definition: remote.c:211
#define LLSTRSIZE
Definition: xml_element.c:1103
Don&#39;t overwrite existing values.
Definition: xml_internal.h:365
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1