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__format_named_time
  13. crm_xml_add
  14. crm_xml_replace
  15. crm_xml_add_int
  16. crm_xml_add_ms
  17. crm_xml_add_ll
  18. crm_xml_add_timeval
  19. crm_element_value
  20. crm_element_value_int
  21. crm_element_value_ll
  22. crm_element_value_ms
  23. crm_element_value_epoch
  24. crm_element_value_timeval
  25. crm_element_value_copy
  26. hash2smartfield
  27. hash2field
  28. hash2metafield
  29. crm_create_nvpair_xml
  30. hash2nvpair
  31. xml2list
  32. pcmk__xe_set_bool_attr
  33. pcmk__xe_get_bool_attr
  34. pcmk__xe_attr_is_true
  35. pcmk_scan_nvpair
  36. pcmk_format_nvpair
  37. pcmk_format_named_time

   1 /*
   2  * Copyright 2004-2022 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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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] nvpair  Name/value pair to free
  66  */
  67 static void
  68 pcmk__free_nvpair(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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] list  List to free
 100  */
 101 void
 102 pcmk_free_nvpairs(GSList *nvpairs)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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]     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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 222 {
 223 #ifdef SSCANF_HAS_M
 224     *name = NULL;
 225     *value = NULL;
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 285 {
 286     return crm_strdup_printf("%s=\"%s%s\"", name, value, units ? units : "");
 287 }
 288 
 289 /*!
 290  * \internal
 291  * \brief Format a name/time pair.
 292  *
 293  * See pcmk__format_nvpair() for more details.
 294  *
 295  * \note The caller is responsible for freeing the return value after use.
 296  *
 297  * \param[in]     name       The name for the time.
 298  * \param[in]     epoch_time The time to format.
 299  *
 300  * \return Newly allocated string with name/value pair
 301  */
 302 char *
 303 pcmk__format_named_time(const char *name, time_t epoch_time)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305     const char *now_str = pcmk__epoch2str(&epoch_time);
 306 
 307     return crm_strdup_printf("%s=\"%s\"", name, now_str ? now_str : "");
 308 }
 309 
 310 // XML attribute handling
 311 
 312 /*!
 313  * \brief Create an XML attribute with specified name and value
 314  *
 315  * \param[in,out] node   XML node to modify
 316  * \param[in]     name   Attribute name to set
 317  * \param[in]     value  Attribute value to set
 318  *
 319  * \return New value on success, \c NULL otherwise
 320  * \note This does nothing if node, name, or value are \c NULL or empty.
 321  */
 322 const char *
 323 crm_xml_add(xmlNode *node, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 324 {
 325     bool dirty = FALSE;
 326     xmlAttr *attr = NULL;
 327 
 328     CRM_CHECK(node != NULL, return NULL);
 329     CRM_CHECK(name != NULL, return NULL);
 330 
 331     if (value == NULL) {
 332         return NULL;
 333     }
 334 #if XML_PARANOIA_CHECKS
 335     {
 336         const char *old_value = NULL;
 337 
 338         old_value = crm_element_value(node, name);
 339 
 340         /* Could be re-setting the same value */
 341         CRM_CHECK(old_value != value,
 342                   crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
 343                   return value);
 344     }
 345 #endif
 346 
 347     if (pcmk__tracking_xml_changes(node, FALSE)) {
 348         const char *old = crm_element_value(node, name);
 349 
 350         if (old == NULL || value == NULL || strcmp(old, value) != 0) {
 351             dirty = TRUE;
 352         }
 353     }
 354 
 355     if (dirty && (pcmk__check_acl(node, name, pcmk__xf_acl_create) == FALSE)) {
 356         crm_trace("Cannot add %s=%s to %s", name, value, node->name);
 357         return NULL;
 358     }
 359 
 360     attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
 361     if (dirty) {
 362         pcmk__mark_xml_attr_dirty(attr);
 363     }
 364 
 365     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
 366     return (char *)attr->children->content;
 367 }
 368 
 369 /*!
 370  * \brief Replace an XML attribute with specified name and (possibly NULL) value
 371  *
 372  * \param[in,out] node   XML node to modify
 373  * \param[in]     name   Attribute name to set
 374  * \param[in]     value  Attribute value to set
 375  *
 376  * \return New value on success, \c NULL otherwise
 377  * \note This does nothing if node or name is \c NULL or empty.
 378  */
 379 const char *
 380 crm_xml_replace(xmlNode *node, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 381 {
 382     bool dirty = FALSE;
 383     xmlAttr *attr = NULL;
 384     const char *old_value = NULL;
 385 
 386     CRM_CHECK(node != NULL, return NULL);
 387     CRM_CHECK(name != NULL && name[0] != 0, return NULL);
 388 
 389     old_value = crm_element_value(node, name);
 390 
 391     /* Could be re-setting the same value */
 392     CRM_CHECK(old_value != value, return value);
 393 
 394     if (pcmk__check_acl(node, name, pcmk__xf_acl_write) == FALSE) {
 395         /* Create a fake object linked to doc->_private instead? */
 396         crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
 397         return NULL;
 398 
 399     } else if (old_value && !value) {
 400         xml_remove_prop(node, name);
 401         return NULL;
 402     }
 403 
 404     if (pcmk__tracking_xml_changes(node, FALSE)) {
 405         if (!old_value || !value || !strcmp(old_value, value)) {
 406             dirty = TRUE;
 407         }
 408     }
 409 
 410     attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
 411     if (dirty) {
 412         pcmk__mark_xml_attr_dirty(attr);
 413     }
 414     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
 415     return (char *) attr->children->content;
 416 }
 417 
 418 /*!
 419  * \brief Create an XML attribute with specified name and integer value
 420  *
 421  * This is like \c crm_xml_add() but taking an integer value.
 422  *
 423  * \param[in,out] node   XML node to modify
 424  * \param[in]     name   Attribute name to set
 425  * \param[in]     value  Attribute value to set
 426  *
 427  * \return New value as string on success, \c NULL otherwise
 428  * \note This does nothing if node or name are \c NULL or empty.
 429  */
 430 const char *
 431 crm_xml_add_int(xmlNode *node, const char *name, int value)
     /* [previous][next][first][last][top][bottom][index][help] */
 432 {
 433     char *number = pcmk__itoa(value);
 434     const char *added = crm_xml_add(node, name, number);
 435 
 436     free(number);
 437     return added;
 438 }
 439 
 440 /*!
 441  * \brief Create an XML attribute with specified name and unsigned value
 442  *
 443  * This is like \c crm_xml_add() but taking a guint value.
 444  *
 445  * \param[in,out] node   XML node to modify
 446  * \param[in]     name   Attribute name to set
 447  * \param[in]     ms     Attribute value to set
 448  *
 449  * \return New value as string on success, \c NULL otherwise
 450  * \note This does nothing if node or name are \c NULL or empty.
 451  */
 452 const char *
 453 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 454 {
 455     char *number = crm_strdup_printf("%u", ms);
 456     const char *added = crm_xml_add(node, name, number);
 457 
 458     free(number);
 459     return added;
 460 }
 461 
 462 // Maximum size of null-terminated string representation of 64-bit integer
 463 // -9223372036854775808
 464 #define LLSTRSIZE 21
 465 
 466 /*!
 467  * \brief Create an XML attribute with specified name and long long int value
 468  *
 469  * This is like \c crm_xml_add() but taking a long long int value. It is a
 470  * useful equivalent for defined types like time_t, etc.
 471  *
 472  * \param[in,out] xml    XML node to modify
 473  * \param[in]     name   Attribute name to set
 474  * \param[in]     value  Attribute value to set
 475  *
 476  * \return New value as string on success, \c NULL otherwise
 477  * \note This does nothing if xml or name are \c NULL or empty.
 478  *       This does not support greater than 64-bit values.
 479  */
 480 const char *
 481 crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
     /* [previous][next][first][last][top][bottom][index][help] */
 482 {
 483     char s[LLSTRSIZE] = { '\0', };
 484 
 485     if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
 486         return NULL;
 487     }
 488     return crm_xml_add(xml, name, s);
 489 }
 490 
 491 /*!
 492  * \brief Create XML attributes for seconds and microseconds
 493  *
 494  * This is like \c crm_xml_add() but taking a struct timeval.
 495  *
 496  * \param[in,out] xml        XML node to modify
 497  * \param[in]     name_sec   Name of XML attribute for seconds
 498  * \param[in]     name_usec  Name of XML attribute for microseconds (or NULL)
 499  * \param[in]     value      Time value to set
 500  *
 501  * \return New seconds value as string on success, \c NULL otherwise
 502  * \note This does nothing if xml, name_sec, or value is \c NULL.
 503  */
 504 const char *
 505 crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
     /* [previous][next][first][last][top][bottom][index][help] */
 506                     const struct timeval *value)
 507 {
 508     const char *added = NULL;
 509 
 510     if (xml && name_sec && value) {
 511         added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
 512         if (added && name_usec) {
 513             // Any error is ignored (we successfully added seconds)
 514             crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
 515         }
 516     }
 517     return added;
 518 }
 519 
 520 /*!
 521  * \brief Retrieve the value of an XML attribute
 522  *
 523  * \param[in] data   XML node to check
 524  * \param[in] name   Attribute name to check
 525  *
 526  * \return Value of specified attribute (may be \c NULL)
 527  */
 528 const char *
 529 crm_element_value(const xmlNode *data, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 530 {
 531     xmlAttr *attr = NULL;
 532 
 533     if (data == NULL) {
 534         crm_err("Couldn't find %s in NULL", name ? name : "<null>");
 535         CRM_LOG_ASSERT(data != NULL);
 536         return NULL;
 537 
 538     } else if (name == NULL) {
 539         crm_err("Couldn't find NULL in %s", crm_element_name(data));
 540         return NULL;
 541     }
 542 
 543     /* The first argument to xmlHasProp() has always been const,
 544      * but libxml2 <2.9.2 didn't declare that, so cast it
 545      */
 546     attr = xmlHasProp((xmlNode *) data, (pcmkXmlStr) name);
 547     if (!attr || !attr->children) {
 548         return NULL;
 549     }
 550     return (const char *) attr->children->content;
 551 }
 552 
 553 /*!
 554  * \brief Retrieve the integer value of an XML attribute
 555  *
 556  * This is like \c crm_element_value() but getting the value as an integer.
 557  *
 558  * \param[in] data   XML node to check
 559  * \param[in] name   Attribute name to check
 560  * \param[in] dest   Where to store element value
 561  *
 562  * \return 0 on success, -1 otherwise
 563  */
 564 int
 565 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 566 {
 567     const char *value = NULL;
 568 
 569     CRM_CHECK(dest != NULL, return -1);
 570     value = crm_element_value(data, name);
 571     if (value) {
 572         long long value_ll;
 573 
 574         if ((pcmk__scan_ll(value, &value_ll, 0LL) != pcmk_rc_ok)
 575             || (value_ll < INT_MIN) || (value_ll > INT_MAX)) {
 576             *dest = PCMK__PARSE_INT_DEFAULT;
 577         } else {
 578             *dest = (int) value_ll;
 579             return 0;
 580         }
 581     }
 582     return -1;
 583 }
 584 
 585 /*!
 586  * \brief Retrieve the long long integer value of an XML attribute
 587  *
 588  * This is like \c crm_element_value() but getting the value as a long long int.
 589  *
 590  * \param[in] data   XML node to check
 591  * \param[in] name   Attribute name to check
 592  * \param[in] dest   Where to store element value
 593  *
 594  * \return 0 on success, -1 otherwise
 595  */
 596 int
 597 crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 598 {
 599     const char *value = NULL;
 600 
 601     CRM_CHECK(dest != NULL, return -1);
 602     value = crm_element_value(data, name);
 603     if ((value != NULL)
 604         && (pcmk__scan_ll(value, dest, PCMK__PARSE_INT_DEFAULT) == pcmk_rc_ok)) {
 605         return 0;
 606     }
 607     return -1;
 608 }
 609 
 610 /*!
 611  * \brief Retrieve the millisecond value of an XML attribute
 612  *
 613  * This is like \c crm_element_value() but returning the value as a guint.
 614  *
 615  * \param[in]  data   XML node to check
 616  * \param[in]  name   Attribute name to check
 617  * \param[out] dest   Where to store attribute value
 618  *
 619  * \return \c pcmk_ok on success, -1 otherwise
 620  */
 621 int
 622 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 623 {
 624     const char *value = NULL;
 625     long long value_ll;
 626 
 627     CRM_CHECK(dest != NULL, return -1);
 628     *dest = 0;
 629     value = crm_element_value(data, name);
 630     if ((pcmk__scan_ll(value, &value_ll, 0LL) != pcmk_rc_ok)
 631         || (value_ll < 0) || (value_ll > G_MAXUINT)) {
 632         return -1;
 633     }
 634     *dest = (guint) value_ll;
 635     return pcmk_ok;
 636 }
 637 
 638 /*!
 639  * \brief Retrieve the seconds-since-epoch value of an XML attribute
 640  *
 641  * This is like \c crm_element_value() but returning the value as a time_t.
 642  *
 643  * \param[in]  xml    XML node to check
 644  * \param[in]  name   Attribute name to check
 645  * \param[out] dest   Where to store attribute value
 646  *
 647  * \return \c pcmk_ok on success, -1 otherwise
 648  */
 649 int
 650 crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 651 {
 652     long long value_ll = 0;
 653 
 654     if (crm_element_value_ll(xml, name, &value_ll) < 0) {
 655         return -1;
 656     }
 657 
 658     /* Unfortunately, we can't do any bounds checking, since time_t has neither
 659      * standardized bounds nor constants defined for them.
 660      */
 661     *dest = (time_t) value_ll;
 662     return pcmk_ok;
 663 }
 664 
 665 /*!
 666  * \brief Retrieve the value of XML second/microsecond attributes as time
 667  *
 668  * This is like \c crm_element_value() but returning value as a struct timeval.
 669  *
 670  * \param[in]  xml        XML to parse
 671  * \param[in]  name_sec   Name of XML attribute for seconds
 672  * \param[in]  name_usec  Name of XML attribute for microseconds
 673  * \param[out] dest       Where to store result
 674  *
 675  * \return \c pcmk_ok on success, -errno on error
 676  * \note Values default to 0 if XML or XML attribute does not exist
 677  */
 678 int
 679 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
     /* [previous][next][first][last][top][bottom][index][help] */
 680                           const char *name_usec, struct timeval *dest)
 681 {
 682     long long value_i = 0;
 683 
 684     CRM_CHECK(dest != NULL, return -EINVAL);
 685     dest->tv_sec = 0;
 686     dest->tv_usec = 0;
 687 
 688     if (xml == NULL) {
 689         return pcmk_ok;
 690     }
 691 
 692     /* Unfortunately, we can't do any bounds checking, since there are no
 693      * constants provided for the bounds of time_t and suseconds_t, and
 694      * calculating them isn't worth the effort. If there are XML values
 695      * beyond the native sizes, there will probably be worse problems anyway.
 696      */
 697 
 698     // Parse seconds
 699     errno = 0;
 700     if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
 701         return -errno;
 702     }
 703     dest->tv_sec = (time_t) value_i;
 704 
 705     // Parse microseconds
 706     if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
 707         return -errno;
 708     }
 709     dest->tv_usec = (suseconds_t) value_i;
 710 
 711     return pcmk_ok;
 712 }
 713 
 714 /*!
 715  * \brief Retrieve a copy of the value of an XML attribute
 716  *
 717  * This is like \c crm_element_value() but allocating new memory for the result.
 718  *
 719  * \param[in] data   XML node to check
 720  * \param[in] name   Attribute name to check
 721  *
 722  * \return Value of specified attribute (may be \c NULL)
 723  * \note The caller is responsible for freeing the result.
 724  */
 725 char *
 726 crm_element_value_copy(const xmlNode *data, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 727 {
 728     char *value_copy = NULL;
 729 
 730     pcmk__str_update(&value_copy, crm_element_value(data, name));
 731     return value_copy;
 732 }
 733 
 734 /*!
 735  * \brief Add hash table entry to XML as (possibly legacy) name/value
 736  *
 737  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 738  * and value, with an XML node passed as user data, and adds an XML attribute
 739  * with the specified name and value if it does not already exist. If the key
 740  * name starts with a digit, this will instead add a \<param name=NAME
 741  * value=VALUE/> child to the XML (for legacy compatibility with heartbeat).
 742  *
 743  * \param[in] key        Key of hash table entry
 744  * \param[in] value      Value of hash table entry
 745  * \param[in] user_data  XML node
 746  */
 747 void
 748 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 749 {
 750     const char *name = key;
 751     const char *s_value = value;
 752 
 753     xmlNode *xml_node = user_data;
 754 
 755     if (isdigit(name[0])) {
 756         xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
 757 
 758         crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
 759         crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
 760 
 761     } else if (crm_element_value(xml_node, name) == NULL) {
 762         crm_xml_add(xml_node, name, s_value);
 763         crm_trace("dumped: %s=%s", name, s_value);
 764 
 765     } else {
 766         crm_trace("duplicate: %s=%s", name, s_value);
 767     }
 768 }
 769 
 770 /*!
 771  * \brief Set XML attribute based on hash table entry
 772  *
 773  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 774  * and value, with an XML node passed as user data, and adds an XML attribute
 775  * with the specified name and value if it does not already exist.
 776  *
 777  * \param[in] key        Key of hash table entry
 778  * \param[in] value      Value of hash table entry
 779  * \param[in] user_data  XML node
 780  */
 781 void
 782 hash2field(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 783 {
 784     const char *name = key;
 785     const char *s_value = value;
 786 
 787     xmlNode *xml_node = user_data;
 788 
 789     if (crm_element_value(xml_node, name) == NULL) {
 790         crm_xml_add(xml_node, name, s_value);
 791 
 792     } else {
 793         crm_trace("duplicate: %s=%s", name, s_value);
 794     }
 795 }
 796 
 797 /*!
 798  * \brief Set XML attribute based on hash table entry, as meta-attribute name
 799  *
 800  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 801  * and value, with an XML node passed as user data, and adds an XML attribute
 802  * with the meta-attribute version of the specified name and value if it does
 803  * not already exist and if the name does not appear to be cluster-internal.
 804  *
 805  * \param[in] key        Key of hash table entry
 806  * \param[in] value      Value of hash table entry
 807  * \param[in] user_data  XML node
 808  */
 809 void
 810 hash2metafield(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 811 {
 812     char *crm_name = NULL;
 813 
 814     if (key == NULL || value == NULL) {
 815         return;
 816     }
 817 
 818     /* Filter out cluster-generated attributes that contain a '#' or ':'
 819      * (like fail-count and last-failure).
 820      */
 821     for (crm_name = key; *crm_name; ++crm_name) {
 822         if ((*crm_name == '#') || (*crm_name == ':')) {
 823             return;
 824         }
 825     }
 826 
 827     crm_name = crm_meta_name(key);
 828     hash2field(crm_name, value, user_data);
 829     free(crm_name);
 830 }
 831 
 832 // nvpair handling
 833 
 834 /*!
 835  * \brief Create an XML name/value pair
 836  *
 837  * \param[in] parent  If not \c NULL, make new XML node a child of this one
 838  * \param[in] id      If not \c NULL, use this as ID (otherwise auto-generate)
 839  * \param[in] name    Name to use
 840  * \param[in] value   Value to use
 841  *
 842  * \return New XML object on success, \c NULL otherwise
 843  */
 844 xmlNode *
 845 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 846                       const char *value)
 847 {
 848     xmlNode *nvp;
 849 
 850     /* id can be NULL so we auto-generate one, and name can be NULL if this
 851      * will be used to delete a name/value pair by ID, but both can't be NULL
 852      */
 853     CRM_CHECK(id || name, return NULL);
 854 
 855     nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
 856     CRM_CHECK(nvp, return NULL);
 857 
 858     if (id) {
 859         crm_xml_add(nvp, XML_ATTR_ID, id);
 860     } else {
 861         const char *parent_id = ID(parent);
 862 
 863         crm_xml_set_id(nvp, "%s-%s",
 864                        (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
 865     }
 866     crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
 867     crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
 868     return nvp;
 869 }
 870 
 871 /*!
 872  * \brief Add XML nvpair element based on hash table entry
 873  *
 874  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 875  * and value, with an XML node passed as the user data, and adds an \c nvpair
 876  * XML element with the specified name and value.
 877  *
 878  * \param[in] key        Key of hash table entry
 879  * \param[in] value      Value of hash table entry
 880  * \param[in] user_data  XML node
 881  */
 882 void
 883 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 884 {
 885     const char *name = key;
 886     const char *s_value = value;
 887     xmlNode *xml_node = user_data;
 888 
 889     crm_create_nvpair_xml(xml_node, name, name, s_value);
 890     crm_trace("dumped: name=%s value=%s", name, s_value);
 891 }
 892 
 893 /*!
 894  * \brief Retrieve XML attributes as a hash table
 895  *
 896  * Given an XML element, this will look for any \<attributes> element child,
 897  * creating a hash table of (newly allocated string) name/value pairs taken
 898  * first from the attributes element's NAME=VALUE XML attributes, and then
 899  * from any \<param name=NAME value=VALUE> children of attributes.
 900  *
 901  * \param[in]  XML node to parse
 902  *
 903  * \return Hash table with name/value pairs
 904  * \note It is the caller's responsibility to free the result using
 905  *       \c g_hash_table_destroy().
 906  */
 907 GHashTable *
 908 xml2list(xmlNode *parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 909 {
 910     xmlNode *child = NULL;
 911     xmlAttrPtr pIter = NULL;
 912     xmlNode *nvpair_list = NULL;
 913     GHashTable *nvpair_hash = pcmk__strkey_table(free, free);
 914 
 915     CRM_CHECK(parent != NULL, return nvpair_hash);
 916 
 917     nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
 918     if (nvpair_list == NULL) {
 919         crm_trace("No attributes in %s", crm_element_name(parent));
 920         crm_log_xml_trace(parent, "No attributes for resource op");
 921     }
 922 
 923     crm_log_xml_trace(nvpair_list, "Unpacking");
 924 
 925     for (pIter = pcmk__xe_first_attr(nvpair_list); pIter != NULL;
 926          pIter = pIter->next) {
 927 
 928         const char *p_name = (const char *)pIter->name;
 929         const char *p_value = pcmk__xml_attr_value(pIter);
 930 
 931         crm_trace("Added %s=%s", p_name, p_value);
 932 
 933         g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
 934     }
 935 
 936     for (child = pcmk__xml_first_child(nvpair_list); child != NULL;
 937          child = pcmk__xml_next(child)) {
 938 
 939         if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
 940             const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
 941             const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
 942 
 943             crm_trace("Added %s=%s", key, value);
 944             if (key != NULL && value != NULL) {
 945                 g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
 946             }
 947         }
 948     }
 949 
 950     return nvpair_hash;
 951 }
 952 
 953 void
 954 pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
     /* [previous][next][first][last][top][bottom][index][help] */
 955 {
 956     crm_xml_add(node, name, value ? XML_BOOLEAN_TRUE : XML_BOOLEAN_FALSE);
 957 }
 958 
 959 int
 960 pcmk__xe_get_bool_attr(xmlNodePtr node, const char *name, bool *value) {
     /* [previous][next][first][last][top][bottom][index][help] */
 961     const char *xml_value = NULL;
 962     int ret, rc;
 963 
 964     if (node == NULL) {
 965         return ENODATA;
 966     } else if (name == NULL || value == NULL) {
 967         return EINVAL;
 968     }
 969 
 970     xml_value = crm_element_value(node, name);
 971 
 972     if (xml_value == NULL) {
 973         return ENODATA;
 974     }
 975 
 976     rc = crm_str_to_boolean(xml_value, &ret);
 977     if (rc == 1) {
 978         *value = ret;
 979         return pcmk_rc_ok;
 980     } else {
 981         return pcmk_rc_unknown_format;
 982     }
 983 }
 984 
 985 bool
 986 pcmk__xe_attr_is_true(xmlNodePtr node, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 987 {
 988     bool value = false;
 989     int rc;
 990 
 991     rc = pcmk__xe_get_bool_attr(node, name, &value);
 992     return rc == pcmk_rc_ok && value == true;
 993 }
 994 
 995 // Deprecated functions kept only for backward API compatibility
 996 // LCOV_EXCL_START
 997 
 998 #include <crm/common/util_compat.h>
 999 
1000 int
1001 pcmk_scan_nvpair(const char *input, char **name, char **value)
     /* [previous][next][first][last][top][bottom][index][help] */
1002 {
1003     return pcmk__scan_nvpair(input, name, value);
1004 }
1005 
1006 char *
1007 pcmk_format_nvpair(const char *name, const char *value,
     /* [previous][next][first][last][top][bottom][index][help] */
1008                    const char *units)
1009 {
1010     return pcmk__format_nvpair(name, value, units);
1011 }
1012 
1013 char *
1014 pcmk_format_named_time(const char *name, time_t epoch_time)
     /* [previous][next][first][last][top][bottom][index][help] */
1015 {
1016     return pcmk__format_named_time(name, epoch_time);
1017 }
1018 
1019 // LCOV_EXCL_STOP
1020 // End deprecated API

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