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__scan_nvpair
  6. pcmk__format_nvpair
  7. hash2smartfield
  8. hash2field
  9. hash2metafield
  10. crm_create_nvpair_xml
  11. xml2list
  12. unpack_nvpair
  13. pcmk__unpack_nvpair_block
  14. pcmk_unpack_nvpair_blocks
  15. crm_meta_name
  16. crm_meta_value
  17. pcmk__cmp_nvpair_blocks
  18. pcmk__compare_nvpair
  19. pcmk_sort_nvpairs
  20. pcmk_xml_attrs2nvpairs
  21. pcmk__nvpair_add_xml_attr
  22. pcmk_nvpairs2xml_attrs
  23. hash2nvpair

   1 /*
   2  * Copyright 2004-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <stdint.h>         // UINT32_MAX
  14 #include <inttypes.h>       // PRIu32
  15 #include <sys/types.h>
  16 #include <string.h>
  17 #include <ctype.h>
  18 #include <glib.h>           // gchar, gint, etc.
  19 #include <libxml/tree.h>
  20 
  21 #include <crm/crm.h>
  22 #include <crm/common/xml.h>
  23 #include <crm/common/xml_internal.h>
  24 #include "crmcommon_private.h"
  25 
  26 /*
  27  * This file isolates handling of various kinds of name/value pairs:
  28  *
  29  * - pcmk_nvpair_t data type
  30  * - name=value strings
  31  * - XML nvpair elements (<nvpair id=ID name=NAME value=VALUE>)
  32  * - Instance attributes and meta-attributes (for resources and actions)
  33  */
  34 
  35 // pcmk_nvpair_t handling
  36 
  37 /*!
  38  * \internal
  39  * \brief Allocate a new name/value pair
  40  *
  41  * \param[in] name   New name (required)
  42  * \param[in] value  New value
  43  *
  44  * \return Newly allocated name/value pair
  45  * \note The caller is responsible for freeing the result with
  46  *       \c pcmk__free_nvpair().
  47  */
  48 static pcmk_nvpair_t *
  49 pcmk__new_nvpair(const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
  50 {
  51     pcmk_nvpair_t *nvpair = NULL;
  52 
  53     pcmk__assert(name);
  54 
  55     nvpair = pcmk__assert_alloc(1, sizeof(pcmk_nvpair_t));
  56 
  57     nvpair->name = pcmk__str_copy(name);
  58     nvpair->value = pcmk__str_copy(value);
  59     return nvpair;
  60 }
  61 
  62 /*!
  63  * \internal
  64  * \brief Free a name/value pair
  65  *
  66  * \param[in,out] nvpair  Name/value pair to free
  67  */
  68 static void
  69 pcmk__free_nvpair(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  70 {
  71     if (data) {
  72         pcmk_nvpair_t *nvpair = data;
  73 
  74         free(nvpair->name);
  75         free(nvpair->value);
  76         free(nvpair);
  77     }
  78 }
  79 
  80 /*!
  81  * \brief Prepend a name/value pair to a list
  82  *
  83  * \param[in,out] nvpairs  List to modify
  84  * \param[in]     name     New entry's name
  85  * \param[in]     value    New entry's value
  86  *
  87  * \return New head of list
  88  * \note The caller is responsible for freeing the list with
  89  *       \c pcmk_free_nvpairs().
  90  */
  91 GSList *
  92 pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
  93 {
  94     return g_slist_prepend(nvpairs, pcmk__new_nvpair(name, value));
  95 }
  96 
  97 /*!
  98  * \brief Free a list of name/value pairs
  99  *
 100  * \param[in,out] list  List to free
 101  */
 102 void
 103 pcmk_free_nvpairs(GSList *nvpairs)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105     g_slist_free_full(nvpairs, pcmk__free_nvpair);
 106 }
 107 
 108 
 109 // name=value string handling
 110 
 111 /*!
 112  * \internal
 113  * \brief Extract the name and value from a string formatted as "name=value"
 114  *
 115  * \param[in]  input  Input string, likely from the command line
 116  * \param[out] name   Everything before the first \c '=' in the input string
 117  * \param[out] value  Everything after the first \c '=' in the input string,
 118  *                    minus trailing newlines
 119  *
 120  * \return Standard Pacemaker return code
 121  *
 122  * \note On success, the caller is responsible for freeing \p *name and
 123  *       \p *value using \c g_free(). On failure, nothing is allocated.
 124  */
 125 int
 126 pcmk__scan_nvpair(const gchar *input, gchar **name, gchar **value)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128     gchar **nvpair = NULL;
 129     int rc = pcmk_rc_ok;
 130 
 131     pcmk__assert(input != NULL);
 132     pcmk__assert((name != NULL) && (*name == NULL));
 133     pcmk__assert((value != NULL) && (*value == NULL));
 134 
 135     nvpair = g_strsplit(input, "=", 2);
 136 
 137     /* Check whether nvpair is well-formed (short-circuits if input was split
 138      * into fewer than 2 tokens)
 139      */
 140     if (pcmk__str_empty(nvpair[0]) || pcmk__str_empty(nvpair[1])) {
 141         rc = pcmk_rc_bad_nvpair;
 142         goto done;
 143     }
 144 
 145     *name = nvpair[0];
 146     *value = nvpair[1];
 147     pcmk__trim((char *) *value);
 148 
 149     // name and value took ownership
 150     nvpair[0] = NULL;
 151     nvpair[1] = NULL;
 152 
 153 done:
 154     g_strfreev(nvpair);
 155     return rc;
 156 }
 157 
 158 /*!
 159  * \internal
 160  * \brief Format a name/value pair.
 161  *
 162  * Units can optionally be provided for the value.  Note that unlike most
 163  * formatting functions, this one returns the formatted string.  It is
 164  * assumed that the most common use of this function will be to build up
 165  * a string to be output as part of other functions.
 166  *
 167  * \note The caller is responsible for freeing the return value after use.
 168  *
 169  * \param[in]     name  The name of the nvpair.
 170  * \param[in]     value The value of the nvpair.
 171  * \param[in]     units Optional units for the value, or NULL.
 172  *
 173  * \return Newly allocated string with name/value pair
 174  */
 175 char *
 176 pcmk__format_nvpair(const char *name, const char *value, const char *units)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178     return crm_strdup_printf("%s=\"%s%s\"", name, value, units ? units : "");
 179 }
 180 
 181 /*!
 182  * \brief Safely add hash table entry to XML as attribute or name-value pair
 183  *
 184  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 185  * and value, with an XML node passed as user data, and adds an XML attribute
 186  * with the specified name and value if it does not already exist. If the key
 187  * name starts with a digit, then it's not a valid XML attribute name. In that
 188  * case, this will instead add a <tt><param name=NAME value=VALUE/></tt> child
 189  * to the XML.
 190  *
 191  * \param[in]     key        Key of hash table entry
 192  * \param[in]     value      Value of hash table entry
 193  * \param[in,out] user_data  XML node
 194  */
 195 void
 196 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 197 {
 198     /* @TODO Generate PCMK__XE_PARAM nodes for all keys that aren't valid XML
 199      * attribute names (not just those that start with digits), or possibly for
 200      * all keys to simplify parsing.
 201      *
 202      * Consider either deprecating as public API or exposing PCMK__XE_PARAM.
 203      * PCMK__XE_PARAM is currently private because it doesn't appear in any
 204      * output that Pacemaker generates.
 205      */
 206     const char *name = key;
 207     const char *s_value = value;
 208 
 209     xmlNode *xml_node = user_data;
 210 
 211     if (isdigit(name[0])) {
 212         xmlNode *tmp = pcmk__xe_create(xml_node, PCMK__XE_PARAM);
 213 
 214         crm_xml_add(tmp, PCMK_XA_NAME, name);
 215         crm_xml_add(tmp, PCMK_XA_VALUE, s_value);
 216 
 217     } else if (crm_element_value(xml_node, name) == NULL) {
 218         crm_xml_add(xml_node, name, s_value);
 219         crm_trace("dumped: %s=%s", name, s_value);
 220 
 221     } else {
 222         crm_trace("duplicate: %s=%s", name, s_value);
 223     }
 224 }
 225 
 226 /*!
 227  * \brief Set XML attribute based on hash table entry
 228  *
 229  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 230  * and value, with an XML node passed as user data, and adds an XML attribute
 231  * with the specified name and value if it does not already exist.
 232  *
 233  * \param[in]     key        Key of hash table entry
 234  * \param[in]     value      Value of hash table entry
 235  * \param[in,out] user_data  XML node
 236  */
 237 void
 238 hash2field(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 239 {
 240     const char *name = key;
 241     const char *s_value = value;
 242 
 243     xmlNode *xml_node = user_data;
 244 
 245     if (crm_element_value(xml_node, name) == NULL) {
 246         crm_xml_add(xml_node, name, s_value);
 247 
 248     } else {
 249         crm_trace("duplicate: %s=%s", name, s_value);
 250     }
 251 }
 252 
 253 /*!
 254  * \brief Set XML attribute based on hash table entry, as meta-attribute name
 255  *
 256  * Suitable for \c g_hash_table_foreach(), this function takes a hash table key
 257  * and value, with an XML node passed as user data, and adds an XML attribute
 258  * with the meta-attribute version of the specified name and value if it does
 259  * not already exist and if the name does not appear to be cluster-internal.
 260  *
 261  * \param[in]     key        Key of hash table entry
 262  * \param[in]     value      Value of hash table entry
 263  * \param[in,out] user_data  XML node
 264  */
 265 void
 266 hash2metafield(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 267 {
 268     char *crm_name = NULL;
 269 
 270     if (key == NULL || value == NULL) {
 271         return;
 272     }
 273 
 274     /* Filter out cluster-generated attributes that contain a '#' or ':'
 275      * (like fail-count and last-failure).
 276      */
 277     for (crm_name = key; *crm_name; ++crm_name) {
 278         if ((*crm_name == '#') || (*crm_name == ':')) {
 279             return;
 280         }
 281     }
 282 
 283     crm_name = crm_meta_name(key);
 284     hash2field(crm_name, value, user_data);
 285     free(crm_name);
 286 }
 287 
 288 // nvpair handling
 289 
 290 /*!
 291  * \brief Create an XML name/value pair
 292  *
 293  * \param[in,out] parent  If not \c NULL, make new XML node a child of this one
 294  * \param[in]     id      Set this as XML ID (or NULL to auto-generate)
 295  * \param[in]     name    Name to use
 296  * \param[in]     value   Value to use
 297  *
 298  * \return New XML object on success, \c NULL otherwise
 299  */
 300 xmlNode *
 301 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 302                       const char *value)
 303 {
 304     xmlNode *nvp;
 305 
 306     /* id can be NULL so we auto-generate one, and name can be NULL if this
 307      * will be used to delete a name/value pair by ID, but both can't be NULL
 308      */
 309     CRM_CHECK(id || name, return NULL);
 310 
 311     nvp = pcmk__xe_create(parent, PCMK_XE_NVPAIR);
 312 
 313     if (id) {
 314         crm_xml_add(nvp, PCMK_XA_ID, id);
 315     } else {
 316         pcmk__xe_set_id(nvp, "%s-%s",
 317                         pcmk__s(pcmk__xe_id(parent), PCMK_XE_NVPAIR), name);
 318     }
 319     crm_xml_add(nvp, PCMK_XA_NAME, name);
 320     crm_xml_add(nvp, PCMK_XA_VALUE, value);
 321     return nvp;
 322 }
 323 
 324 /*!
 325  * \brief Retrieve XML attributes as a hash table
 326  *
 327  * Given an XML element, this will look for any \<attributes> element child,
 328  * creating a hash table of (newly allocated string) name/value pairs taken
 329  * first from the attributes element's NAME=VALUE XML attributes, and then
 330  * from any \<param name=NAME value=VALUE> children of attributes.
 331  *
 332  * \param[in]  XML node to parse
 333  *
 334  * \return Hash table with name/value pairs
 335  * \note It is the caller's responsibility to free the result using
 336  *       \c g_hash_table_destroy().
 337  */
 338 GHashTable *
 339 xml2list(const xmlNode *parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341     xmlNode *child = NULL;
 342     xmlAttrPtr pIter = NULL;
 343     xmlNode *nvpair_list = NULL;
 344     GHashTable *nvpair_hash = pcmk__strkey_table(free, free);
 345 
 346     CRM_CHECK(parent != NULL, return nvpair_hash);
 347 
 348     nvpair_list = pcmk__xe_first_child(parent, PCMK__XE_ATTRIBUTES, NULL, NULL);
 349     if (nvpair_list == NULL) {
 350         crm_trace("No attributes in %s", parent->name);
 351         crm_log_xml_trace(parent, "No attributes for resource op");
 352     }
 353 
 354     crm_log_xml_trace(nvpair_list, "Unpacking");
 355 
 356     for (pIter = pcmk__xe_first_attr(nvpair_list); pIter != NULL;
 357          pIter = pIter->next) {
 358 
 359         const char *p_name = (const char *)pIter->name;
 360         const char *p_value = pcmk__xml_attr_value(pIter);
 361 
 362         crm_trace("Added %s=%s", p_name, p_value);
 363 
 364         pcmk__insert_dup(nvpair_hash, p_name, p_value);
 365     }
 366 
 367     for (child = pcmk__xe_first_child(nvpair_list, PCMK__XE_PARAM, NULL, NULL);
 368          child != NULL; child = pcmk__xe_next(child, PCMK__XE_PARAM)) {
 369 
 370         const char *key = crm_element_value(child, PCMK_XA_NAME);
 371         const char *value = crm_element_value(child, PCMK_XA_VALUE);
 372 
 373         crm_trace("Added %s=%s", key, value);
 374         if (key != NULL && value != NULL) {
 375             pcmk__insert_dup(nvpair_hash, key, value);
 376         }
 377     }
 378 
 379     return nvpair_hash;
 380 }
 381 
 382 /*!
 383  * \internal
 384  * \brief Unpack a single nvpair XML element into a hash table
 385  *
 386  * \param[in]     nvpair    XML nvpair element to unpack
 387  * \param[in,out] userdata  Unpack data
 388  *
 389  * \return pcmk_rc_ok (to always proceed to next nvpair)
 390  */
 391 static int
 392 unpack_nvpair(xmlNode *nvpair, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 393 {
 394     pcmk__nvpair_unpack_t *unpack_data = userdata;
 395 
 396     const char *name = NULL;
 397     const char *value = NULL;
 398     const char *old_value = NULL;
 399     const xmlNode *ref_nvpair = pcmk__xe_resolve_idref(nvpair, NULL);
 400 
 401     if (ref_nvpair == NULL) {
 402         /* Not possible with schema validation enabled (error already
 403          * logged)
 404          */
 405         return pcmk_rc_ok;
 406     }
 407 
 408     name = crm_element_value(ref_nvpair, PCMK_XA_NAME);
 409     value = crm_element_value(ref_nvpair, PCMK_XA_VALUE);
 410     if ((name == NULL) || (value == NULL)) {
 411         return pcmk_rc_ok; // Not possible with schema validation enabled
 412     }
 413 
 414     old_value = g_hash_table_lookup(unpack_data->values, name);
 415 
 416     if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
 417         // @COMPAT Deprecated since 2.1.8
 418         pcmk__config_warn("Support for setting meta-attributes (such as "
 419                           "%s) to the explicit value '#default' is "
 420                           "deprecated and will be removed in a future "
 421                           "release", name);
 422         if (old_value != NULL) {
 423             g_hash_table_remove(unpack_data->values, name);
 424         }
 425 
 426     } else if ((old_value == NULL) || unpack_data->overwrite) {
 427         crm_trace("Setting %s=\"%s\" (was %s)",
 428                   name, value, pcmk__s(old_value, "unset"));
 429         pcmk__insert_dup(unpack_data->values, name, value);
 430     }
 431     return pcmk_rc_ok;
 432 }
 433 
 434 /*!
 435  * \internal
 436  * \brief Unpack an XML block of nvpair elements into a hash table,
 437  *        evaluated for any rule
 438  *
 439  * \param[in]     data       XML block to unpack
 440  * \param[in,out] user_data  Unpack data
 441  *
 442  * \note This is suitable for use as a GList iterator function
 443  */
 444 void
 445 pcmk__unpack_nvpair_block(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 446 {
 447     xmlNode *pair = data;
 448     pcmk__nvpair_unpack_t *unpack_data = user_data;
 449 
 450     xmlNode *rule_xml = NULL;
 451 
 452     pcmk__assert((pair != NULL) && (unpack_data != NULL)
 453                  && (unpack_data->values != NULL));
 454 
 455     rule_xml = pcmk__xe_first_child(pair, PCMK_XE_RULE, NULL, NULL);
 456     if ((rule_xml != NULL)
 457         && (pcmk_evaluate_rule(rule_xml, &(unpack_data->rule_input),
 458                                unpack_data->next_change) != pcmk_rc_ok)) {
 459         return;
 460     }
 461 
 462     crm_trace("Adding name/value pairs from %s %s overwrite",
 463               pcmk__xe_id(pair), (unpack_data->overwrite? "with" : "without"));
 464     if (pcmk__xe_is(pair->children, PCMK__XE_ATTRIBUTES)) {
 465         pair = pair->children;
 466     }
 467     pcmk__xe_foreach_child(pair, PCMK_XE_NVPAIR, unpack_nvpair, unpack_data);
 468 }
 469 
 470 /*!
 471  * \brief Unpack nvpair blocks contained by an XML element into a hash table,
 472  *        evaluated for any rules
 473  *
 474  * \param[in]  xml           XML element containing blocks of nvpair elements
 475  * \param[in]  element_name  If not NULL, only unpack blocks of this element
 476  * \param[in]  first_id      If not NULL, process block with this ID first
 477  * \param[in]  rule_input    Values used to evaluate rule criteria
 478  * \param[out] values        Where to store extracted name/value pairs
 479  * \param[out] next_change   If not NULL, set to when evaluation will next
 480  *                           change, if sooner than its current value
 481  */
 482 void
 483 pcmk_unpack_nvpair_blocks(const xmlNode *xml, const char *element_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 484                           const char *first_id,
 485                           const pcmk_rule_input_t *rule_input,
 486                           GHashTable *values, crm_time_t *next_change)
 487 {
 488     GList *blocks = pcmk__xe_dereference_children(xml, element_name);
 489 
 490     if (blocks != NULL) {
 491         pcmk__nvpair_unpack_t data = {
 492             .values = values,
 493             .first_id = first_id,
 494             .rule_input = {
 495                 .now = NULL,
 496             },
 497             .overwrite = false,
 498             .next_change = next_change,
 499         };
 500 
 501         if (rule_input != NULL) {
 502             data.rule_input = *rule_input;
 503         }
 504         blocks = g_list_sort_with_data(blocks, pcmk__cmp_nvpair_blocks, &data);
 505         g_list_foreach(blocks, pcmk__unpack_nvpair_block, &data);
 506         g_list_free(blocks);
 507     }
 508 }
 509 
 510 
 511 // Meta-attribute handling
 512 
 513 /*!
 514  * \brief Get the environment variable equivalent of a meta-attribute name
 515  *
 516  * \param[in] attr_name  Name of meta-attribute
 517  *
 518  * \return Newly allocated string for \p attr_name with "CRM_meta_" prefix and
 519  *         underbars instead of dashes
 520  * \note This asserts on an invalid argument or memory allocation error, so
 521  *       callers can assume the result is non-NULL. The caller is responsible
 522  *       for freeing the result using free().
 523  */
 524 char *
 525 crm_meta_name(const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 526 {
 527     char *env_name = NULL;
 528 
 529     pcmk__assert(!pcmk__str_empty(attr_name));
 530 
 531     env_name = crm_strdup_printf(CRM_META "_%s", attr_name);
 532     for (char *c = env_name; *c != '\0'; ++c) {
 533         if (*c == '-') {
 534             *c = '_';
 535         }
 536     }
 537     return env_name;
 538 }
 539 
 540 /*!
 541  * \brief Get the value of a meta-attribute
 542  *
 543  * Get the value of a meta-attribute from a hash table whose keys are
 544  * meta-attribute environment variable names (as crm_meta_name() would
 545  * create, like pcmk__graph_action_t:params, not pcmk_resource_t:meta).
 546  *
 547  * \param[in] meta       Hash table of meta-attributes
 548  * \param[in] attr_name  Name of meta-attribute to get
 549  *
 550  * \return Value of given meta-attribute
 551  */
 552 const char *
 553 crm_meta_value(GHashTable *meta, const char *attr_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 554 {
 555     if ((meta != NULL) && (attr_name != NULL)) {
 556         char *key = crm_meta_name(attr_name);
 557         const char *value = g_hash_table_lookup(meta, key);
 558 
 559         free(key);
 560         return value;
 561     }
 562     return NULL;
 563 }
 564 
 565 /*!
 566  * \internal
 567  * \brief Compare processing order of two XML blocks of name/value pairs
 568  *
 569  * \param[in] a          First XML block to compare
 570  * \param[in] b          Second XML block to compare
 571  * \param[in] user_data  pcmk__nvpair_unpack_t with first_id (whether a
 572  *                       particular XML ID should have priority) and overwrite
 573  *                       (whether later-processed blocks will overwrite values
 574  *                       from earlier ones) set as desired
 575  *
 576  * \return Standard comparison return code (a negative value if \p a should sort
 577  *         first, a positive value if \p b should sort first, and 0 if they
 578  *         should sort equally)
 579  * \note This is suitable for use as a GList sorting function.
 580  */
 581 gint
 582 pcmk__cmp_nvpair_blocks(gconstpointer a, gconstpointer b, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 583 {
 584     const xmlNode *pair_a = a;
 585     const xmlNode *pair_b = b;
 586     const pcmk__nvpair_unpack_t *unpack_data = user_data;
 587 
 588     int score_a = 0;
 589     int score_b = 0;
 590     int rc = pcmk_rc_ok;
 591 
 592     /* If we're overwriting values, we want to process blocks from
 593      * lowest priority to highest, so higher-priority values overwrite
 594      * lower-priority ones. If we're not overwriting values, we want to process
 595      * from highest priority to lowest.
 596      */
 597     const gint a_is_higher = ((unpack_data != NULL)
 598                               && unpack_data->overwrite)? 1 : -1;
 599     const gint b_is_higher = -a_is_higher;
 600 
 601     /* NULL values have lowest priority, regardless of the other's score
 602      * (it won't be possible in practice anyway, this is just a failsafe)
 603      */
 604     if (a == NULL) {
 605         return (b == NULL)? 0 : b_is_higher;
 606 
 607     } else if (b == NULL) {
 608         return a_is_higher;
 609     }
 610 
 611     /* A particular XML ID can be specified as having highest priority
 612      * regardless of score (schema validation, if enabled, prevents two blocks
 613      * from having the same ID, so we can ignore handling that case
 614      * specifically)
 615      */
 616     if ((unpack_data != NULL) && (unpack_data->first_id != NULL)) {
 617         if (pcmk__str_eq(pcmk__xe_id(pair_a), unpack_data->first_id,
 618                          pcmk__str_none)) {
 619             return a_is_higher;
 620 
 621         } else if (pcmk__str_eq(pcmk__xe_id(pair_b), unpack_data->first_id,
 622                                 pcmk__str_none)) {
 623             return b_is_higher;
 624         }
 625     }
 626 
 627     // Otherwise, check the scores
 628 
 629     rc = pcmk__xe_get_score(pair_a, PCMK_XA_SCORE, &score_a, 0);
 630     if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
 631         pcmk__config_warn("Using 0 as %s score because '%s' "
 632                           "is not a valid score: %s",
 633                           pcmk__xe_id(pair_a),
 634                           crm_element_value(pair_a, PCMK_XA_SCORE),
 635                           pcmk_rc_str(rc));
 636     }
 637 
 638     rc = pcmk__xe_get_score(pair_b, PCMK_XA_SCORE, &score_b, 0);
 639     if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
 640         pcmk__config_warn("Using 0 as %s score because '%s' "
 641                           "is not a valid score: %s",
 642                           pcmk__xe_id(pair_b),
 643                           crm_element_value(pair_b, PCMK_XA_SCORE),
 644                           pcmk_rc_str(rc));
 645     }
 646 
 647     if (score_a < score_b) {
 648         return b_is_higher;
 649 
 650     } else if (score_a > score_b) {
 651         return a_is_higher;
 652     }
 653     return 0;
 654 }
 655 
 656 // Deprecated functions kept only for backward API compatibility
 657 // LCOV_EXCL_START
 658 
 659 #include <crm/common/nvpair_compat.h>
 660 
 661 static gint
 662 pcmk__compare_nvpair(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 663 {
 664     int rc = 0;
 665     const pcmk_nvpair_t *pair_a = a;
 666     const pcmk_nvpair_t *pair_b = b;
 667 
 668     pcmk__assert((pair_a != NULL) && (pair_a->name != NULL)
 669                  && (pair_b != NULL) && (pair_b->name != NULL));
 670 
 671     rc = strcmp(pair_a->name, pair_b->name);
 672     if (rc < 0) {
 673         return -1;
 674     } else if (rc > 0) {
 675         return 1;
 676     }
 677     return 0;
 678 }
 679 
 680 GSList *
 681 pcmk_sort_nvpairs(GSList *list)
     /* [previous][next][first][last][top][bottom][index][help] */
 682 {
 683     return g_slist_sort(list, pcmk__compare_nvpair);
 684 }
 685 
 686 GSList *
 687 pcmk_xml_attrs2nvpairs(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 688 {
 689     GSList *result = NULL;
 690 
 691     for (xmlAttrPtr iter = pcmk__xe_first_attr(xml); iter != NULL;
 692          iter = iter->next) {
 693 
 694         result = pcmk_prepend_nvpair(result,
 695                                      (const char *) iter->name,
 696                                      (const char *) pcmk__xml_attr_value(iter));
 697     }
 698     return result;
 699 }
 700 
 701 static void
 702 pcmk__nvpair_add_xml_attr(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 703 {
 704     pcmk_nvpair_t *pair = data;
 705     xmlNode *parent = user_data;
 706 
 707     crm_xml_add(parent, pair->name, pair->value);
 708 }
 709 
 710 void
 711 pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 712 {
 713     g_slist_foreach(list, pcmk__nvpair_add_xml_attr, xml);
 714 }
 715 
 716 void
 717 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 718 {
 719     const char *name = key;
 720     const char *s_value = value;
 721     xmlNode *xml_node = user_data;
 722 
 723     crm_create_nvpair_xml(xml_node, name, name, s_value);
 724     crm_trace("dumped: name=%s value=%s", name, s_value);
 725 }
 726 
 727 // LCOV_EXCL_STOP
 728 // End deprecated API

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