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_scan_nvpair
  33. pcmk_format_nvpair
  34. pcmk_format_named_time

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

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