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

   1 /*
   2  * Copyright 2004-2020 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__first_xml_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  * \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 char *
 282 pcmk_format_nvpair(const char *name, const char *value, const char *units) {
     /* [previous][next][first][last][top][bottom][index][help] */
 283     return crm_strdup_printf("%s=\"%s%s\"", name, value, units ? units : "");
 284 }
 285 
 286 /*!
 287  * \internal
 288  * \brief Format a name/time pair.
 289  *
 290  * See pcmk_format_nvpair() for more details.
 291  *
 292  * \note The caller is responsible for freeing the return value after use.
 293  *
 294  * \param[in]     name       The name for the time.
 295  * \param[in]     epoch_time The time to format.
 296  */
 297 char *
 298 pcmk_format_named_time(const char *name, time_t epoch_time) {
     /* [previous][next][first][last][top][bottom][index][help] */
 299     const char *now_str = pcmk__epoch2str(&epoch_time);
 300 
 301     return crm_strdup_printf("%s=\"%s\"", name, now_str ? now_str : "");
 302 }
 303 
 304 // XML attribute handling
 305 
 306 /*!
 307  * \brief Create an XML attribute with specified name and value
 308  *
 309  * \param[in,out] node   XML node to modify
 310  * \param[in]     name   Attribute name to set
 311  * \param[in]     value  Attribute value to set
 312  *
 313  * \return New value on success, \c NULL otherwise
 314  * \note This does nothing if node, name, or value are \c NULL or empty.
 315  */
 316 const char *
 317 crm_xml_add(xmlNode *node, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 318 {
 319     bool dirty = FALSE;
 320     xmlAttr *attr = NULL;
 321 
 322     CRM_CHECK(node != NULL, return NULL);
 323     CRM_CHECK(name != NULL, return NULL);
 324 
 325     if (value == NULL) {
 326         return NULL;
 327     }
 328 #if XML_PARANOIA_CHECKS
 329     {
 330         const char *old_value = NULL;
 331 
 332         old_value = crm_element_value(node, name);
 333 
 334         /* Could be re-setting the same value */
 335         CRM_CHECK(old_value != value,
 336                   crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
 337                   return value);
 338     }
 339 #endif
 340 
 341     if (pcmk__tracking_xml_changes(node, FALSE)) {
 342         const char *old = crm_element_value(node, name);
 343 
 344         if (old == NULL || value == NULL || strcmp(old, value) != 0) {
 345             dirty = TRUE;
 346         }
 347     }
 348 
 349     if (dirty && (pcmk__check_acl(node, name, xpf_acl_create) == FALSE)) {
 350         crm_trace("Cannot add %s=%s to %s", name, value, node->name);
 351         return NULL;
 352     }
 353 
 354     attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
 355     if (dirty) {
 356         pcmk__mark_xml_attr_dirty(attr);
 357     }
 358 
 359     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
 360     return (char *)attr->children->content;
 361 }
 362 
 363 /*!
 364  * \brief Replace an XML attribute with specified name and (possibly NULL) value
 365  *
 366  * \param[in,out] node   XML node to modify
 367  * \param[in]     name   Attribute name to set
 368  * \param[in]     value  Attribute value to set
 369  *
 370  * \return New value on success, \c NULL otherwise
 371  * \note This does nothing if node or name is \c NULL or empty.
 372  */
 373 const char *
 374 crm_xml_replace(xmlNode *node, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 375 {
 376     bool dirty = FALSE;
 377     xmlAttr *attr = NULL;
 378     const char *old_value = NULL;
 379 
 380     CRM_CHECK(node != NULL, return NULL);
 381     CRM_CHECK(name != NULL && name[0] != 0, return NULL);
 382 
 383     old_value = crm_element_value(node, name);
 384 
 385     /* Could be re-setting the same value */
 386     CRM_CHECK(old_value != value, return value);
 387 
 388     if (pcmk__check_acl(node, name, xpf_acl_write) == FALSE) {
 389         /* Create a fake object linked to doc->_private instead? */
 390         crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
 391         return NULL;
 392 
 393     } else if (old_value && !value) {
 394         xml_remove_prop(node, name);
 395         return NULL;
 396     }
 397 
 398     if (pcmk__tracking_xml_changes(node, FALSE)) {
 399         if (!old_value || !value || !strcmp(old_value, value)) {
 400             dirty = TRUE;
 401         }
 402     }
 403 
 404     attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
 405     if (dirty) {
 406         pcmk__mark_xml_attr_dirty(attr);
 407     }
 408     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
 409     return (char *) attr->children->content;
 410 }
 411 
 412 /*!
 413  * \brief Create an XML attribute with specified name and integer value
 414  *
 415  * This is like \c crm_xml_add() but taking an integer value.
 416  *
 417  * \param[in,out] node   XML node to modify
 418  * \param[in]     name   Attribute name to set
 419  * \param[in]     value  Attribute value to set
 420  *
 421  * \return New value as string on success, \c NULL otherwise
 422  * \note This does nothing if node or name are \c NULL or empty.
 423  */
 424 const char *
 425 crm_xml_add_int(xmlNode *node, const char *name, int value)
     /* [previous][next][first][last][top][bottom][index][help] */
 426 {
 427     char *number = crm_itoa(value);
 428     const char *added = crm_xml_add(node, name, number);
 429 
 430     free(number);
 431     return added;
 432 }
 433 
 434 /*!
 435  * \brief Create an XML attribute with specified name and unsigned value
 436  *
 437  * This is like \c crm_xml_add() but taking a guint value.
 438  *
 439  * \param[in,out] node   XML node to modify
 440  * \param[in]     name   Attribute name to set
 441  * \param[in]     ms     Attribute value to set
 442  *
 443  * \return New value as string on success, \c NULL otherwise
 444  * \note This does nothing if node or name are \c NULL or empty.
 445  */
 446 const char *
 447 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 448 {
 449     char *number = crm_strdup_printf("%u", ms);
 450     const char *added = crm_xml_add(node, name, number);
 451 
 452     free(number);
 453     return added;
 454 }
 455 
 456 // Maximum size of null-terminated string representation of 64-bit integer
 457 // -9223372036854775808
 458 #define LLSTRSIZE 21
 459 
 460 /*!
 461  * \brief Create an XML attribute with specified name and long long int value
 462  *
 463  * This is like \c crm_xml_add() but taking a long long int value. It is a
 464  * useful equivalent for defined types like time_t, etc.
 465  *
 466  * \param[in,out] xml    XML node to modify
 467  * \param[in]     name   Attribute name to set
 468  * \param[in]     value  Attribute value to set
 469  *
 470  * \return New value as string on success, \c NULL otherwise
 471  * \note This does nothing if xml or name are \c NULL or empty.
 472  *       This does not support greater than 64-bit values.
 473  */
 474 const char *
 475 crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
     /* [previous][next][first][last][top][bottom][index][help] */
 476 {
 477     char s[LLSTRSIZE] = { '\0', };
 478 
 479     if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
 480         return NULL;
 481     }
 482     return crm_xml_add(xml, name, s);
 483 }
 484 
 485 /*!
 486  * \brief Create XML attributes for seconds and microseconds
 487  *
 488  * This is like \c crm_xml_add() but taking a struct timeval.
 489  *
 490  * \param[in,out] xml        XML node to modify
 491  * \param[in]     name_sec   Name of XML attribute for seconds
 492  * \param[in]     name_usec  Name of XML attribute for microseconds (or NULL)
 493  * \param[in]     value      Time value to set
 494  *
 495  * \return New seconds value as string on success, \c NULL otherwise
 496  * \note This does nothing if xml, name_sec, or value is \c NULL.
 497  */
 498 const char *
 499 crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
     /* [previous][next][first][last][top][bottom][index][help] */
 500                     const struct timeval *value)
 501 {
 502     const char *added = NULL;
 503 
 504     if (xml && name_sec && value) {
 505         added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
 506         if (added && name_usec) {
 507             // Any error is ignored (we successfully added seconds)
 508             crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
 509         }
 510     }
 511     return added;
 512 }
 513 
 514 /*!
 515  * \brief Retrieve the value of an XML attribute
 516  *
 517  * \param[in] data   XML node to check
 518  * \param[in] name   Attribute name to check
 519  *
 520  * \return Value of specified attribute (may be \c NULL)
 521  */
 522 const char *
 523 crm_element_value(const xmlNode *data, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 524 {
 525     xmlAttr *attr = NULL;
 526 
 527     if (data == NULL) {
 528         crm_err("Couldn't find %s in NULL", name ? name : "<null>");
 529         CRM_LOG_ASSERT(data != NULL);
 530         return NULL;
 531 
 532     } else if (name == NULL) {
 533         crm_err("Couldn't find NULL in %s", crm_element_name(data));
 534         return NULL;
 535     }
 536 
 537     /* The first argument to xmlHasProp() has always been const,
 538      * but libxml2 <2.9.2 didn't declare that, so cast it
 539      */
 540     attr = xmlHasProp((xmlNode *) data, (pcmkXmlStr) name);
 541     if (!attr || !attr->children) {
 542         return NULL;
 543     }
 544     return (const char *) attr->children->content;
 545 }
 546 
 547 /*!
 548  * \brief Retrieve the integer value of an XML attribute
 549  *
 550  * This is like \c crm_element_value() but getting the value as an integer.
 551  *
 552  * \param[in] data   XML node to check
 553  * \param[in] name   Attribute name to check
 554  * \param[in] dest   Where to store element value
 555  *
 556  * \return 0 on success, -1 otherwise
 557  */
 558 int
 559 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 560 {
 561     const char *value = NULL;
 562 
 563     CRM_CHECK(dest != NULL, return -1);
 564     value = crm_element_value(data, name);
 565     if (value) {
 566         errno = 0;
 567         *dest = crm_parse_int(value, NULL);
 568         if (errno == 0) {
 569             return 0;
 570         }
 571     }
 572     return -1;
 573 }
 574 
 575 /*!
 576  * \brief Retrieve the long long integer value of an XML attribute
 577  *
 578  * This is like \c crm_element_value() but getting the value as a long long int.
 579  *
 580  * \param[in] data   XML node to check
 581  * \param[in] name   Attribute name to check
 582  * \param[in] dest   Where to store element value
 583  *
 584  * \return 0 on success, -1 otherwise
 585  */
 586 int
 587 crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 588 {
 589     const char *value = NULL;
 590 
 591     CRM_CHECK(dest != NULL, return -1);
 592     value = crm_element_value(data, name);
 593     if (value) {
 594         errno = 0;
 595         *dest = crm_parse_ll(value, NULL);
 596         if (errno == 0) {
 597             return 0;
 598         }
 599     }
 600     return -1;
 601 }
 602 
 603 /*!
 604  * \brief Retrieve the millisecond value of an XML attribute
 605  *
 606  * This is like \c crm_element_value() but returning the value as a guint.
 607  *
 608  * \param[in]  data   XML node to check
 609  * \param[in]  name   Attribute name to check
 610  * \param[out] dest   Where to store attribute value
 611  *
 612  * \return \c pcmk_ok on success, -1 otherwise
 613  */
 614 int
 615 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 616 {
 617     const char *value = NULL;
 618     long long value_ll;
 619 
 620     CRM_CHECK(dest != NULL, return -1);
 621     *dest = 0;
 622 
 623     value = crm_element_value(data, name);
 624     if (value == NULL) {
 625         return pcmk_ok;
 626     }
 627 
 628     errno = 0;
 629     value_ll = crm_parse_ll(value, NULL);
 630     if ((errno != 0) || (value_ll < 0) || (value_ll > G_MAXUINT)) {
 631         return -1;
 632     }
 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     const char *value = crm_element_value(data, name);
 730 
 731     if (value != NULL) {
 732         value_copy = strdup(value);
 733     }
 734     return value_copy;
 735 }
 736 
 737 /*!
 738  * \brief Add hash table entry to XML as (possibly legacy) name/value
 739  *
 740  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 741  * and value, with an XML node passed as user data, and adds an XML attribute
 742  * with the specified name and value if it does not already exist. If the key
 743  * name starts with a digit, this will instead add a \<param name=NAME
 744  * value=VALUE/> child to the XML (for legacy compatibility with heartbeat).
 745  *
 746  * \param[in] key        Key of hash table entry
 747  * \param[in] value      Value of hash table entry
 748  * \param[in] user_data  XML node
 749  */
 750 void
 751 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 752 {
 753     const char *name = key;
 754     const char *s_value = value;
 755 
 756     xmlNode *xml_node = user_data;
 757 
 758     if (isdigit(name[0])) {
 759         xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
 760 
 761         crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
 762         crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
 763 
 764     } else if (crm_element_value(xml_node, name) == NULL) {
 765         crm_xml_add(xml_node, name, s_value);
 766         crm_trace("dumped: %s=%s", name, s_value);
 767 
 768     } else {
 769         crm_trace("duplicate: %s=%s", name, s_value);
 770     }
 771 }
 772 
 773 /*!
 774  * \brief Set XML attribute based on hash table entry
 775  *
 776  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 777  * and value, with an XML node passed as user data, and adds an XML attribute
 778  * with the specified name and value if it does not already exist.
 779  *
 780  * \param[in] key        Key of hash table entry
 781  * \param[in] value      Value of hash table entry
 782  * \param[in] user_data  XML node
 783  */
 784 void
 785 hash2field(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 786 {
 787     const char *name = key;
 788     const char *s_value = value;
 789 
 790     xmlNode *xml_node = user_data;
 791 
 792     if (crm_element_value(xml_node, name) == NULL) {
 793         crm_xml_add(xml_node, name, s_value);
 794 
 795     } else {
 796         crm_trace("duplicate: %s=%s", name, s_value);
 797     }
 798 }
 799 
 800 /*!
 801  * \brief Set XML attribute based on hash table entry, as meta-attribute name
 802  *
 803  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 804  * and value, with an XML node passed as user data, and adds an XML attribute
 805  * with the meta-attribute version of the specified name and value if it does
 806  * not already exist and if the name does not appear to be cluster-internal.
 807  *
 808  * \param[in] key        Key of hash table entry
 809  * \param[in] value      Value of hash table entry
 810  * \param[in] user_data  XML node
 811  */
 812 void
 813 hash2metafield(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 814 {
 815     char *crm_name = NULL;
 816 
 817     if (key == NULL || value == NULL) {
 818         return;
 819     }
 820 
 821     /* Filter out cluster-generated attributes that contain a '#' or ':'
 822      * (like fail-count and last-failure).
 823      */
 824     for (crm_name = key; *crm_name; ++crm_name) {
 825         if ((*crm_name == '#') || (*crm_name == ':')) {
 826             return;
 827         }
 828     }
 829 
 830     crm_name = crm_meta_name(key);
 831     hash2field(crm_name, value, user_data);
 832     free(crm_name);
 833 }
 834 
 835 // nvpair handling
 836 
 837 /*!
 838  * \brief Create an XML name/value pair
 839  *
 840  * \param[in] parent  If not \c NULL, make new XML node a child of this one
 841  * \param[in] id      If not \c NULL, use this as ID (otherwise auto-generate)
 842  * \param[in] name    Name to use
 843  * \param[in] value   Value to use
 844  *
 845  * \return New XML object on success, \c NULL otherwise
 846  */
 847 xmlNode *
 848 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 849                       const char *value)
 850 {
 851     xmlNode *nvp;
 852 
 853     /* id can be NULL so we auto-generate one, and name can be NULL if this
 854      * will be used to delete a name/value pair by ID, but both can't be NULL
 855      */
 856     CRM_CHECK(id || name, return NULL);
 857 
 858     nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
 859     CRM_CHECK(nvp, return NULL);
 860 
 861     if (id) {
 862         crm_xml_add(nvp, XML_ATTR_ID, id);
 863     } else {
 864         const char *parent_id = ID(parent);
 865 
 866         crm_xml_set_id(nvp, "%s-%s",
 867                        (parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
 868     }
 869     crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
 870     crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
 871     return nvp;
 872 }
 873 
 874 /*!
 875  * \brief Add XML nvpair element based on hash table entry
 876  *
 877  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 878  * and value, with an XML node passed as the user data, and adds an \c nvpair
 879  * XML element with the specified name and value.
 880  *
 881  * \param[in] key        Key of hash table entry
 882  * \param[in] value      Value of hash table entry
 883  * \param[in] user_data  XML node
 884  */
 885 void
 886 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 887 {
 888     const char *name = key;
 889     const char *s_value = value;
 890     xmlNode *xml_node = user_data;
 891 
 892     crm_create_nvpair_xml(xml_node, name, name, s_value);
 893     crm_trace("dumped: name=%s value=%s", name, s_value);
 894 }
 895 
 896 /*!
 897  * \brief Retrieve XML attributes as a hash table
 898  *
 899  * Given an XML element, this will look for any \<attributes> element child,
 900  * creating a hash table of (newly allocated string) name/value pairs taken
 901  * first from the attributes element's NAME=VALUE XML attributes, and then
 902  * from any \<param name=NAME value=VALUE> children of attributes.
 903  *
 904  * \param[in]  XML node to parse
 905  *
 906  * \return Hash table with name/value pairs
 907  * \note It is the caller's responsibility to free the result using
 908  *       \c g_hash_table_destroy().
 909  */
 910 GHashTable *
 911 xml2list(xmlNode *parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 912 {
 913     xmlNode *child = NULL;
 914     xmlAttrPtr pIter = NULL;
 915     xmlNode *nvpair_list = NULL;
 916     GHashTable *nvpair_hash = crm_str_table_new();
 917 
 918     CRM_CHECK(parent != NULL, return nvpair_hash);
 919 
 920     nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
 921     if (nvpair_list == NULL) {
 922         crm_trace("No attributes in %s", crm_element_name(parent));
 923         crm_log_xml_trace(parent, "No attributes for resource op");
 924     }
 925 
 926     crm_log_xml_trace(nvpair_list, "Unpacking");
 927 
 928     for (pIter = pcmk__first_xml_attr(nvpair_list); pIter != NULL;
 929          pIter = pIter->next) {
 930 
 931         const char *p_name = (const char *)pIter->name;
 932         const char *p_value = pcmk__xml_attr_value(pIter);
 933 
 934         crm_trace("Added %s=%s", p_name, p_value);
 935 
 936         g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
 937     }
 938 
 939     for (child = pcmk__xml_first_child(nvpair_list); child != NULL;
 940          child = pcmk__xml_next(child)) {
 941 
 942         if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
 943             const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
 944             const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
 945 
 946             crm_trace("Added %s=%s", key, value);
 947             if (key != NULL && value != NULL) {
 948                 g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
 949             }
 950         }
 951     }
 952 
 953     return nvpair_hash;
 954 }

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