pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
xml_element.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2025 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#include <libxml/xmlstring.h> // xmlChar
22
23#include <crm/crm.h>
24#include <crm/common/nvpair.h> // crm_xml_add(), etc.
25#include <crm/common/results.h> // pcmk_rc_ok, etc.
26#include <crm/common/xml.h>
27#include "crmcommon_private.h"
28
42xmlNode *
43pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
44 const char *attr_n, const char *attr_v)
45{
46 xmlNode *child = NULL;
47
48 CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL);
49
50 if (parent == NULL) {
51 return NULL;
52 }
53
54 child = parent->children;
55 while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
56 child = child->next;
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 (attr_n == NULL) {
84 crm_trace("%s XML has no child element of %s type",
85 (const char *) parent->name, pcmk__s(node_name, "any"));
86 } else {
87 crm_trace("%s XML has no child element of %s type with %s='%s'",
88 (const char *) parent->name, pcmk__s(node_name, "any"),
89 attr_n, attr_v);
90 }
91 return NULL;
92}
93
103xmlNode *
104pcmk__xe_next(const xmlNode *xml, const char *element_name)
105{
106 for (xmlNode *next = (xml == NULL)? NULL : xml->next;
107 next != NULL; next = next->next) {
108 if ((next->type == XML_ELEMENT_NODE)
109 && ((element_name == NULL) || pcmk__xe_is(next, element_name))) {
110 return next;
111 }
112 }
113 return NULL;
114}
115
129int
130pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score,
131 int default_score)
132{
133 const char *value = NULL;
134
135 CRM_CHECK((xml != NULL) && (name != NULL), return EINVAL);
136 value = crm_element_value(xml, name);
137 return pcmk_parse_score(value, score, default_score);
138}
139
171int
172pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
173{
174 const char *old_value = NULL;
175
176 CRM_CHECK((target != NULL) && (name != NULL), return EINVAL);
177
178 if (value == NULL) {
179 // @TODO Maybe instead delete the attribute or set it to 0
180 return pcmk_rc_ok;
181 }
182
183 old_value = crm_element_value(target, name);
184
185 // If no previous value, skip to default case and set the value unexpanded.
186 if (old_value != NULL) {
187 const char *n = name;
188 const char *v = value;
189
190 // Stop at first character that differs between name and value
191 for (; (*n == *v) && (*n != '\0'); n++, v++);
192
193 // If value begins with name followed by a "++" or "+="
194 if ((*n == '\0')
195 && (*v++ == '+')
196 && ((*v == '+') || (*v == '='))) {
197
198 int add = 1;
199 int old_value_i = 0;
200 int rc = pcmk_rc_ok;
201
202 // If we're expanding ourselves, no previous value was set; use 0
203 if (old_value != value) {
204 rc = pcmk_parse_score(old_value, &old_value_i, 0);
205 if (rc != pcmk_rc_ok) {
206 // @TODO This is inconsistent with old_value==NULL
207 crm_trace("Using 0 before incrementing %s because '%s' "
208 "is not a score", name, old_value);
209 }
210 }
211
212 /* value="X++": new value of X is old_value + 1
213 * value="X+=Y": new value of X is old_value + Y (for some number Y)
214 */
215 if (*v != '+') {
216 rc = pcmk_parse_score(++v, &add, 0);
217 if (rc != pcmk_rc_ok) {
218 // @TODO We should probably skip expansion instead
219 crm_trace("Not incrementing %s because '%s' does not have "
220 "a valid increment", name, value);
221 }
222 }
223
224 crm_xml_add_int(target, name, pcmk__add_scores(old_value_i, add));
225 return pcmk_rc_ok;
226 }
227 }
228
229 // Default case: set the attribute unexpanded (with value treated literally)
230 if (old_value != value) {
231 crm_xml_add(target, name, value);
232 }
233 return pcmk_rc_ok;
234}
235
249int
250pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
251{
252 CRM_CHECK((src != NULL) && (target != NULL), return EINVAL);
253
254 for (xmlAttr *attr = pcmk__xe_first_attr(src); attr != NULL;
255 attr = attr->next) {
256
257 const char *name = (const char *) attr->name;
258 const char *value = pcmk__xml_attr_value(attr);
259
261 && (crm_element_value(target, name) != NULL)) {
262 continue;
263 }
264
267 } else {
268 crm_xml_add(target, name, value);
269 }
270 }
271
272 return pcmk_rc_ok;
273}
274
288static gint
289compare_xml_attr(gconstpointer a, gconstpointer b)
290{
291 const xmlAttr *attr_a = a;
292 const xmlAttr *attr_b = b;
293
294 return pcmk__strcmp((const char *) attr_a->name,
295 (const char *) attr_b->name, pcmk__str_none);
296}
297
309void
311{
312 GSList *attr_list = NULL;
313
314 for (xmlAttr *iter = pcmk__xe_first_attr(xml); iter != NULL;
315 iter = iter->next) {
316 attr_list = g_slist_prepend(attr_list, iter);
317 }
318 attr_list = g_slist_sort(attr_list, compare_xml_attr);
319
320 for (GSList *iter = attr_list; iter != NULL; iter = iter->next) {
321 xmlNode *attr = iter->data;
322
323 xmlUnlinkNode(attr);
324 xmlAddChild(xml, attr);
325 }
326 g_slist_free(attr_list);
327}
328
336void
337pcmk__xe_remove_attr(xmlNode *element, const char *name)
338{
339 if (name != NULL) {
340 pcmk__xa_remove(xmlHasProp(element, (const xmlChar *) name), false);
341 }
342}
343
358bool
359pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
360{
361 const char *name = user_data;
362
364 return true;
365}
366
378void
379pcmk__xe_remove_matching_attrs(xmlNode *element, bool force,
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, force) != pcmk_rc_ok) {
389 return;
390 }
391 }
392 }
393}
394
406xmlNode *
407pcmk__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, (const xmlChar *) name, NULL);
417 pcmk__mem_assert(node);
418
419 xmlDocSetRootElement(doc, node);
420
421 } else {
422 node = xmlNewChild(parent, NULL, (const xmlChar *) name, NULL);
423 pcmk__mem_assert(node);
424 }
425
427 return node;
428}
429
441G_GNUC_PRINTF(2, 3)
442void
443pcmk__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, (const xmlChar *) content);
478 free(buf);
479 }
480}
481
493G_GNUC_PRINTF(2, 3)
494void
495pcmk__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((const xmlChar *) id)) {
512 }
513 crm_xml_add(node, PCMK_XA_ID, id);
514 free(id);
515}
516
525const 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
569static void
570update_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
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
646done:
647 free(trace_s);
648}
649
668static bool
669delete_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
716int
717pcmk__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
746static void
747replace_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
760 // Replaced sections may have included relevant ACLs
761 pcmk__apply_acl(new);
762 }
763 pcmk__xml_mark_changes(old, new);
765}
766
784static bool
785replace_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
831int
832pcmk__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
856struct update_data {
857 xmlNode *update;
858 uint32_t flags;
859};
860
882static bool
883update_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
925int
926pcmk__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
951void
952pcmk__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
969void
970pcmk__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
978int
979pcmk__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
1014const char *
1015crm_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
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, (const xmlChar *) name, (const xmlChar *) 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
1069const char *
1070crm_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
1091const char *
1092crm_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
1119const char *
1120crm_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
1143const char *
1144crm_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
1167const char *
1168crm_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, (const xmlChar *) name);
1183 if (!attr || !attr->children) {
1184 return NULL;
1185 }
1186 return (const char *) attr->children->content;
1187}
1188
1200int
1201crm_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
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
1242int
1243pcmk__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
1290int
1291crm_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
1321int
1322crm_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
1358int
1359crm_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
1387int
1388crm_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
1435int
1436pcmk__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
1465char *
1466crm_element_value_copy(const xmlNode *data, const char *name)
1467{
1469}
1470
1479void
1480pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
1481{
1482 crm_xml_add(node, name, pcmk__btoa(value));
1483}
1484
1500int
1501pcmk__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
1537bool
1538pcmk__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
1556xmlNode *
1557expand_idref(xmlNode *input, xmlNode *top)
1558{
1559 return pcmk__xe_resolve_idref(input, top);
1560}
1561
1562void
1563crm_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
1576 crm_xml_add(xml, PCMK_XA_ID, id);
1577 free(id);
1578}
1579
1580xmlNode *
1581sorted_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);
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
void pcmk__apply_acl(xmlNode *xml)
Definition acl.c:216
bool pcmk__check_acl(xmlNode *xml, const char *attr_name, enum pcmk__xml_flags mode)
Definition acl.c:748
const char * parent
Definition cib.c:27
const char * name
Definition cib.c:26
uint64_t flags
Definition remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
char data[0]
Definition cpg.c:10
A dumping ground.
G_GNUC_INTERNAL int pcmk__xa_remove(xmlAttr *attr, bool force)
Definition xml_attr.c:45
G_GNUC_INTERNAL xmlDoc * pcmk__xml_new_doc(void)
Definition xml.c:499
G_GNUC_INTERNAL void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
G_GNUC_INTERNAL bool pcmk__xml_reset_node_flags(xmlNode *xml, void *user_data)
Definition xml.c:180
G_GNUC_INTERNAL void pcmk__xml_free_node(xmlNode *xml)
Definition xml.c:716
G_GNUC_INTERNAL void pcmk__mark_xml_attr_dirty(xmlAttr *a)
Definition xml_attr.c:77
G_GNUC_INTERNAL void pcmk__xml_new_private_data(xmlNode *xml)
Definition xml.c:387
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:112
struct crm_time_s crm_time_t
Definition iso8601.h:32
char * pcmk__epoch2str(const time_t *source, uint32_t flags)
Definition iso8601.c:2151
#define crm_warn(fmt, args...)
Definition logging.h:360
#define CRM_LOG_ASSERT(expr)
Definition logging.h:196
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_log_xml_trace(xml, text)
Definition logging.h:378
#define crm_trace(fmt, args...)
Definition logging.h:370
#define pcmk__if_tracing(if_action, else_action)
xmlNode * input
Functionality for manipulating name/value pairs.
void pcmk_free_nvpairs(GSList *nvpairs)
Free a list of name/value pairs.
Definition nvpair.c:103
Deprecated Pacemaker name-value pair API.
GSList * pcmk_sort_nvpairs(GSList *list)
Definition nvpair.c:681
void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
Definition nvpair.c:711
GSList * pcmk_xml_attrs2nvpairs(const xmlNode *xml)
Definition nvpair.c:687
pcmk__action_result_t result
Definition pcmk_fence.c:37
const char * target
Definition pcmk_fence.c:31
#define ENODATA
Definition portability.h:61
Function and executable result codes.
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_unpack_error
Definition results.h:122
@ pcmk_rc_bad_input
Definition results.h:119
#define pcmk_ok
Definition results.h:65
#define pcmk__assert(expr)
#define pcmk__mem_assert(ptr)
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition scores.c:34
int pcmk__add_scores(int score1, int score2)
Definition scores.c:159
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:498
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:92
@ pcmk__str_none
@ pcmk__str_casei
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition strings.c:1166
#define pcmk__str_copy(str)
#define PCMK__PARSE_INT_DEFAULT
Wrappers for and extensions to libxml2.
Deprecated Pacemaker XML API.
void crm_xml_sanitize_id(char *id)
Definition xml.c:1758
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
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.
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
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.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
void pcmk__xe_set_id(xmlNode *node, const char *format,...)
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.
int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool force, bool(*match)(xmlAttrPtr, void *), void *user_data)
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
const char * pcmk__xe_add_last_written(xmlNode *xe)
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
void crm_xml_set_id(xmlNode *xml, const char *format,...)
int pcmk__xe_get_flags(const xmlNode *xml, const char *name, uint32_t *dest, uint32_t default_value)
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
xmlNode * pcmk__xe_next(const xmlNode *xml, const char *element_name)
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
int pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
int pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score, int default_score)
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
void pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
int pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
void pcmk__xe_set_props(xmlNodePtr node,...)
int crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
Retrieve the long long integer value of an XML attribute.
int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
void pcmk__xe_set_content(xmlNode *node, const char *format,...)
int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
Retrieve the seconds-since-epoch value of an XML attribute.
#define LLSTRSIZE
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
const char * crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
Create an XML attribute with specified name and unsigned value.
void pcmk__xe_sort_attrs(xmlNode *xml)
Deprecated Pacemaker XML element API.
xmlNode * pcmk__xe_resolve_idref(xmlNode *xml, xmlNode *search)
Definition xml_idref.c:85
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
void pcmk__xml_sanitize_id(char *id)
Definition xml.c:674
@ pcmk__xaf_no_overwrite
Don't overwrite existing values.
@ pcmk__xaf_score_update
bool pcmk__xml_doc_all_flags_set(const xmlDoc *xml, uint32_t flags)
Definition xml.c:147
bool pcmk__xml_tree_foreach(xmlNode *xml, bool(*fn)(xmlNode *, void *), void *user_data)
Definition xml.c:87
@ pcmk__xf_acl_create
@ pcmk__xf_tracking
Tracking is enabled (set for document only)
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
void pcmk__xml_mark_changes(xmlNode *old_xml, xmlNode *new_xml)
Definition xml.c:1537
#define PCMK_XA_CIB_LAST_WRITTEN
Definition xml_names.h:244
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_ID_REF
Definition xml_names.h:303