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

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

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