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