root/lib/common/nvpair.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcmk__new_nvpair
  2. pcmk__free_nvpair
  3. pcmk_prepend_nvpair
  4. pcmk_free_nvpairs
  5. pcmk__compare_nvpair
  6. pcmk_sort_nvpairs
  7. pcmk_xml_attrs2nvpairs
  8. pcmk__nvpair_add_xml_attr
  9. pcmk_nvpairs2xml_attrs
  10. pcmk__scan_nvpair
  11. pcmk__format_nvpair
  12. pcmk__xe_set_attr_force
  13. crm_xml_add
  14. crm_xml_add_int
  15. crm_xml_add_ms
  16. crm_xml_add_ll
  17. crm_xml_add_timeval
  18. crm_element_value
  19. crm_element_value_int
  20. pcmk__xe_get_flags
  21. crm_element_value_ll
  22. crm_element_value_ms
  23. crm_element_value_epoch
  24. crm_element_value_timeval
  25. pcmk__xe_get_datetime
  26. crm_element_value_copy
  27. hash2smartfield
  28. hash2field
  29. hash2metafield
  30. crm_create_nvpair_xml
  31. hash2nvpair
  32. xml2list
  33. pcmk__xe_set_bool_attr
  34. pcmk__xe_get_bool_attr
  35. pcmk__xe_attr_is_true
  36. crm_meta_name
  37. crm_meta_value
  38. pcmk_scan_nvpair
  39. pcmk_format_nvpair
  40. pcmk_format_named_time
  41. crm_xml_replace

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

/* [previous][next][first][last][top][bottom][index][help] */