root/lib/common/xml_element.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__xe_first_child
  2. pcmk__xe_next
  3. pcmk__xe_get_score
  4. pcmk__xe_set_score
  5. pcmk__xe_copy_attrs
  6. compare_xml_attr
  7. pcmk__xe_sort_attrs
  8. pcmk__xe_remove_attr
  9. pcmk__xe_remove_attr_cb
  10. pcmk__xe_remove_matching_attrs
  11. pcmk__xe_create
  12. G_GNUC_PRINTF
  13. G_GNUC_PRINTF
  14. pcmk__xe_add_last_written
  15. update_xe
  16. delete_xe_if_matching
  17. pcmk__xe_delete_match
  18. replace_node
  19. replace_xe_if_matching
  20. pcmk__xe_replace_match
  21. update_xe_if_matching
  22. pcmk__xe_update_match
  23. pcmk__xe_set_propv
  24. pcmk__xe_set_props
  25. pcmk__xe_foreach_child
  26. crm_xml_add
  27. crm_xml_add_int
  28. crm_xml_add_ms
  29. crm_xml_add_ll
  30. crm_xml_add_timeval
  31. crm_element_value
  32. crm_element_value_int
  33. pcmk__xe_get_flags
  34. crm_element_value_ll
  35. crm_element_value_ms
  36. crm_element_value_epoch
  37. crm_element_value_timeval
  38. pcmk__xe_get_datetime
  39. crm_element_value_copy
  40. pcmk__xe_set_bool_attr
  41. pcmk__xe_get_bool_attr
  42. pcmk__xe_attr_is_true
  43. expand_idref
  44. crm_xml_set_id
  45. sorted_xml

   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 <stdarg.h>                     // va_start(), etc.
  13 #include <stdint.h>                     // uint32_t
  14 #include <stdio.h>                      // NULL, etc.
  15 #include <stdlib.h>                     // free(), etc.
  16 #include <string.h>                     // strchr(), etc.
  17 #include <sys/types.h>                  // time_t, etc.
  18 
  19 #include <libxml/tree.h>                // xmlNode, etc.
  20 #include <libxml/valid.h>               // xmlValidateNameValue()
  21 
  22 #include <crm/crm.h>
  23 #include <crm/common/nvpair.h>          // crm_xml_add(), etc.
  24 #include <crm/common/results.h>         // pcmk_rc_ok, etc.
  25 #include <crm/common/xml.h>
  26 #include "crmcommon_private.h"
  27 
  28 /*!
  29  * \internal
  30  * \brief Find first XML child element matching given criteria
  31  *
  32  * \param[in] parent     XML element to search (can be \c NULL)
  33  * \param[in] node_name  If not \c NULL, only match children of this type
  34  * \param[in] attr_n     If not \c NULL, only match children with an attribute
  35  *                       of this name.
  36  * \param[in] attr_v     If \p attr_n and this are not NULL, only match children
  37  *                       with an attribute named \p attr_n and this value
  38  *
  39  * \return Matching XML child element, or \c NULL if none found
  40  */
  41 xmlNode *
  42 pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
     /* [previous][next][first][last][top][bottom][index][help] */
  43                      const char *attr_n, const char *attr_v)
  44 {
  45     xmlNode *child = NULL;
  46     const char *parent_name = "<null>";
  47 
  48     CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL);
  49 
  50     if (parent != NULL) {
  51         child = parent->children;
  52         while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
  53             child = child->next;
  54         }
  55 
  56         parent_name = (const char *) parent->name;
  57     }
  58 
  59     for (; child != NULL; child = pcmk__xe_next(child, NULL)) {
  60         const char *value = NULL;
  61 
  62         if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) {
  63             // Node name mismatch
  64             continue;
  65         }
  66         if (attr_n == NULL) {
  67             // No attribute match needed
  68             return child;
  69         }
  70 
  71         value = crm_element_value(child, attr_n);
  72 
  73         if ((attr_v == NULL) && (value != NULL)) {
  74             // attr_v == NULL: Attribute attr_n must be set (to any value)
  75             return child;
  76         }
  77         if ((attr_v != NULL) && (pcmk__str_eq(value, attr_v, pcmk__str_none))) {
  78             // attr_v != NULL: Attribute attr_n must be set to value attr_v
  79             return child;
  80         }
  81     }
  82 
  83     if (node_name == NULL) {
  84         node_name = "(any)";    // For logging
  85     }
  86     if (attr_n != NULL) {
  87         crm_trace("XML child node <%s %s=%s> not found in %s",
  88                   node_name, attr_n, attr_v, parent_name);
  89     } else {
  90         crm_trace("XML child node <%s> not found in %s",
  91                   node_name, parent_name);
  92     }
  93     return NULL;
  94 }
  95 
  96 /*!
  97  * \internal
  98  * \brief Return next sibling element of an XML element
  99  *
 100  * \param[in] xml           XML element to check
 101  * \param[in] element_name  If not NULL, get next sibling with this element name
 102  *
 103  * \return Next desired sibling of \p xml (or NULL if none)
 104  */
 105 xmlNode *
 106 pcmk__xe_next(const xmlNode *xml, const char *element_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 107 {
 108     for (xmlNode *next = (xml == NULL)? NULL : xml->next;
 109          next != NULL; next = next->next) {
 110         if ((next->type == XML_ELEMENT_NODE)
 111             && ((element_name == NULL) || pcmk__xe_is(next, element_name))) {
 112             return next;
 113         }
 114     }
 115     return NULL;
 116 }
 117 
 118 /*!
 119  * \internal
 120  * \brief Parse an integer score from an XML attribute
 121  *
 122  * \param[in]  xml            XML element with attribute to parse
 123  * \param[in]  name           Name of attribute to parse
 124  * \param[out] score          Where to store parsed score (can be NULL to
 125  *                            just validate)
 126  * \param[in]  default_score  What to return if the attribute value is not
 127  *                            present or invalid
 128  *
 129  * \return Standard Pacemaker return code
 130  */
 131 int
 132 pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score,
     /* [previous][next][first][last][top][bottom][index][help] */
 133                    int default_score)
 134 {
 135     const char *value = NULL;
 136 
 137     CRM_CHECK((xml != NULL) && (name != NULL), return EINVAL);
 138     value = crm_element_value(xml, name);
 139     return pcmk_parse_score(value, score, default_score);
 140 }
 141 
 142 /*!
 143  * \internal
 144  * \brief Set an XML attribute, expanding \c ++ and \c += where appropriate
 145  *
 146  * If \p target already has an attribute named \p name set to an integer value
 147  * and \p value is an addition assignment expression on \p name, then expand
 148  * \p value to an integer and set attribute \p name to the expanded value in
 149  * \p target.
 150  *
 151  * Otherwise, set attribute \p name on \p target using the literal \p value.
 152  *
 153  * The original attribute value in \p target and the number in an assignment
 154  * expression in \p value are parsed and added as scores (that is, their values
 155  * are capped at \c INFINITY and \c -INFINITY). For more details, refer to
 156  * \c pcmk_parse_score().
 157  *
 158  * For example, suppose \p target has an attribute named \c "X" with value
 159  * \c "5", and that \p name is \c "X".
 160  * * If \p value is \c "X++", the new value of \c "X" in \p target is \c "6".
 161  * * If \p value is \c "X+=3", the new value of \c "X" in \p target is \c "8".
 162  * * If \p value is \c "val", the new value of \c "X" in \p target is \c "val".
 163  * * If \p value is \c "Y++", the new value of \c "X" in \p target is \c "Y++".
 164  *
 165  * \param[in,out] target  XML node whose attribute to set
 166  * \param[in]     name    Name of the attribute to set
 167  * \param[in]     value   New value of attribute to set (if NULL, initial value
 168  *                        will be left unchanged)
 169  *
 170  * \return Standard Pacemaker return code (specifically, \c EINVAL on invalid
 171  *         argument, or \c pcmk_rc_ok otherwise)
 172  */
 173 int
 174 pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 175 {
 176     const char *old_value = NULL;
 177 
 178     CRM_CHECK((target != NULL) && (name != NULL), return EINVAL);
 179 
 180     if (value == NULL) {
 181         // @TODO Maybe instead delete the attribute or set it to 0
 182         return pcmk_rc_ok;
 183     }
 184 
 185     old_value = crm_element_value(target, name);
 186 
 187     // If no previous value, skip to default case and set the value unexpanded.
 188     if (old_value != NULL) {
 189         const char *n = name;
 190         const char *v = value;
 191 
 192         // Stop at first character that differs between name and value
 193         for (; (*n == *v) && (*n != '\0'); n++, v++);
 194 
 195         // If value begins with name followed by a "++" or "+="
 196         if ((*n == '\0')
 197             && (*v++ == '+')
 198             && ((*v == '+') || (*v == '='))) {
 199 
 200             int add = 1;
 201             int old_value_i = 0;
 202             int rc = pcmk_rc_ok;
 203 
 204             // If we're expanding ourselves, no previous value was set; use 0
 205             if (old_value != value) {
 206                 rc = pcmk_parse_score(old_value, &old_value_i, 0);
 207                 if (rc != pcmk_rc_ok) {
 208                     // @TODO This is inconsistent with old_value==NULL
 209                     crm_trace("Using 0 before incrementing %s because '%s' "
 210                               "is not a score", name, old_value);
 211                 }
 212             }
 213 
 214             /* value="X++": new value of X is old_value + 1
 215              * value="X+=Y": new value of X is old_value + Y (for some number Y)
 216              */
 217             if (*v != '+') {
 218                 rc = pcmk_parse_score(++v, &add, 0);
 219                 if (rc != pcmk_rc_ok) {
 220                     // @TODO We should probably skip expansion instead
 221                     crm_trace("Not incrementing %s because '%s' does not have "
 222                               "a valid increment", name, value);
 223                 }
 224             }
 225 
 226             crm_xml_add_int(target, name, pcmk__add_scores(old_value_i, add));
 227             return pcmk_rc_ok;
 228         }
 229     }
 230 
 231     // Default case: set the attribute unexpanded (with value treated literally)
 232     if (old_value != value) {
 233         crm_xml_add(target, name, value);
 234     }
 235     return pcmk_rc_ok;
 236 }
 237 
 238 /*!
 239  * \internal
 240  * \brief Copy XML attributes from a source element to a target element
 241  *
 242  * This is similar to \c xmlCopyPropList() except that attributes are marked
 243  * as dirty for change tracking purposes.
 244  *
 245  * \param[in,out] target  XML element to receive copied attributes from \p src
 246  * \param[in]     src     XML element whose attributes to copy to \p target
 247  * \param[in]     flags   Group of <tt>enum pcmk__xa_flags</tt>
 248  *
 249  * \return Standard Pacemaker return code
 250  */
 251 int
 252 pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254     CRM_CHECK((src != NULL) && (target != NULL), return EINVAL);
 255 
 256     for (xmlAttr *attr = pcmk__xe_first_attr(src); attr != NULL;
 257          attr = attr->next) {
 258 
 259         const char *name = (const char *) attr->name;
 260         const char *value = pcmk__xml_attr_value(attr);
 261 
 262         if (pcmk_is_set(flags, pcmk__xaf_no_overwrite)
 263             && (crm_element_value(target, name) != NULL)) {
 264             continue;
 265         }
 266 
 267         if (pcmk_is_set(flags, pcmk__xaf_score_update)) {
 268             pcmk__xe_set_score(target, name, value);
 269         } else {
 270             crm_xml_add(target, name, value);
 271         }
 272     }
 273 
 274     return pcmk_rc_ok;
 275 }
 276 
 277 /*!
 278  * \internal
 279  * \brief Compare two XML attributes by name
 280  *
 281  * \param[in] a  First XML attribute to compare
 282  * \param[in] b  Second XML attribute to compare
 283  *
 284  * \retval  negative \c a->name is \c NULL or comes before \c b->name
 285  *                   lexicographically
 286  * \retval  0        \c a->name and \c b->name are equal
 287  * \retval  positive \c b->name is \c NULL or comes before \c a->name
 288  *                   lexicographically
 289  */
 290 static gint
 291 compare_xml_attr(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 292 {
 293     const xmlAttr *attr_a = a;
 294     const xmlAttr *attr_b = b;
 295 
 296     return pcmk__strcmp((const char *) attr_a->name,
 297                         (const char *) attr_b->name, pcmk__str_none);
 298 }
 299 
 300 /*!
 301  * \internal
 302  * \brief Sort an XML element's attributes by name
 303  *
 304  * This does not consider ACLs and does not mark the attributes as deleted or
 305  * dirty. Upon return, all attributes still exist and are set to the same values
 306  * as before the call. The only thing that may change is the order of the
 307  * attribute list.
 308  *
 309  * \param[in,out] xml  XML element whose attributes to sort
 310  */
 311 void
 312 pcmk__xe_sort_attrs(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 313 {
 314     GSList *attr_list = NULL;
 315 
 316     for (xmlAttr *iter = pcmk__xe_first_attr(xml); iter != NULL;
 317          iter = iter->next) {
 318         attr_list = g_slist_prepend(attr_list, iter);
 319     }
 320     attr_list = g_slist_sort(attr_list, compare_xml_attr);
 321 
 322     for (GSList *iter = attr_list; iter != NULL; iter = iter->next) {
 323         xmlNode *attr = iter->data;
 324 
 325         xmlUnlinkNode(attr);
 326         xmlAddChild(xml, attr);
 327     }
 328     g_slist_free(attr_list);
 329 }
 330 
 331 /*!
 332  * \internal
 333  * \brief Remove a named attribute from an XML element
 334  *
 335  * \param[in,out] element  XML element to remove an attribute from
 336  * \param[in]     name     Name of attribute to remove
 337  */
 338 void
 339 pcmk__xe_remove_attr(xmlNode *element, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341     if (name != NULL) {
 342         pcmk__xa_remove(xmlHasProp(element, (pcmkXmlStr) name), false);
 343     }
 344 }
 345 
 346 /*!
 347  * \internal
 348  * \brief Remove a named attribute from an XML element
 349  *
 350  * This is a wrapper for \c pcmk__xe_remove_attr() for use with
 351  * \c pcmk__xml_tree_foreach().
 352  *
 353  * \param[in,out] xml        XML element to remove an attribute from
 354  * \param[in]     user_data  Name of attribute to remove
 355  *
 356  * \return \c true (to continue traversing the tree)
 357  *
 358  * \note This is compatible with \c pcmk__xml_tree_foreach().
 359  */
 360 bool
 361 pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 362 {
 363     const char *name = user_data;
 364 
 365     pcmk__xe_remove_attr(xml, name);
 366     return true;
 367 }
 368 
 369 /*!
 370  * \internal
 371  * \brief Remove an XML element's attributes that match some criteria
 372  *
 373  * \param[in,out] element    XML element to modify
 374  * \param[in]     match      If not NULL, only remove attributes for which
 375  *                           this function returns true
 376  * \param[in,out] user_data  Data to pass to \p match
 377  */
 378 void
 379 pcmk__xe_remove_matching_attrs(xmlNode *element,
     /* [previous][next][first][last][top][bottom][index][help] */
 380                                bool (*match)(xmlAttrPtr, void *),
 381                                void *user_data)
 382 {
 383     xmlAttrPtr next = NULL;
 384 
 385     for (xmlAttrPtr a = pcmk__xe_first_attr(element); a != NULL; a = next) {
 386         next = a->next; // Grab now because attribute might get removed
 387         if ((match == NULL) || match(a, user_data)) {
 388             if (pcmk__xa_remove(a, false) != pcmk_rc_ok) {
 389                 return;
 390             }
 391         }
 392     }
 393 }
 394 
 395 /*!
 396  * \internal
 397  * \brief Create a new XML element under a given parent
 398  *
 399  * \param[in,out] parent  XML element that will be the new element's parent
 400  *                        (\c NULL to create a new XML document with the new
 401  *                        node as root)
 402  * \param[in]     name    Name of new element
 403  *
 404  * \return Newly created XML element (guaranteed not to be \c NULL)
 405  */
 406 xmlNode *
 407 pcmk__xe_create(xmlNode *parent, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 408 {
 409     xmlNode *node = NULL;
 410 
 411     pcmk__assert(!pcmk__str_empty(name));
 412 
 413     if (parent == NULL) {
 414         xmlDoc *doc = pcmk__xml_new_doc();
 415 
 416         node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
 417         pcmk__mem_assert(node);
 418 
 419         xmlDocSetRootElement(doc, node);
 420 
 421     } else {
 422         node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
 423         pcmk__mem_assert(node);
 424     }
 425 
 426     pcmk__xml_new_private_data(node);
 427     return node;
 428 }
 429 
 430 /*!
 431  * \internal
 432  * \brief Set a formatted string as an XML node's content
 433  *
 434  * \param[in,out] node    Node whose content to set
 435  * \param[in]     format  <tt>printf(3)</tt>-style format string
 436  * \param[in]     ...     Arguments for \p format
 437  *
 438  * \note This function escapes special characters. \c xmlNodeSetContent() does
 439  *       not.
 440  */
 441 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 442 void
 443 pcmk__xe_set_content(xmlNode *node, const char *format, ...)
 444 {
 445     if (node != NULL) {
 446         const char *content = NULL;
 447         char *buf = NULL;
 448 
 449         /* xmlNodeSetContent() frees node->children and replaces it with new
 450          * text. If this function is called for a node that already has a non-
 451          * text child, it's a bug.
 452          */
 453         CRM_CHECK((node->children == NULL)
 454                   || (node->children->type == XML_TEXT_NODE),
 455                   return);
 456 
 457         if (strchr(format, '%') == NULL) {
 458             // Nothing to format
 459             content = format;
 460 
 461         } else {
 462             va_list ap;
 463 
 464             va_start(ap, format);
 465 
 466             if (pcmk__str_eq(format, "%s", pcmk__str_none)) {
 467                 // No need to make a copy
 468                 content = va_arg(ap, const char *);
 469 
 470             } else {
 471                 pcmk__assert(vasprintf(&buf, format, ap) >= 0);
 472                 content = buf;
 473             }
 474             va_end(ap);
 475         }
 476 
 477         xmlNodeSetContent(node, (pcmkXmlStr) content);
 478         free(buf);
 479     }
 480 }
 481 
 482 /*!
 483  * \internal
 484  * \brief Set a formatted string as an XML element's ID
 485  *
 486  * If the formatted string would not be a valid ID, it's first sanitized by
 487  * \c pcmk__xml_sanitize_id().
 488  *
 489  * \param[in,out] node    Node whose ID to set
 490  * \param[in]     format  <tt>printf(3)</tt>-style format string
 491  * \param[in]     ...     Arguments for \p format
 492  */
 493 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 494 void
 495 pcmk__xe_set_id(xmlNode *node, const char *format, ...)
 496 {
 497     char *id = NULL;
 498     va_list ap;
 499 
 500     pcmk__assert(!pcmk__str_empty(format));
 501 
 502     if (node == NULL) {
 503         return;
 504     }
 505 
 506     va_start(ap, format);
 507     pcmk__assert(vasprintf(&id, format, ap) >= 0);
 508     va_end(ap);
 509 
 510     if (!xmlValidateNameValue((pcmkXmlStr) id)) {
 511         pcmk__xml_sanitize_id(id);
 512     }
 513     crm_xml_add(node, PCMK_XA_ID, id);
 514     free(id);
 515 }
 516 
 517 /*!
 518  * \internal
 519  * \brief Add a "last written" attribute to an XML element, set to current time
 520  *
 521  * \param[in,out] xe  XML element to add attribute to
 522  *
 523  * \return Value that was set, or NULL on error
 524  */
 525 const char *
 526 pcmk__xe_add_last_written(xmlNode *xe)
     /* [previous][next][first][last][top][bottom][index][help] */
 527 {
 528     char *now_s = pcmk__epoch2str(NULL, 0);
 529     const char *result = NULL;
 530 
 531     result = crm_xml_add(xe, PCMK_XA_CIB_LAST_WRITTEN,
 532                          pcmk__s(now_s, "Could not determine current time"));
 533     free(now_s);
 534     return result;
 535 }
 536 
 537 /*!
 538  * \internal
 539  * \brief Merge one XML tree into another
 540  *
 541  * Here, "merge" means:
 542  * 1. Copy attribute values from \p update to the target, overwriting in case of
 543  *    conflict.
 544  * 2. Descend through \p update and the target in parallel. At each level, for
 545  *    each child of \p update, look for a matching child of the target.
 546  *    a. For each child, if a match is found, go to step 1, recursively merging
 547  *       the child of \p update into the child of the target.
 548  *    b. Otherwise, copy the child of \p update as a child of the target.
 549  *
 550  * A match is defined as the first child of the same type within the target,
 551  * with:
 552  * * the \c PCMK_XA_ID attribute matching, if set in \p update; otherwise,
 553  * * the \c PCMK_XA_ID_REF attribute matching, if set in \p update
 554  *
 555  * This function does not delete any elements or attributes from the target. It
 556  * may add elements or overwrite attributes, as described above.
 557  *
 558  * \param[in,out] parent   If \p target is NULL and this is not, add or update
 559  *                         child of this XML node that matches \p update
 560  * \param[in,out] target   If not NULL, update this XML
 561  * \param[in]     update   Make the desired XML match this (must not be \c NULL)
 562  * \param[in]     flags    Group of <tt>enum pcmk__xa_flags</tt>
 563  *
 564  * \note At least one of \p parent and \p target must be non-<tt>NULL</tt>.
 565  * \note This function is recursive. For the top-level call, \p parent is
 566  *       \c NULL and \p target is not \c NULL. For recursive calls, \p target is
 567  *       \c NULL and \p parent is not \c NULL.
 568  */
 569 static void
 570 update_xe(xmlNode *parent, xmlNode *target, xmlNode *update, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 571 {
 572     // @TODO Try to refactor further, possibly using pcmk__xml_tree_foreach()
 573     const char *update_name = NULL;
 574     const char *update_id_attr = NULL;
 575     const char *update_id_val = NULL;
 576     char *trace_s = NULL;
 577 
 578     crm_log_xml_trace(update, "update");
 579     crm_log_xml_trace(target, "target");
 580 
 581     CRM_CHECK(update != NULL, goto done);
 582 
 583     if (update->type == XML_COMMENT_NODE) {
 584         pcmk__xc_update(parent, target, update);
 585         goto done;
 586     }
 587 
 588     update_name = (const char *) update->name;
 589 
 590     CRM_CHECK(update_name != NULL, goto done);
 591     CRM_CHECK((target != NULL) || (parent != NULL), goto done);
 592 
 593     update_id_val = pcmk__xe_id(update);
 594     if (update_id_val != NULL) {
 595         update_id_attr = PCMK_XA_ID;
 596 
 597     } else {
 598         update_id_val = crm_element_value(update, PCMK_XA_ID_REF);
 599         if (update_id_val != NULL) {
 600             update_id_attr = PCMK_XA_ID_REF;
 601         }
 602     }
 603 
 604     pcmk__if_tracing(
 605         {
 606             if (update_id_attr != NULL) {
 607                 trace_s = crm_strdup_printf("<%s %s=%s/>",
 608                                             update_name, update_id_attr,
 609                                             update_id_val);
 610             } else {
 611                 trace_s = crm_strdup_printf("<%s/>", update_name);
 612             }
 613         },
 614         {}
 615     );
 616 
 617     if (target == NULL) {
 618         // Recursive call
 619         target = pcmk__xe_first_child(parent, update_name, update_id_attr,
 620                                       update_id_val);
 621     }
 622 
 623     if (target == NULL) {
 624         // Recursive call with no existing matching child
 625         target = pcmk__xe_create(parent, update_name);
 626         crm_trace("Added %s", pcmk__s(trace_s, update_name));
 627 
 628     } else {
 629         // Either recursive call with match, or top-level call
 630         crm_trace("Found node %s to update", pcmk__s(trace_s, update_name));
 631     }
 632 
 633     CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return);
 634 
 635     pcmk__xe_copy_attrs(target, update, flags);
 636 
 637     for (xmlNode *child = pcmk__xml_first_child(update); child != NULL;
 638          child = pcmk__xml_next(child)) {
 639 
 640         crm_trace("Updating child of %s", pcmk__s(trace_s, update_name));
 641         update_xe(target, NULL, child, flags);
 642     }
 643 
 644     crm_trace("Finished with %s", pcmk__s(trace_s, update_name));
 645 
 646 done:
 647     free(trace_s);
 648 }
 649 
 650 /*!
 651  * \internal
 652  * \brief Delete an XML subtree if it matches a search element
 653  *
 654  * A match is defined as follows:
 655  * * \p xml and \p user_data are both element nodes of the same type.
 656  * * If \p user_data has attributes set, \p xml has those attributes set to the
 657  *   same values. (\p xml may have additional attributes set to arbitrary
 658  *   values.)
 659  *
 660  * \param[in,out] xml        XML subtree to delete upon match
 661  * \param[in]     user_data  Search element
 662  *
 663  * \return \c true to continue traversing the tree, or \c false to stop (because
 664  *         \p xml was deleted)
 665  *
 666  * \note This is compatible with \c pcmk__xml_tree_foreach().
 667  */
 668 static bool
 669 delete_xe_if_matching(xmlNode *xml, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 670 {
 671     xmlNode *search = user_data;
 672 
 673     if (!pcmk__xe_is(search, (const char *) xml->name)) {
 674         // No match: either not both elements, or different element types
 675         return true;
 676     }
 677 
 678     for (const xmlAttr *attr = pcmk__xe_first_attr(search); attr != NULL;
 679          attr = attr->next) {
 680 
 681         const char *search_val = pcmk__xml_attr_value(attr);
 682         const char *xml_val = crm_element_value(xml, (const char *) attr->name);
 683 
 684         if (!pcmk__str_eq(search_val, xml_val, pcmk__str_casei)) {
 685             // No match: an attr in xml doesn't match the attr in search
 686             return true;
 687         }
 688     }
 689 
 690     crm_log_xml_trace(xml, "delete-match");
 691     crm_log_xml_trace(search, "delete-search");
 692     pcmk__xml_free(xml);
 693 
 694     // Found a match and deleted it; stop traversing tree
 695     return false;
 696 }
 697 
 698 /*!
 699  * \internal
 700  * \brief Search an XML tree depth-first and delete the first matching element
 701  *
 702  * This function does not attempt to match the tree root (\p xml).
 703  *
 704  * A match with a node \c node is defined as follows:
 705  * * \c node and \p search are both element nodes of the same type.
 706  * * If \p search has attributes set, \c node has those attributes set to the
 707  *   same values. (\c node may have additional attributes set to arbitrary
 708  *   values.)
 709  *
 710  * \param[in,out] xml     XML subtree to search
 711  * \param[in]     search  Element to match against
 712  *
 713  * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on
 714  *         successful deletion and an error code otherwise)
 715  */
 716 int
 717 pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
     /* [previous][next][first][last][top][bottom][index][help] */
 718 {
 719     // See @COMPAT comment in pcmk__xe_replace_match()
 720     CRM_CHECK((xml != NULL) && (search != NULL), return EINVAL);
 721 
 722     for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
 723          xml = pcmk__xe_next(xml, NULL)) {
 724 
 725         if (!pcmk__xml_tree_foreach(xml, delete_xe_if_matching, search)) {
 726             // Found and deleted an element
 727             return pcmk_rc_ok;
 728         }
 729     }
 730 
 731     // No match found in this subtree
 732     return ENXIO;
 733 }
 734 
 735 /*!
 736  * \internal
 737  * \brief Replace one XML node with a copy of another XML node
 738  *
 739  * This function handles change tracking and applies ACLs.
 740  *
 741  * \param[in,out] old  XML node to replace
 742  * \param[in]     new  XML node to copy as replacement for \p old
 743  *
 744  * \note This frees \p old.
 745  */
 746 static void
 747 replace_node(xmlNode *old, xmlNode *new)
     /* [previous][next][first][last][top][bottom][index][help] */
 748 {
 749     // Pass old for its doc; it won't remain the parent of new
 750     new = pcmk__xml_copy(old, new);
 751     old = xmlReplaceNode(old, new);
 752 
 753     // old == NULL means memory allocation error
 754     pcmk__assert(old != NULL);
 755 
 756     // May be unnecessary but avoids slight changes to some test outputs
 757     pcmk__xml_tree_foreach(new, pcmk__xml_reset_node_flags, NULL);
 758 
 759     if (xml_tracking_changes(new)) {
 760         // Replaced sections may have included relevant ACLs
 761         pcmk__apply_acl(new);
 762     }
 763     xml_calculate_changes(old, new);
 764     pcmk__xml_free_node(old);
 765 }
 766 
 767 /*!
 768  * \internal
 769  * \brief Replace one XML subtree with a copy of another if the two match
 770  *
 771  * A match is defined as follows:
 772  * * \p xml and \p user_data are both element nodes of the same type.
 773  * * If \p user_data has the \c PCMK_XA_ID attribute set, then \p xml has
 774  *   \c PCMK_XA_ID set to the same value.
 775  *
 776  * \param[in,out] xml        XML subtree to replace with \p user_data upon match
 777  * \param[in]     user_data  XML to replace \p xml with a copy of upon match
 778  *
 779  * \return \c true to continue traversing the tree, or \c false to stop (because
 780  *         \p xml was replaced by \p user_data)
 781  *
 782  * \note This is compatible with \c pcmk__xml_tree_foreach().
 783  */
 784 static bool
 785 replace_xe_if_matching(xmlNode *xml, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 786 {
 787     xmlNode *replace = user_data;
 788     const char *xml_id = NULL;
 789     const char *replace_id = NULL;
 790 
 791     xml_id = pcmk__xe_id(xml);
 792     replace_id = pcmk__xe_id(replace);
 793 
 794     if (!pcmk__xe_is(replace, (const char *) xml->name)) {
 795         // No match: either not both elements, or different element types
 796         return true;
 797     }
 798 
 799     if ((replace_id != NULL)
 800         && !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) {
 801 
 802         // No match: ID was provided in replace and doesn't match xml's ID
 803         return true;
 804     }
 805 
 806     crm_log_xml_trace(xml, "replace-match");
 807     crm_log_xml_trace(replace, "replace-with");
 808     replace_node(xml, replace);
 809 
 810     // Found a match and replaced it; stop traversing tree
 811     return false;
 812 }
 813 
 814 /*!
 815  * \internal
 816  * \brief Search an XML tree depth-first and replace the first matching element
 817  *
 818  * This function does not attempt to match the tree root (\p xml).
 819  *
 820  * A match with a node \c node is defined as follows:
 821  * * \c node and \p replace are both element nodes of the same type.
 822  * * If \p replace has the \c PCMK_XA_ID attribute set, then \c node has
 823  *   \c PCMK_XA_ID set to the same value.
 824  *
 825  * \param[in,out] xml      XML tree to search
 826  * \param[in]     replace  XML to replace a matching element with a copy of
 827  *
 828  * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on
 829  *         successful replacement and an error code otherwise)
 830  */
 831 int
 832 pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
     /* [previous][next][first][last][top][bottom][index][help] */
 833 {
 834     /* @COMPAT Some of this behavior (like not matching the tree root, which is
 835      * allowed by pcmk__xe_update_match()) is questionable for general use but
 836      * required for backward compatibility by cib_process_replace() and
 837      * cib_process_delete(). Behavior can change at a major version release if
 838      * desired.
 839      */
 840     CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL);
 841 
 842     for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
 843          xml = pcmk__xe_next(xml, NULL)) {
 844 
 845         if (!pcmk__xml_tree_foreach(xml, replace_xe_if_matching, replace)) {
 846             // Found and replaced an element
 847             return pcmk_rc_ok;
 848         }
 849     }
 850 
 851     // No match found in this subtree
 852     return ENXIO;
 853 }
 854 
 855 //! User data for \c update_xe_if_matching()
 856 struct update_data {
 857     xmlNode *update;    //!< Update source
 858     uint32_t flags;     //!< Group of <tt>enum pcmk__xa_flags</tt>
 859 };
 860 
 861 /*!
 862  * \internal
 863  * \brief Update one XML subtree with another if the two match
 864  *
 865  * "Update" means to merge a source subtree into a target subtree (see
 866  * \c update_xe()).
 867  *
 868  * A match is defined as follows:
 869  * * \p xml and \p user_data->update are both element nodes of the same type.
 870  * * \p xml and \p user_data->update have the same \c PCMK_XA_ID attribute
 871  *   value, or \c PCMK_XA_ID is unset in both
 872  *
 873  * \param[in,out] xml        XML subtree to update with \p user_data->update
 874  *                           upon match
 875  * \param[in]     user_data  <tt>struct update_data</tt> object
 876  *
 877  * \return \c true to continue traversing the tree, or \c false to stop (because
 878  *         \p xml was updated by \p user_data->update)
 879  *
 880  * \note This is compatible with \c pcmk__xml_tree_foreach().
 881  */
 882 static bool
 883 update_xe_if_matching(xmlNode *xml, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 884 {
 885     struct update_data *data = user_data;
 886     xmlNode *update = data->update;
 887 
 888     if (!pcmk__xe_is(update, (const char *) xml->name)) {
 889         // No match: either not both elements, or different element types
 890         return true;
 891     }
 892 
 893     if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) {
 894         // No match: ID mismatch
 895         return true;
 896     }
 897 
 898     crm_log_xml_trace(xml, "update-match");
 899     crm_log_xml_trace(update, "update-with");
 900     update_xe(NULL, xml, update, data->flags);
 901 
 902     // Found a match and replaced it; stop traversing tree
 903     return false;
 904 }
 905 
 906 /*!
 907  * \internal
 908  * \brief Search an XML tree depth-first and update the first matching element
 909  *
 910  * "Update" means to merge a source subtree into a target subtree (see
 911  * \c update_xe()).
 912  *
 913  * A match with a node \c node is defined as follows:
 914  * * \c node and \p update are both element nodes of the same type.
 915  * * \c node and \p update have the same \c PCMK_XA_ID attribute value, or
 916  *   \c PCMK_XA_ID is unset in both
 917  *
 918  * \param[in,out] xml     XML tree to search
 919  * \param[in]     update  XML to update a matching element with
 920  * \param[in]     flags   Group of <tt>enum pcmk__xa_flags</tt>
 921  *
 922  * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on
 923  *         successful update and an error code otherwise)
 924  */
 925 int
 926 pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 927 {
 928     /* @COMPAT In pcmk__xe_delete_match() and pcmk__xe_replace_match(), we
 929      * compare IDs only if the equivalent of the update argument has an ID.
 930      * Here, we're stricter: we consider it a mismatch if only one element has
 931      * an ID attribute, or if both elements have IDs but they don't match.
 932      *
 933      * Perhaps we should align the behavior at a major version release.
 934      */
 935     struct update_data data = {
 936         .update = update,
 937         .flags = flags,
 938     };
 939 
 940     CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL);
 941 
 942     if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, &data)) {
 943         // Found and updated an element
 944         return pcmk_rc_ok;
 945     }
 946 
 947     // No match found in this subtree
 948     return ENXIO;
 949 }
 950 
 951 void
 952 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
     /* [previous][next][first][last][top][bottom][index][help] */
 953 {
 954     while (true) {
 955         const char *name, *value;
 956 
 957         name = va_arg(pairs, const char *);
 958         if (name == NULL) {
 959             return;
 960         }
 961 
 962         value = va_arg(pairs, const char *);
 963         if (value != NULL) {
 964             crm_xml_add(node, name, value);
 965         }
 966     }
 967 }
 968 
 969 void
 970 pcmk__xe_set_props(xmlNodePtr node, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 971 {
 972     va_list pairs;
 973     va_start(pairs, node);
 974     pcmk__xe_set_propv(node, pairs);
 975     va_end(pairs);
 976 }
 977 
 978 int
 979 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 980                        int (*handler)(xmlNode *xml, void *userdata),
 981                        void *userdata)
 982 {
 983     xmlNode *children = (xml? xml->children : NULL);
 984 
 985     pcmk__assert(handler != NULL);
 986 
 987     for (xmlNode *node = children; node != NULL; node = node->next) {
 988         if ((node->type == XML_ELEMENT_NODE)
 989             && ((child_element_name == NULL)
 990                 || pcmk__xe_is(node, child_element_name))) {
 991             int rc = handler(node, userdata);
 992 
 993             if (rc != pcmk_rc_ok) {
 994                 return rc;
 995             }
 996         }
 997     }
 998 
 999     return pcmk_rc_ok;
1000 }
1001 
1002 // XML attribute handling
1003 
1004 /*!
1005  * \brief Create an XML attribute with specified name and value
1006  *
1007  * \param[in,out] node   XML node to modify
1008  * \param[in]     name   Attribute name to set
1009  * \param[in]     value  Attribute value to set
1010  *
1011  * \return New value on success, \c NULL otherwise
1012  * \note This does nothing if node, name, or value are \c NULL or empty.
1013  */
1014 const char *
1015 crm_xml_add(xmlNode *node, const char *name, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1016 {
1017     // @TODO Replace with internal function that returns the new attribute
1018     bool dirty = FALSE;
1019     xmlAttr *attr = NULL;
1020 
1021     CRM_CHECK(node != NULL, return NULL);
1022     CRM_CHECK(name != NULL, return NULL);
1023 
1024     if (value == NULL) {
1025         return NULL;
1026     }
1027 
1028     if (pcmk__tracking_xml_changes(node, FALSE)) {
1029         const char *old = crm_element_value(node, name);
1030 
1031         if (old == NULL || value == NULL || strcmp(old, value) != 0) {
1032             dirty = TRUE;
1033         }
1034     }
1035 
1036     if (dirty && (pcmk__check_acl(node, name, pcmk__xf_acl_create) == FALSE)) {
1037         crm_trace("Cannot add %s=%s to %s", name, value, node->name);
1038         return NULL;
1039     }
1040 
1041     attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
1042 
1043     /* If the attribute already exists, this does nothing. Attribute values
1044      * don't get private data.
1045      */
1046     pcmk__xml_new_private_data((xmlNode *) attr);
1047 
1048     if (dirty) {
1049         pcmk__mark_xml_attr_dirty(attr);
1050     }
1051 
1052     CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
1053     return (char *)attr->children->content;
1054 }
1055 
1056 
1057 /*!
1058  * \brief Create an XML attribute with specified name and integer value
1059  *
1060  * This is like \c crm_xml_add() but taking an integer value.
1061  *
1062  * \param[in,out] node   XML node to modify
1063  * \param[in]     name   Attribute name to set
1064  * \param[in]     value  Attribute value to set
1065  *
1066  * \return New value as string on success, \c NULL otherwise
1067  * \note This does nothing if node or name are \c NULL or empty.
1068  */
1069 const char *
1070 crm_xml_add_int(xmlNode *node, const char *name, int value)
     /* [previous][next][first][last][top][bottom][index][help] */
1071 {
1072     char *number = pcmk__itoa(value);
1073     const char *added = crm_xml_add(node, name, number);
1074 
1075     free(number);
1076     return added;
1077 }
1078 
1079 /*!
1080  * \brief Create an XML attribute with specified name and unsigned value
1081  *
1082  * This is like \c crm_xml_add() but taking a guint value.
1083  *
1084  * \param[in,out] node   XML node to modify
1085  * \param[in]     name   Attribute name to set
1086  * \param[in]     ms     Attribute value to set
1087  *
1088  * \return New value as string on success, \c NULL otherwise
1089  * \note This does nothing if node or name are \c NULL or empty.
1090  */
1091 const char *
1092 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
     /* [previous][next][first][last][top][bottom][index][help] */
1093 {
1094     char *number = crm_strdup_printf("%u", ms);
1095     const char *added = crm_xml_add(node, name, number);
1096 
1097     free(number);
1098     return added;
1099 }
1100 
1101 // Maximum size of null-terminated string representation of 64-bit integer
1102 // -9223372036854775808
1103 #define LLSTRSIZE 21
1104 
1105 /*!
1106  * \brief Create an XML attribute with specified name and long long int value
1107  *
1108  * This is like \c crm_xml_add() but taking a long long int value. It is a
1109  * useful equivalent for defined types like time_t, etc.
1110  *
1111  * \param[in,out] xml    XML node to modify
1112  * \param[in]     name   Attribute name to set
1113  * \param[in]     value  Attribute value to set
1114  *
1115  * \return New value as string on success, \c NULL otherwise
1116  * \note This does nothing if xml or name are \c NULL or empty.
1117  *       This does not support greater than 64-bit values.
1118  */
1119 const char *
1120 crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
     /* [previous][next][first][last][top][bottom][index][help] */
1121 {
1122     char s[LLSTRSIZE] = { '\0', };
1123 
1124     if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
1125         return NULL;
1126     }
1127     return crm_xml_add(xml, name, s);
1128 }
1129 
1130 /*!
1131  * \brief Create XML attributes for seconds and microseconds
1132  *
1133  * This is like \c crm_xml_add() but taking a struct timeval.
1134  *
1135  * \param[in,out] xml        XML node to modify
1136  * \param[in]     name_sec   Name of XML attribute for seconds
1137  * \param[in]     name_usec  Name of XML attribute for microseconds (or NULL)
1138  * \param[in]     value      Time value to set
1139  *
1140  * \return New seconds value as string on success, \c NULL otherwise
1141  * \note This does nothing if xml, name_sec, or value is \c NULL.
1142  */
1143 const char *
1144 crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
     /* [previous][next][first][last][top][bottom][index][help] */
1145                     const struct timeval *value)
1146 {
1147     const char *added = NULL;
1148 
1149     if (xml && name_sec && value) {
1150         added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
1151         if (added && name_usec) {
1152             // Any error is ignored (we successfully added seconds)
1153             crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
1154         }
1155     }
1156     return added;
1157 }
1158 
1159 /*!
1160  * \brief Retrieve the value of an XML attribute
1161  *
1162  * \param[in] data   XML node to check
1163  * \param[in] name   Attribute name to check
1164  *
1165  * \return Value of specified attribute (may be \c NULL)
1166  */
1167 const char *
1168 crm_element_value(const xmlNode *data, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
1169 {
1170     xmlAttr *attr = NULL;
1171 
1172     if (data == NULL) {
1173         crm_err("Couldn't find %s in NULL", name ? name : "<null>");
1174         CRM_LOG_ASSERT(data != NULL);
1175         return NULL;
1176 
1177     } else if (name == NULL) {
1178         crm_err("Couldn't find NULL in %s", data->name);
1179         return NULL;
1180     }
1181 
1182     attr = xmlHasProp(data, (pcmkXmlStr) name);
1183     if (!attr || !attr->children) {
1184         return NULL;
1185     }
1186     return (const char *) attr->children->content;
1187 }
1188 
1189 /*!
1190  * \brief Retrieve the integer value of an XML attribute
1191  *
1192  * This is like \c crm_element_value() but getting the value as an integer.
1193  *
1194  * \param[in]  data  XML node to check
1195  * \param[in]  name  Attribute name to check
1196  * \param[out] dest  Where to store element value
1197  *
1198  * \return 0 on success, -1 otherwise
1199  */
1200 int
1201 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
1202 {
1203     const char *value = NULL;
1204 
1205     CRM_CHECK(dest != NULL, return -1);
1206     value = crm_element_value(data, name);
1207     if (value) {
1208         long long value_ll;
1209         int rc = pcmk__scan_ll(value, &value_ll, 0LL);
1210 
1211         *dest = PCMK__PARSE_INT_DEFAULT;
1212         if (rc != pcmk_rc_ok) {
1213             crm_warn("Using default for %s "
1214                      "because '%s' is not a valid integer: %s",
1215                      name, value, pcmk_rc_str(rc));
1216         } else if ((value_ll < INT_MIN) || (value_ll > INT_MAX)) {
1217             crm_warn("Using default for %s because '%s' is out of range",
1218                      name, value);
1219         } else {
1220             *dest = (int) value_ll;
1221             return 0;
1222         }
1223     }
1224     return -1;
1225 }
1226 
1227 /*!
1228  * \internal
1229  * \brief Retrieve a flag group from an XML attribute value
1230  *
1231  * This is like \c crm_element_value() except getting the value as a 32-bit
1232  * unsigned integer.
1233  *
1234  * \param[in]  xml            XML node to check
1235  * \param[in]  name           Attribute name to check (must not be NULL)
1236  * \param[out] dest           Where to store flags (may be NULL to just
1237  *                            validate type)
1238  * \param[in]  default_value  What to use for missing or invalid value
1239  *
1240  * \return Standard Pacemaker return code
1241  */
1242 int
1243 pcmk__xe_get_flags(const xmlNode *xml, const char *name, uint32_t *dest,
     /* [previous][next][first][last][top][bottom][index][help] */
1244                    uint32_t default_value)
1245 {
1246     const char *value = NULL;
1247     long long value_ll = 0LL;
1248     int rc = pcmk_rc_ok;
1249 
1250     if (dest != NULL) {
1251         *dest = default_value;
1252     }
1253 
1254     if (name == NULL) {
1255         return EINVAL;
1256     }
1257     if (xml == NULL) {
1258         return pcmk_rc_ok;
1259     }
1260     value = crm_element_value(xml, name);
1261     if (value == NULL) {
1262         return pcmk_rc_ok;
1263     }
1264 
1265     rc = pcmk__scan_ll(value, &value_ll, default_value);
1266     if ((value_ll < 0) || (value_ll > UINT32_MAX)) {
1267         value_ll = default_value;
1268         if (rc == pcmk_rc_ok) {
1269             rc = pcmk_rc_bad_input;
1270         }
1271     }
1272 
1273     if (dest != NULL) {
1274         *dest = (uint32_t) value_ll;
1275     }
1276     return rc;
1277 }
1278 
1279 /*!
1280  * \brief Retrieve the long long integer value of an XML attribute
1281  *
1282  * This is like \c crm_element_value() but getting the value as a long long int.
1283  *
1284  * \param[in]  data  XML node to check
1285  * \param[in]  name  Attribute name to check
1286  * \param[out] dest  Where to store element value
1287  *
1288  * \return 0 on success, -1 otherwise
1289  */
1290 int
1291 crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
1292 {
1293     const char *value = NULL;
1294 
1295     CRM_CHECK(dest != NULL, return -1);
1296     value = crm_element_value(data, name);
1297     if (value != NULL) {
1298         int rc = pcmk__scan_ll(value, dest, PCMK__PARSE_INT_DEFAULT);
1299 
1300         if (rc == pcmk_rc_ok) {
1301             return 0;
1302         }
1303         crm_warn("Using default for %s "
1304                  "because '%s' is not a valid integer: %s",
1305                  name, value, pcmk_rc_str(rc));
1306     }
1307     return -1;
1308 }
1309 
1310 /*!
1311  * \brief Retrieve the millisecond value of an XML attribute
1312  *
1313  * This is like \c crm_element_value() but returning the value as a guint.
1314  *
1315  * \param[in]  data   XML node to check
1316  * \param[in]  name   Attribute name to check
1317  * \param[out] dest   Where to store attribute value
1318  *
1319  * \return \c pcmk_ok on success, -1 otherwise
1320  */
1321 int
1322 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
1323 {
1324     const char *value = NULL;
1325     long long value_ll;
1326     int rc = pcmk_rc_ok;
1327 
1328     CRM_CHECK(dest != NULL, return -1);
1329     *dest = 0;
1330     value = crm_element_value(data, name);
1331     rc = pcmk__scan_ll(value, &value_ll, 0LL);
1332     if (rc != pcmk_rc_ok) {
1333         crm_warn("Using default for %s "
1334                  "because '%s' is not valid milliseconds: %s",
1335                  name, value, pcmk_rc_str(rc));
1336         return -1;
1337     }
1338     if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
1339         crm_warn("Using default for %s because '%s' is out of range",
1340                  name, value);
1341         return -1;
1342     }
1343     *dest = (guint) value_ll;
1344     return pcmk_ok;
1345 }
1346 
1347 /*!
1348  * \brief Retrieve the seconds-since-epoch value of an XML attribute
1349  *
1350  * This is like \c crm_element_value() but returning the value as a time_t.
1351  *
1352  * \param[in]  xml    XML node to check
1353  * \param[in]  name   Attribute name to check
1354  * \param[out] dest   Where to store attribute value
1355  *
1356  * \return \c pcmk_ok on success, -1 otherwise
1357  */
1358 int
1359 crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
1360 {
1361     long long value_ll = 0;
1362 
1363     if (crm_element_value_ll(xml, name, &value_ll) < 0) {
1364         return -1;
1365     }
1366 
1367     /* Unfortunately, we can't do any bounds checking, since time_t has neither
1368      * standardized bounds nor constants defined for them.
1369      */
1370     *dest = (time_t) value_ll;
1371     return pcmk_ok;
1372 }
1373 
1374 /*!
1375  * \brief Retrieve the value of XML second/microsecond attributes as time
1376  *
1377  * This is like \c crm_element_value() but returning value as a struct timeval.
1378  *
1379  * \param[in]  xml        XML to parse
1380  * \param[in]  name_sec   Name of XML attribute for seconds
1381  * \param[in]  name_usec  Name of XML attribute for microseconds
1382  * \param[out] dest       Where to store result
1383  *
1384  * \return \c pcmk_ok on success, -errno on error
1385  * \note Values default to 0 if XML or XML attribute does not exist
1386  */
1387 int
1388 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
     /* [previous][next][first][last][top][bottom][index][help] */
1389                           const char *name_usec, struct timeval *dest)
1390 {
1391     long long value_i = 0;
1392 
1393     CRM_CHECK(dest != NULL, return -EINVAL);
1394     dest->tv_sec = 0;
1395     dest->tv_usec = 0;
1396 
1397     if (xml == NULL) {
1398         return pcmk_ok;
1399     }
1400 
1401     /* Unfortunately, we can't do any bounds checking, since there are no
1402      * constants provided for the bounds of time_t and suseconds_t, and
1403      * calculating them isn't worth the effort. If there are XML values
1404      * beyond the native sizes, there will probably be worse problems anyway.
1405      */
1406 
1407     // Parse seconds
1408     errno = 0;
1409     if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
1410         return -errno;
1411     }
1412     dest->tv_sec = (time_t) value_i;
1413 
1414     // Parse microseconds
1415     if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
1416         return -errno;
1417     }
1418     dest->tv_usec = (suseconds_t) value_i;
1419 
1420     return pcmk_ok;
1421 }
1422 
1423 /*!
1424  * \internal
1425  * \brief Get a date/time object from an XML attribute value
1426  *
1427  * \param[in]  xml   XML with attribute to parse (from CIB)
1428  * \param[in]  attr  Name of attribute to parse
1429  * \param[out] t     Where to create date/time object
1430  *                   (\p *t must be NULL initially)
1431  *
1432  * \return Standard Pacemaker return code
1433  * \note The caller is responsible for freeing \p *t using crm_time_free().
1434  */
1435 int
1436 pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
     /* [previous][next][first][last][top][bottom][index][help] */
1437 {
1438     const char *value = NULL;
1439 
1440     if ((t == NULL) || (*t != NULL) || (xml == NULL) || (attr == NULL)) {
1441         return EINVAL;
1442     }
1443 
1444     value = crm_element_value(xml, attr);
1445     if (value != NULL) {
1446         *t = crm_time_new(value);
1447         if (*t == NULL) {
1448             return pcmk_rc_unpack_error;
1449         }
1450     }
1451     return pcmk_rc_ok;
1452 }
1453 
1454 /*!
1455  * \brief Retrieve a copy of the value of an XML attribute
1456  *
1457  * This is like \c crm_element_value() but allocating new memory for the result.
1458  *
1459  * \param[in] data   XML node to check
1460  * \param[in] name   Attribute name to check
1461  *
1462  * \return Value of specified attribute (may be \c NULL)
1463  * \note The caller is responsible for freeing the result.
1464  */
1465 char *
1466 crm_element_value_copy(const xmlNode *data, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
1467 {
1468     return pcmk__str_copy(crm_element_value(data, name));
1469 }
1470 
1471 /*!
1472  * \internal
1473  * \brief Add a boolean attribute to an XML node.
1474  *
1475  * \param[in,out] node  XML node to add attributes to
1476  * \param[in]     name  XML attribute to create
1477  * \param[in]     value Value to give to the attribute
1478  */
1479 void
1480 pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
     /* [previous][next][first][last][top][bottom][index][help] */
1481 {
1482     crm_xml_add(node, name, pcmk__btoa(value));
1483 }
1484 
1485 /*!
1486  * \internal
1487  * \brief Extract a boolean attribute's value from an XML element, with
1488  *        error checking
1489  *
1490  * \param[in]  node  XML node to get attribute from
1491  * \param[in]  name  XML attribute to get
1492  * \param[out] value Destination for the value of the attribute
1493  *
1494  * \return EINVAL if \p name or \p value are NULL, ENODATA if \p node is
1495  *         NULL or the attribute does not exist, pcmk_rc_unknown_format
1496  *         if the attribute is not a boolean, and pcmk_rc_ok otherwise.
1497  *
1498  * \note \p value only has any meaning if the return value is pcmk_rc_ok.
1499  */
1500 int
1501 pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1502 {
1503     const char *xml_value = NULL;
1504     int ret, rc;
1505 
1506     if (node == NULL) {
1507         return ENODATA;
1508     } else if (name == NULL || value == NULL) {
1509         return EINVAL;
1510     }
1511 
1512     xml_value = crm_element_value(node, name);
1513 
1514     if (xml_value == NULL) {
1515         return ENODATA;
1516     }
1517 
1518     rc = crm_str_to_boolean(xml_value, &ret);
1519     if (rc == 1) {
1520         *value = ret;
1521         return pcmk_rc_ok;
1522     } else {
1523         return pcmk_rc_bad_input;
1524     }
1525 }
1526 
1527 /*!
1528  * \internal
1529  * \brief Extract a boolean attribute's value from an XML element
1530  *
1531  * \param[in] node XML node to get attribute from
1532  * \param[in] name XML attribute to get
1533  *
1534  * \return True if the given \p name is an attribute on \p node and has
1535  *         the value \c PCMK_VALUE_TRUE, False in all other cases
1536  */
1537 bool
1538 pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
1539 {
1540     bool value = false;
1541     int rc;
1542 
1543     rc = pcmk__xe_get_bool_attr(node, name, &value);
1544     return rc == pcmk_rc_ok && value == true;
1545 }
1546 
1547 // Deprecated functions kept only for backward API compatibility
1548 // LCOV_EXCL_START
1549 
1550 #include <glib.h>                       // gboolean, GSList
1551 
1552 #include <crm/common/nvpair_compat.h>   // pcmk_xml_attrs2nvpairs(), etc.
1553 #include <crm/common/xml_compat.h>      // crm_xml_sanitize_id()
1554 #include <crm/common/xml_element_compat.h>
1555 
1556 xmlNode *
1557 expand_idref(xmlNode *input, xmlNode *top)
     /* [previous][next][first][last][top][bottom][index][help] */
1558 {
1559     return pcmk__xe_resolve_idref(input, top);
1560 }
1561 
1562 void
1563 crm_xml_set_id(xmlNode *xml, const char *format, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
1564 {
1565     va_list ap;
1566     int len = 0;
1567     char *id = NULL;
1568 
1569     /* equivalent to crm_strdup_printf() */
1570     va_start(ap, format);
1571     len = vasprintf(&id, format, ap);
1572     va_end(ap);
1573     pcmk__assert(len > 0);
1574 
1575     crm_xml_sanitize_id(id);
1576     crm_xml_add(xml, PCMK_XA_ID, id);
1577     free(id);
1578 }
1579 
1580 xmlNode *
1581 sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
     /* [previous][next][first][last][top][bottom][index][help] */
1582 {
1583     xmlNode *child = NULL;
1584     GSList *nvpairs = NULL;
1585     xmlNode *result = NULL;
1586 
1587     CRM_CHECK(input != NULL, return NULL);
1588 
1589     result = pcmk__xe_create(parent, (const char *) input->name);
1590     nvpairs = pcmk_xml_attrs2nvpairs(input);
1591     nvpairs = pcmk_sort_nvpairs(nvpairs);
1592     pcmk_nvpairs2xml_attrs(nvpairs, result);
1593     pcmk_free_nvpairs(nvpairs);
1594 
1595     for (child = pcmk__xe_first_child(input, NULL, NULL, NULL); child != NULL;
1596          child = pcmk__xe_next(child, NULL)) {
1597 
1598         if (recursive) {
1599             sorted_xml(child, result, recursive);
1600         } else {
1601             pcmk__xml_copy(result, child);
1602         }
1603     }
1604 
1605     return result;
1606 }
1607 
1608 // LCOV_EXCL_STOP
1609 // End deprecated API

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