1 /*
2 * Copyright 2004-2023 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 <string.h>
15 #include <ctype.h>
16 #include <glib.h>
17 #include <libxml/tree.h>
18
19 #include <crm/crm.h>
20 #include <crm/msg_xml.h>
21 #include <crm/common/xml.h>
22 #include <crm/common/xml_internal.h>
23 #include "crmcommon_private.h"
24
25 /*
26 * This file isolates handling of three types of name/value pairs:
27 *
28 * - pcmk_nvpair_t data type
29 * - XML attributes (<TAG ... NAME=VALUE ...>)
30 * - XML nvpair elements (<nvpair id=ID name=NAME value=VALUE>)
31 */
32
33 // pcmk_nvpair_t handling
34
35 /*!
36 * \internal
37 * \brief Allocate a new name/value pair
38 *
39 * \param[in] name New name (required)
40 * \param[in] value New value
41 *
42 * \return Newly allocated name/value pair
43 * \note The caller is responsible for freeing the result with
44 * \c pcmk__free_nvpair().
45 */
46 static pcmk_nvpair_t *
47 pcmk__new_nvpair(const char *name, const char *value)
48 {
49 pcmk_nvpair_t *nvpair = NULL;
50
51 CRM_ASSERT(name);
52
53 nvpair = calloc(1, sizeof(pcmk_nvpair_t));
54 CRM_ASSERT(nvpair);
55
56 pcmk__str_update(&nvpair->name, name);
57 pcmk__str_update(&nvpair->value, value);
58 return nvpair;
59 }
60
61 /*!
62 * \internal
63 * \brief Free a name/value pair
64 *
65 * \param[in,out] nvpair Name/value pair to free
66 */
67 static void
68 pcmk__free_nvpair(gpointer data)
69 {
70 if (data) {
71 pcmk_nvpair_t *nvpair = data;
72
73 free(nvpair->name);
74 free(nvpair->value);
75 free(nvpair);
76 }
77 }
78
79 /*!
80 * \brief Prepend a name/value pair to a list
81 *
82 * \param[in,out] nvpairs List to modify
83 * \param[in] name New entry's name
84 * \param[in] value New entry's value
85 *
86 * \return New head of list
87 * \note The caller is responsible for freeing the list with
88 * \c pcmk_free_nvpairs().
89 */
90 GSList *
91 pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
92 {
93 return g_slist_prepend(nvpairs, pcmk__new_nvpair(name, value));
94 }
95
96 /*!
97 * \brief Free a list of name/value pairs
98 *
99 * \param[in,out] list List to free
100 */
101 void
102 pcmk_free_nvpairs(GSList *nvpairs)
103 {
104 g_slist_free_full(nvpairs, pcmk__free_nvpair);
105 }
106
107 /*!
108 * \internal
109 * \brief Compare two name/value pairs
110 *
111 * \param[in] a First name/value pair to compare
112 * \param[in] b Second name/value pair to compare
113 *
114 * \return 0 if a == b, 1 if a > b, -1 if a < b
115 */
116 static gint
117 pcmk__compare_nvpair(gconstpointer a, gconstpointer b)
118 {
119 int rc = 0;
120 const pcmk_nvpair_t *pair_a = a;
121 const pcmk_nvpair_t *pair_b = b;
122
123 CRM_ASSERT(a != NULL);
124 CRM_ASSERT(pair_a->name != NULL);
125
126 CRM_ASSERT(b != NULL);
127 CRM_ASSERT(pair_b->name != NULL);
128
129 rc = strcmp(pair_a->name, pair_b->name);
130 if (rc < 0) {
131 return -1;
132 } else if (rc > 0) {
133 return 1;
134 }
135 return 0;
136 }
137
138 /*!
139 * \brief Sort a list of name/value pairs
140 *
141 * \param[in,out] list List to sort
142 *
143 * \return New head of list
144 */
145 GSList *
146 pcmk_sort_nvpairs(GSList *list)
147 {
148 return g_slist_sort(list, pcmk__compare_nvpair);
149 }
150
151 /*!
152 * \brief Create a list of name/value pairs from an XML node's attributes
153 *
154 * \param[in] XML to parse
155 *
156 * \return New list of name/value pairs
157 * \note It is the caller's responsibility to free the list with
158 * \c pcmk_free_nvpairs().
159 */
160 GSList *
161 pcmk_xml_attrs2nvpairs(const xmlNode *xml)
162 {
163 GSList *result = NULL;
164
165 for (xmlAttrPtr iter = pcmk__xe_first_attr(xml); iter != NULL;
166 iter = iter->next) {
167
168 result = pcmk_prepend_nvpair(result,
169 (const char *) iter->name,
170 (const char *) pcmk__xml_attr_value(iter));
171 }
172 return result;
173 }
174
175 /*!
176 * \internal
177 * \brief Add an XML attribute corresponding to a name/value pair
178 *
179 * Suitable for glib list iterators, this function adds a NAME=VALUE
180 * XML attribute based on a given name/value pair.
181 *
182 * \param[in] data Name/value pair
183 * \param[out] user_data XML node to add attributes to
184 */
185 static void
186 pcmk__nvpair_add_xml_attr(gpointer data, gpointer user_data)
187 {
188 pcmk_nvpair_t *pair = data;
189 xmlNode *parent = user_data;
190
191 crm_xml_add(parent, pair->name, pair->value);
192 }
193
194 /*!
195 * \brief Add XML attributes based on a list of name/value pairs
196 *
197 * \param[in,out] list List of name/value pairs
198 * \param[in,out] xml XML node to add attributes to
199 */
200 void
201 pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
202 {
203 g_slist_foreach(list, pcmk__nvpair_add_xml_attr, xml);
204 }
205
206 // convenience function for name=value strings
207
208 /*!
209 * \internal
210 * \brief Extract the name and value from an input string formatted as "name=value".
211 * If unable to extract them, they are returned as NULL.
212 *
213 * \param[in] input The input string, likely from the command line
214 * \param[out] name Everything before the first '=' in the input string
215 * \param[out] value Everything after the first '=' in the input string
216 *
217 * \return 2 if both name and value could be extracted, 1 if only one could, and
218 * and error code otherwise
219 */
220 int
221 pcmk__scan_nvpair(const char *input, char **name, char **value)
222 {
223 #ifdef HAVE_SSCANF_M
224 *name = NULL;
225 *value = NULL;
|
(1) Event allocate_storage: |
Allocating storage in "*name" to hold parsed value. |
|
(2) Event path: |
Condition "sscanf(input, "%m[^=]=%m[^\n]", name, value) <= 0", taking true branch. |
226 if (sscanf(input, "%m[^=]=%m[^\n]", name, value) <= 0) {
227 return -pcmk_err_bad_nvpair;
228 }
229 #else
230 char *sep = NULL;
231 *name = NULL;
232 *value = NULL;
233
234 sep = strstr(optarg, "=");
235 if (sep == NULL) {
236 return -pcmk_err_bad_nvpair;
237 }
238
239 *name = strndup(input, sep-input);
240
241 if (*name == NULL) {
242 return -ENOMEM;
243 }
244
245 /* If the last char in optarg is =, the user gave no
246 * value for the option. Leave it as NULL.
247 */
248 if (*(sep+1) != '\0') {
249 *value = strdup(sep+1);
250
251 if (*value == NULL) {
252 return -ENOMEM;
253 }
254 }
255 #endif
256
257 if (*name != NULL && *value != NULL) {
258 return 2;
259 } else if (*name != NULL || *value != NULL) {
260 return 1;
261 } else {
262 return -pcmk_err_bad_nvpair;
263 }
264 }
265
266 /*!
267 * \internal
268 * \brief Format a name/value pair.
269 *
270 * Units can optionally be provided for the value. Note that unlike most
271 * formatting functions, this one returns the formatted string. It is
272 * assumed that the most common use of this function will be to build up
273 * a string to be output as part of other functions.
274 *
275 * \note The caller is responsible for freeing the return value after use.
276 *
277 * \param[in] name The name of the nvpair.
278 * \param[in] value The value of the nvpair.
279 * \param[in] units Optional units for the value, or NULL.
280 *
281 * \return Newly allocated string with name/value pair
282 */
283 char *
284 pcmk__format_nvpair(const char *name, const char *value, const char *units)
285 {
286 return crm_strdup_printf("%s=\"%s%s\"", name, value, units ? units : "");
287 }
288
289 // XML attribute handling
290
291 /*!
292 * \brief Create an XML attribute with specified name and value
293 *
294 * \param[in,out] node XML node to modify
295 * \param[in] name Attribute name to set
296 * \param[in] value Attribute value to set
297 *
298 * \return New value on success, \c NULL otherwise
299 * \note This does nothing if node, name, or value are \c NULL or empty.
300 */
301 const char *
302 crm_xml_add(xmlNode *node, const char *name, const char *value)
303 {
304 bool dirty = FALSE;
305 xmlAttr *attr = NULL;
306
307 CRM_CHECK(node != NULL, return NULL);
308 CRM_CHECK(name != NULL, return NULL);
309
310 if (value == NULL) {
311 return NULL;
312 }
313
314 if (pcmk__tracking_xml_changes(node, FALSE)) {
315 const char *old = crm_element_value(node, name);
316
317 if (old == NULL || value == NULL || strcmp(old, value) != 0) {
318 dirty = TRUE;
319 }
320 }
321
322 if (dirty && (pcmk__check_acl(node, name, pcmk__xf_acl_create) == FALSE)) {
323 crm_trace("Cannot add %s=%s to %s", name, value, node->name);
324 return NULL;
325 }
326
327 attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
328 if (dirty) {
329 pcmk__mark_xml_attr_dirty(attr);
330 }
331
332 CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
333 return (char *)attr->children->content;
334 }
335
336 /*!
337 * \brief Create an XML attribute with specified name and integer value
338 *
339 * This is like \c crm_xml_add() but taking an integer value.
340 *
341 * \param[in,out] node XML node to modify
342 * \param[in] name Attribute name to set
343 * \param[in] value Attribute value to set
344 *
345 * \return New value as string on success, \c NULL otherwise
346 * \note This does nothing if node or name are \c NULL or empty.
347 */
348 const char *
349 crm_xml_add_int(xmlNode *node, const char *name, int value)
350 {
351 char *number = pcmk__itoa(value);
352 const char *added = crm_xml_add(node, name, number);
353
354 free(number);
355 return added;
356 }
357
358 /*!
359 * \brief Create an XML attribute with specified name and unsigned value
360 *
361 * This is like \c crm_xml_add() but taking a guint value.
362 *
363 * \param[in,out] node XML node to modify
364 * \param[in] name Attribute name to set
365 * \param[in] ms Attribute value to set
366 *
367 * \return New value as string on success, \c NULL otherwise
368 * \note This does nothing if node or name are \c NULL or empty.
369 */
370 const char *
371 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
372 {
373 char *number = crm_strdup_printf("%u", ms);
374 const char *added = crm_xml_add(node, name, number);
375
376 free(number);
377 return added;
378 }
379
380 // Maximum size of null-terminated string representation of 64-bit integer
381 // -9223372036854775808
382 #define LLSTRSIZE 21
383
384 /*!
385 * \brief Create an XML attribute with specified name and long long int value
386 *
387 * This is like \c crm_xml_add() but taking a long long int value. It is a
388 * useful equivalent for defined types like time_t, etc.
389 *
390 * \param[in,out] xml XML node to modify
391 * \param[in] name Attribute name to set
392 * \param[in] value Attribute value to set
393 *
394 * \return New value as string on success, \c NULL otherwise
395 * \note This does nothing if xml or name are \c NULL or empty.
396 * This does not support greater than 64-bit values.
397 */
398 const char *
399 crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
400 {
401 char s[LLSTRSIZE] = { '\0', };
402
403 if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
404 return NULL;
405 }
406 return crm_xml_add(xml, name, s);
407 }
408
409 /*!
410 * \brief Create XML attributes for seconds and microseconds
411 *
412 * This is like \c crm_xml_add() but taking a struct timeval.
413 *
414 * \param[in,out] xml XML node to modify
415 * \param[in] name_sec Name of XML attribute for seconds
416 * \param[in] name_usec Name of XML attribute for microseconds (or NULL)
417 * \param[in] value Time value to set
418 *
419 * \return New seconds value as string on success, \c NULL otherwise
420 * \note This does nothing if xml, name_sec, or value is \c NULL.
421 */
422 const char *
423 crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
424 const struct timeval *value)
425 {
426 const char *added = NULL;
427
428 if (xml && name_sec && value) {
429 added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
430 if (added && name_usec) {
431 // Any error is ignored (we successfully added seconds)
432 crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
433 }
434 }
435 return added;
436 }
437
438 /*!
439 * \brief Retrieve the value of an XML attribute
440 *
441 * \param[in] data XML node to check
442 * \param[in] name Attribute name to check
443 *
444 * \return Value of specified attribute (may be \c NULL)
445 */
446 const char *
447 crm_element_value(const xmlNode *data, const char *name)
448 {
449 xmlAttr *attr = NULL;
450
451 if (data == NULL) {
452 crm_err("Couldn't find %s in NULL", name ? name : "<null>");
453 CRM_LOG_ASSERT(data != NULL);
454 return NULL;
455
456 } else if (name == NULL) {
457 crm_err("Couldn't find NULL in %s", data->name);
458 return NULL;
459 }
460
461 /* The first argument to xmlHasProp() has always been const,
462 * but libxml2 <2.9.2 didn't declare that, so cast it
463 */
464 attr = xmlHasProp((xmlNode *) data, (pcmkXmlStr) name);
465 if (!attr || !attr->children) {
466 return NULL;
467 }
468 return (const char *) attr->children->content;
469 }
470
471 /*!
472 * \brief Retrieve the integer value of an XML attribute
473 *
474 * This is like \c crm_element_value() but getting the value as an integer.
475 *
476 * \param[in] data XML node to check
477 * \param[in] name Attribute name to check
478 * \param[out] dest Where to store element value
479 *
480 * \return 0 on success, -1 otherwise
481 */
482 int
483 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
484 {
485 const char *value = NULL;
486
487 CRM_CHECK(dest != NULL, return -1);
488 value = crm_element_value(data, name);
489 if (value) {
490 long long value_ll;
491
492 if ((pcmk__scan_ll(value, &value_ll, 0LL) != pcmk_rc_ok)
493 || (value_ll < INT_MIN) || (value_ll > INT_MAX)) {
494 *dest = PCMK__PARSE_INT_DEFAULT;
495 } else {
496 *dest = (int) value_ll;
497 return 0;
498 }
499 }
500 return -1;
501 }
502
503 /*!
504 * \brief Retrieve the long long integer value of an XML attribute
505 *
506 * This is like \c crm_element_value() but getting the value as a long long int.
507 *
508 * \param[in] data XML node to check
509 * \param[in] name Attribute name to check
510 * \param[out] dest Where to store element value
511 *
512 * \return 0 on success, -1 otherwise
513 */
514 int
515 crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
516 {
517 const char *value = NULL;
518
519 CRM_CHECK(dest != NULL, return -1);
520 value = crm_element_value(data, name);
521 if ((value != NULL)
522 && (pcmk__scan_ll(value, dest, PCMK__PARSE_INT_DEFAULT) == pcmk_rc_ok)) {
523 return 0;
524 }
525 return -1;
526 }
527
528 /*!
529 * \brief Retrieve the millisecond value of an XML attribute
530 *
531 * This is like \c crm_element_value() but returning the value as a guint.
532 *
533 * \param[in] data XML node to check
534 * \param[in] name Attribute name to check
535 * \param[out] dest Where to store attribute value
536 *
537 * \return \c pcmk_ok on success, -1 otherwise
538 */
539 int
540 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
541 {
542 const char *value = NULL;
543 long long value_ll;
544
545 CRM_CHECK(dest != NULL, return -1);
546 *dest = 0;
547 value = crm_element_value(data, name);
548 if ((pcmk__scan_ll(value, &value_ll, 0LL) != pcmk_rc_ok)
549 || (value_ll < 0) || (value_ll > G_MAXUINT)) {
550 return -1;
551 }
552 *dest = (guint) value_ll;
553 return pcmk_ok;
554 }
555
556 /*!
557 * \brief Retrieve the seconds-since-epoch value of an XML attribute
558 *
559 * This is like \c crm_element_value() but returning the value as a time_t.
560 *
561 * \param[in] xml XML node to check
562 * \param[in] name Attribute name to check
563 * \param[out] dest Where to store attribute value
564 *
565 * \return \c pcmk_ok on success, -1 otherwise
566 */
567 int
568 crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
569 {
570 long long value_ll = 0;
571
572 if (crm_element_value_ll(xml, name, &value_ll) < 0) {
573 return -1;
574 }
575
576 /* Unfortunately, we can't do any bounds checking, since time_t has neither
577 * standardized bounds nor constants defined for them.
578 */
579 *dest = (time_t) value_ll;
580 return pcmk_ok;
581 }
582
583 /*!
584 * \brief Retrieve the value of XML second/microsecond attributes as time
585 *
586 * This is like \c crm_element_value() but returning value as a struct timeval.
587 *
588 * \param[in] xml XML to parse
589 * \param[in] name_sec Name of XML attribute for seconds
590 * \param[in] name_usec Name of XML attribute for microseconds
591 * \param[out] dest Where to store result
592 *
593 * \return \c pcmk_ok on success, -errno on error
594 * \note Values default to 0 if XML or XML attribute does not exist
595 */
596 int
597 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
598 const char *name_usec, struct timeval *dest)
599 {
600 long long value_i = 0;
601
602 CRM_CHECK(dest != NULL, return -EINVAL);
603 dest->tv_sec = 0;
604 dest->tv_usec = 0;
605
606 if (xml == NULL) {
607 return pcmk_ok;
608 }
609
610 /* Unfortunately, we can't do any bounds checking, since there are no
611 * constants provided for the bounds of time_t and suseconds_t, and
612 * calculating them isn't worth the effort. If there are XML values
613 * beyond the native sizes, there will probably be worse problems anyway.
614 */
615
616 // Parse seconds
617 errno = 0;
618 if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
619 return -errno;
620 }
621 dest->tv_sec = (time_t) value_i;
622
623 // Parse microseconds
624 if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
625 return -errno;
626 }
627 dest->tv_usec = (suseconds_t) value_i;
628
629 return pcmk_ok;
630 }
631
632 /*!
633 * \brief Retrieve a copy of the value of an XML attribute
634 *
635 * This is like \c crm_element_value() but allocating new memory for the result.
636 *
637 * \param[in] data XML node to check
638 * \param[in] name Attribute name to check
639 *
640 * \return Value of specified attribute (may be \c NULL)
641 * \note The caller is responsible for freeing the result.
642 */
643 char *
644 crm_element_value_copy(const xmlNode *data, const char *name)
645 {
646 char *value_copy = NULL;
647
648 pcmk__str_update(&value_copy, crm_element_value(data, name));
649 return value_copy;
650 }
651
652 /*!
653 * \brief Add hash table entry to XML as (possibly legacy) name/value
654 *
655 * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
656 * and value, with an XML node passed as user data, and adds an XML attribute
657 * with the specified name and value if it does not already exist. If the key
658 * name starts with a digit, this will instead add a \<param name=NAME
659 * value=VALUE/> child to the XML (for legacy compatibility with heartbeat).
660 *
661 * \param[in] key Key of hash table entry
662 * \param[in] value Value of hash table entry
663 * \param[in,out] user_data XML node
664 */
665 void
666 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
667 {
668 const char *name = key;
669 const char *s_value = value;
670
671 xmlNode *xml_node = user_data;
672
673 if (isdigit(name[0])) {
674 xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
675
676 crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
677 crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
678
679 } else if (crm_element_value(xml_node, name) == NULL) {
680 crm_xml_add(xml_node, name, s_value);
681 crm_trace("dumped: %s=%s", name, s_value);
682
683 } else {
684 crm_trace("duplicate: %s=%s", name, s_value);
685 }
686 }
687
688 /*!
689 * \brief Set XML attribute based on hash table entry
690 *
691 * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
692 * and value, with an XML node passed as user data, and adds an XML attribute
693 * with the specified name and value if it does not already exist.
694 *
695 * \param[in] key Key of hash table entry
696 * \param[in] value Value of hash table entry
697 * \param[in,out] user_data XML node
698 */
699 void
700 hash2field(gpointer key, gpointer value, gpointer user_data)
701 {
702 const char *name = key;
703 const char *s_value = value;
704
705 xmlNode *xml_node = user_data;
706
707 if (crm_element_value(xml_node, name) == NULL) {
708 crm_xml_add(xml_node, name, s_value);
709
710 } else {
711 crm_trace("duplicate: %s=%s", name, s_value);
712 }
713 }
714
715 /*!
716 * \brief Set XML attribute based on hash table entry, as meta-attribute name
717 *
718 * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
719 * and value, with an XML node passed as user data, and adds an XML attribute
720 * with the meta-attribute version of the specified name and value if it does
721 * not already exist and if the name does not appear to be cluster-internal.
722 *
723 * \param[in] key Key of hash table entry
724 * \param[in] value Value of hash table entry
725 * \param[in,out] user_data XML node
726 */
727 void
728 hash2metafield(gpointer key, gpointer value, gpointer user_data)
729 {
730 char *crm_name = NULL;
731
732 if (key == NULL || value == NULL) {
733 return;
734 }
735
736 /* Filter out cluster-generated attributes that contain a '#' or ':'
737 * (like fail-count and last-failure).
738 */
739 for (crm_name = key; *crm_name; ++crm_name) {
740 if ((*crm_name == '#') || (*crm_name == ':')) {
741 return;
742 }
743 }
744
745 crm_name = crm_meta_name(key);
746 hash2field(crm_name, value, user_data);
747 free(crm_name);
748 }
749
750 // nvpair handling
751
752 /*!
753 * \brief Create an XML name/value pair
754 *
755 * \param[in,out] parent If not \c NULL, make new XML node a child of this one
756 * \param[in] id Set this as XML ID (or NULL to auto-generate)
757 * \param[in] name Name to use
758 * \param[in] value Value to use
759 *
760 * \return New XML object on success, \c NULL otherwise
761 */
762 xmlNode *
763 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
764 const char *value)
765 {
766 xmlNode *nvp;
767
768 /* id can be NULL so we auto-generate one, and name can be NULL if this
769 * will be used to delete a name/value pair by ID, but both can't be NULL
770 */
771 CRM_CHECK(id || name, return NULL);
772
773 nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
774 CRM_CHECK(nvp, return NULL);
775
776 if (id) {
777 crm_xml_add(nvp, XML_ATTR_ID, id);
778 } else {
779 const char *parent_id = ID(parent);
780
781 crm_xml_set_id(nvp, "%s-%s",
782 (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
783 }
784 crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
785 crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
786 return nvp;
787 }
788
789 /*!
790 * \brief Add XML nvpair element based on hash table entry
791 *
792 * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
793 * and value, with an XML node passed as the user data, and adds an \c nvpair
794 * XML element with the specified name and value.
795 *
796 * \param[in] key Key of hash table entry
797 * \param[in] value Value of hash table entry
798 * \param[in,out] user_data XML node
799 */
800 void
801 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
802 {
803 const char *name = key;
804 const char *s_value = value;
805 xmlNode *xml_node = user_data;
806
807 crm_create_nvpair_xml(xml_node, name, name, s_value);
808 crm_trace("dumped: name=%s value=%s", name, s_value);
809 }
810
811 /*!
812 * \brief Retrieve XML attributes as a hash table
813 *
814 * Given an XML element, this will look for any \<attributes> element child,
815 * creating a hash table of (newly allocated string) name/value pairs taken
816 * first from the attributes element's NAME=VALUE XML attributes, and then
817 * from any \<param name=NAME value=VALUE> children of attributes.
818 *
819 * \param[in] XML node to parse
820 *
821 * \return Hash table with name/value pairs
822 * \note It is the caller's responsibility to free the result using
823 * \c g_hash_table_destroy().
824 */
825 GHashTable *
826 xml2list(const xmlNode *parent)
827 {
828 xmlNode *child = NULL;
829 xmlAttrPtr pIter = NULL;
830 xmlNode *nvpair_list = NULL;
831 GHashTable *nvpair_hash = pcmk__strkey_table(free, free);
832
833 CRM_CHECK(parent != NULL, return nvpair_hash);
834
835 nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
836 if (nvpair_list == NULL) {
837 crm_trace("No attributes in %s", parent->name);
838 crm_log_xml_trace(parent, "No attributes for resource op");
839 }
840
841 crm_log_xml_trace(nvpair_list, "Unpacking");
842
843 for (pIter = pcmk__xe_first_attr(nvpair_list); pIter != NULL;
844 pIter = pIter->next) {
845
846 const char *p_name = (const char *)pIter->name;
847 const char *p_value = pcmk__xml_attr_value(pIter);
848
849 crm_trace("Added %s=%s", p_name, p_value);
850
851 g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
852 }
853
854 for (child = pcmk__xml_first_child(nvpair_list); child != NULL;
855 child = pcmk__xml_next(child)) {
856
857 if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
858 const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
859 const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
860
861 crm_trace("Added %s=%s", key, value);
862 if (key != NULL && value != NULL) {
863 g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
864 }
865 }
866 }
867
868 return nvpair_hash;
869 }
870
871 void
872 pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
873 {
874 crm_xml_add(node, name, value ? XML_BOOLEAN_TRUE : XML_BOOLEAN_FALSE);
875 }
876
877 int
878 pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
879 {
880 const char *xml_value = NULL;
881 int ret, rc;
882
883 if (node == NULL) {
884 return ENODATA;
885 } else if (name == NULL || value == NULL) {
886 return EINVAL;
887 }
888
889 xml_value = crm_element_value(node, name);
890
891 if (xml_value == NULL) {
892 return ENODATA;
893 }
894
895 rc = crm_str_to_boolean(xml_value, &ret);
896 if (rc == 1) {
897 *value = ret;
898 return pcmk_rc_ok;
899 } else {
900 return pcmk_rc_bad_input;
901 }
902 }
903
904 bool
905 pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
906 {
907 bool value = false;
908 int rc;
909
910 rc = pcmk__xe_get_bool_attr(node, name, &value);
911 return rc == pcmk_rc_ok && value == true;
912 }
913
914 // Deprecated functions kept only for backward API compatibility
915 // LCOV_EXCL_START
916
917 #include <crm/common/util_compat.h>
918
919 int
920 pcmk_scan_nvpair(const char *input, char **name, char **value)
921 {
922 return pcmk__scan_nvpair(input, name, value);
923 }
924
925 char *
926 pcmk_format_nvpair(const char *name, const char *value,
927 const char *units)
928 {
929 return pcmk__format_nvpair(name, value, units);
930 }
931
932 char *
933 pcmk_format_named_time(const char *name, time_t epoch_time)
934 {
935 char *now_s = pcmk__epoch2str(&epoch_time, 0);
936 char *result = crm_strdup_printf("%s=\"%s\"", name, pcmk__s(now_s, ""));
937
938 free(now_s);
939 return result;
940 }
941
942 const char *
943 crm_xml_replace(xmlNode *node, const char *name, const char *value)
944 {
945 bool dirty = FALSE;
946 xmlAttr *attr = NULL;
947 const char *old_value = NULL;
948
949 CRM_CHECK(node != NULL, return NULL);
950 CRM_CHECK(name != NULL && name[0] != 0, return NULL);
951
952 old_value = crm_element_value(node, name);
953
954 /* Could be re-setting the same value */
955 CRM_CHECK(old_value != value, return value);
956
957 if (pcmk__check_acl(node, name, pcmk__xf_acl_write) == FALSE) {
958 /* Create a fake object linked to doc->_private instead? */
959 crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
960 return NULL;
961
962 } else if (old_value && !value) {
963 xml_remove_prop(node, name);
964 return NULL;
965 }
966
967 if (pcmk__tracking_xml_changes(node, FALSE)) {
968 if (!old_value || !value || !strcmp(old_value, value)) {
969 dirty = TRUE;
970 }
971 }
972
973 attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
974 if (dirty) {
975 pcmk__mark_xml_attr_dirty(attr);
976 }
977 CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
978 return (char *) attr->children->content;
979 }
980
981 // LCOV_EXCL_STOP
982 // End deprecated API
983