root/include/crm/common/xml_internal.h

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

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. pcmk__xe_id
  2. pcmk__xe_is
  3. pcmk__xml_first_child
  4. pcmk__xml_next
  5. pcmk__xe_next
  6. pcmk__xe_set_content
  7. pcmk__xe_set_props
  8. pcmk__xml_attr_value
  9. pcmk__map_element_name

   1 /*
   2  * Copyright 2017-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 #ifndef PCMK__XML_INTERNAL__H
  11 #define PCMK__XML_INTERNAL__H
  12 
  13 /*
  14  * Internal-only wrappers for and extensions to libxml2 (libxslt)
  15  */
  16 
  17 #include <stdlib.h>
  18 #include <stdint.h>   // uint32_t
  19 #include <stdio.h>
  20 #include <string.h>
  21 
  22 #include <crm/crm.h>  /* transitively imports qblog.h */
  23 #include <crm/common/output_internal.h>
  24 #include <crm/common/xml_io_internal.h>
  25 #include <crm/common/xml_names_internal.h>    // PCMK__XE_PROMOTABLE_LEGACY
  26 
  27 #include <libxml/relaxng.h>
  28 
  29 /*!
  30  * \brief Base for directing lib{xml2,xslt} log into standard libqb backend
  31  *
  32  * This macro implements the core of what can be needed for directing
  33  * libxml2 or libxslt error messaging into standard, preconfigured
  34  * libqb-backed log stream.
  35  *
  36  * It's a bit unfortunate that libxml2 (and more sparsely, also libxslt)
  37  * emits a single message by chunks (location is emitted separatedly from
  38  * the message itself), so we have to take the effort to combine these
  39  * chunks back to single message.  Whether to do this or not is driven
  40  * with \p dechunk toggle.
  41  *
  42  * The form of a macro was chosen for implicit deriving of __FILE__, etc.
  43  * and also because static dechunking buffer should be differentiated per
  44  * library (here we assume different functions referring to this macro
  45  * will not ever be using both at once), preferably also per-library
  46  * context of use to avoid clashes altogether.
  47  *
  48  * Note that we cannot use qb_logt, because callsite data have to be known
  49  * at the moment of compilation, which it is not always the case -- xml_log
  50  * (and unfortunately there's no clear explanation of the fail to compile).
  51  *
  52  * Also note that there's no explicit guard against said libraries producing
  53  * never-newline-terminated chunks (which would just keep consuming memory),
  54  * as it's quite improbable.  Termination of the program in between the
  55  * same-message chunks will raise a flag with valgrind and the likes, though.
  56  *
  57  * And lastly, regarding how dechunking combines with other non-message
  58  * parameters -- for \p priority, most important running specification
  59  * wins (possibly elevated to LOG_ERR in case of nonconformance with the
  60  * newline-termination "protocol"), \p dechunk is expected to always be
  61  * on once it was at the start, and the rest (\p postemit and \p prefix)
  62  * are picked directly from the last chunk entry finalizing the message
  63  * (also reasonable to always have it the same with all related entries).
  64  *
  65  * \param[in] priority Syslog priority for the message to be logged
  66  * \param[in] dechunk  Whether to dechunk new-line terminated message
  67  * \param[in] postemit Code to be executed once message is sent out
  68  * \param[in] prefix   How to prefix the message or NULL for raw passing
  69  * \param[in] fmt      Format string as with printf-like functions
  70  * \param[in] ap       Variable argument list to supplement \p fmt format string
  71  */
  72 #define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)        \
  73 do {                                                                            \
  74     if (!(dechunk) && (prefix) == NULL) {  /* quick pass */                     \
  75         qb_log_from_external_source_va(__func__, __FILE__, (fmt),               \
  76                                        (priority), __LINE__, 0, (ap));          \
  77         (void) (postemit);                                                      \
  78     } else {                                                                    \
  79         int CXLB_len = 0;                                                       \
  80         char *CXLB_buf = NULL;                                                  \
  81         static int CXLB_buffer_len = 0;                                         \
  82         static char *CXLB_buffer = NULL;                                        \
  83         static uint8_t CXLB_priority = 0;                                       \
  84                                                                                 \
  85         CXLB_len = vasprintf(&CXLB_buf, (fmt), (ap));                           \
  86                                                                                 \
  87         if (CXLB_len <= 0 || CXLB_buf[CXLB_len - 1] == '\n' || !(dechunk)) {    \
  88             if (CXLB_len < 0) {                                                 \
  89                 CXLB_buf = (char *) "LOG CORRUPTION HAZARD"; /*we don't modify*/\
  90                 CXLB_priority = QB_MIN(CXLB_priority, LOG_ERR);                 \
  91             } else if (CXLB_len > 0 /* && (dechunk) */                          \
  92                        && CXLB_buf[CXLB_len - 1] == '\n') {                     \
  93                 CXLB_buf[CXLB_len - 1] = '\0';                                  \
  94             }                                                                   \
  95             if (CXLB_buffer) {                                                  \
  96                 qb_log_from_external_source(__func__, __FILE__, "%s%s%s",       \
  97                                             CXLB_priority, __LINE__, 0,         \
  98                                             (prefix) != NULL ? (prefix) : "",   \
  99                                             CXLB_buffer, CXLB_buf);             \
 100                 free(CXLB_buffer);                                              \
 101             } else {                                                            \
 102                 qb_log_from_external_source(__func__, __FILE__, "%s%s",         \
 103                                             (priority), __LINE__, 0,            \
 104                                             (prefix) != NULL ? (prefix) : "",   \
 105                                             CXLB_buf);                          \
 106             }                                                                   \
 107             if (CXLB_len < 0) {                                                 \
 108                 CXLB_buf = NULL;  /* restore temporary override */              \
 109             }                                                                   \
 110             CXLB_buffer = NULL;                                                 \
 111             CXLB_buffer_len = 0;                                                \
 112             (void) (postemit);                                                  \
 113                                                                                 \
 114         } else if (CXLB_buffer == NULL) {                                       \
 115             CXLB_buffer_len = CXLB_len;                                         \
 116             CXLB_buffer = CXLB_buf;                                             \
 117             CXLB_buf = NULL;                                                    \
 118             CXLB_priority = (priority);  /* remember as a running severest */   \
 119                                                                                 \
 120         } else {                                                                \
 121             CXLB_buffer = realloc(CXLB_buffer, 1 + CXLB_buffer_len + CXLB_len); \
 122             memcpy(CXLB_buffer + CXLB_buffer_len, CXLB_buf, CXLB_len);          \
 123             CXLB_buffer_len += CXLB_len;                                        \
 124             CXLB_buffer[CXLB_buffer_len] = '\0';                                \
 125             CXLB_priority = QB_MIN(CXLB_priority, (priority));  /* severest? */ \
 126         }                                                                       \
 127         free(CXLB_buf);                                                         \
 128     }                                                                           \
 129 } while (0)
 130 
 131 /*
 132  * \enum pcmk__xml_fmt_options
 133  * \brief Bit flags to control format in XML logs and dumps
 134  */
 135 enum pcmk__xml_fmt_options {
 136     //! Exclude certain XML attributes (for calculating digests)
 137     pcmk__xml_fmt_filtered   = (1 << 0),
 138 
 139     //! Include indentation and newlines
 140     pcmk__xml_fmt_pretty     = (1 << 1),
 141 
 142     //! Include the opening tag of an XML element, and include XML comments
 143     pcmk__xml_fmt_open       = (1 << 3),
 144 
 145     //! Include the children of an XML element
 146     pcmk__xml_fmt_children   = (1 << 4),
 147 
 148     //! Include the closing tag of an XML element
 149     pcmk__xml_fmt_close      = (1 << 5),
 150 
 151     // @COMPAT Can we start including text nodes unconditionally?
 152     //! Include XML text nodes
 153     pcmk__xml_fmt_text       = (1 << 6),
 154 
 155     // @COMPAT Remove when v1 patchsets are removed
 156     //! Log a created XML subtree
 157     pcmk__xml_fmt_diff_plus  = (1 << 7),
 158 
 159     // @COMPAT Remove when v1 patchsets are removed
 160     //! Log a removed XML subtree
 161     pcmk__xml_fmt_diff_minus = (1 << 8),
 162 
 163     // @COMPAT Remove when v1 patchsets are removed
 164     //! Log a minimal version of an XML diff (only showing the changes)
 165     pcmk__xml_fmt_diff_short = (1 << 9),
 166 };
 167 
 168 int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data,
 169                    int depth, uint32_t options);
 170 int pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml);
 171 
 172 /* XML search strings for guest, remote and pacemaker_remote nodes */
 173 
 174 /* search string to find CIB resources entries for cluster nodes */
 175 #define PCMK__XP_MEMBER_NODE_CONFIG                                 \
 176     "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_NODES    \
 177     "/" PCMK_XE_NODE                                                \
 178     "[not(@" PCMK_XA_TYPE ") or @" PCMK_XA_TYPE "='" PCMK_VALUE_MEMBER "']"
 179 
 180 /* search string to find CIB resources entries for guest nodes */
 181 #define PCMK__XP_GUEST_NODE_CONFIG \
 182     "//" PCMK_XE_CIB "//" PCMK_XE_CONFIGURATION "//" PCMK_XE_PRIMITIVE  \
 183     "//" PCMK_XE_META_ATTRIBUTES "//" PCMK_XE_NVPAIR                    \
 184     "[@" PCMK_XA_NAME "='" PCMK_META_REMOTE_NODE "']"
 185 
 186 /* search string to find CIB resources entries for remote nodes */
 187 #define PCMK__XP_REMOTE_NODE_CONFIG                                     \
 188     "//" PCMK_XE_CIB "//" PCMK_XE_CONFIGURATION "//" PCMK_XE_PRIMITIVE  \
 189     "[@" PCMK_XA_TYPE "='" PCMK_VALUE_REMOTE "']"                       \
 190     "[@" PCMK_XA_PROVIDER "='pacemaker']"
 191 
 192 /* search string to find CIB node status entries for pacemaker_remote nodes */
 193 #define PCMK__XP_REMOTE_NODE_STATUS                                 \
 194     "//" PCMK_XE_CIB "//" PCMK_XE_STATUS "//" PCMK__XE_NODE_STATE   \
 195     "[@" PCMK_XA_REMOTE_NODE "='" PCMK_VALUE_TRUE "']"
 196 /*!
 197  * \internal
 198  * \brief Serialize XML (using libxml) into provided descriptor
 199  *
 200  * \param[in] fd  File descriptor to (piece-wise) write to
 201  * \param[in] cur XML subtree to proceed
 202  * 
 203  * \return a standard Pacemaker return code
 204  */
 205 int pcmk__xml2fd(int fd, xmlNode *cur);
 206 
 207 enum pcmk__xml_artefact_ns {
 208     pcmk__xml_artefact_ns_legacy_rng = 1,
 209     pcmk__xml_artefact_ns_legacy_xslt,
 210     pcmk__xml_artefact_ns_base_rng,
 211     pcmk__xml_artefact_ns_base_xslt,
 212 };
 213 
 214 void pcmk__strip_xml_text(xmlNode *xml);
 215 const char *pcmk__xe_add_last_written(xmlNode *xe);
 216 
 217 xmlNode *pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
 218                               const char *attr_n, const char *attr_v);
 219 
 220 
 221 void pcmk__xe_remove_attr(xmlNode *element, const char *name);
 222 bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data);
 223 void pcmk__xe_remove_matching_attrs(xmlNode *element,
 224                                     bool (*match)(xmlAttrPtr, void *),
 225                                     void *user_data);
 226 int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search);
 227 int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace);
 228 int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags);
 229 
 230 GString *pcmk__element_xpath(const xmlNode *xml);
 231 
 232 /*!
 233  * \internal
 234  * \enum pcmk__xml_escape_type
 235  * \brief Indicators of which XML characters to escape
 236  *
 237  * XML allows the escaping of special characters by replacing them with entity
 238  * references (for example, <tt>"&quot;"</tt>) or character references (for
 239  * example, <tt>"&#13;"</tt>).
 240  *
 241  * The special characters <tt>'&'</tt> (except as the beginning of an entity
 242  * reference) and <tt>'<'</tt> are not allowed in their literal forms in XML
 243  * character data. Character data is non-markup text (for example, the content
 244  * of a text node). <tt>'>'</tt> is allowed under most circumstances; we escape
 245  * it for safety and symmetry.
 246  *
 247  * For more details, see the "Character Data and Markup" section of the XML
 248  * spec, currently section 2.4:
 249  * https://www.w3.org/TR/xml/#dt-markup
 250  *
 251  * Attribute values are handled specially.
 252  * * If an attribute value is delimited by single quotes, then single quotes
 253  *   must be escaped within the value.
 254  * * Similarly, if an attribute value is delimited by double quotes, then double
 255  *   quotes must be escaped within the value.
 256  * * A conformant XML processor replaces a literal whitespace character (tab,
 257  *   newline, carriage return, space) in an attribute value with a space
 258  *   (\c '#x20') character. However, a reference to a whitespace character (for
 259  *   example, \c "&#x0A;" for \c '\n') does not get replaced.
 260  *   * For more details, see the "Attribute-Value Normalization" section of the
 261  *     XML spec, currently section 3.3.3. Note that the default attribute type
 262  *     is CDATA; we don't deal with NMTOKENS, etc.:
 263  *     https://www.w3.org/TR/xml/#AVNormalize
 264  *
 265  * Pacemaker always delimits attribute values with double quotes, so there's no
 266  * need to escape single quotes.
 267  *
 268  * Newlines and tabs should be escaped in attribute values when XML is
 269  * serialized to text, so that future parsing preserves them rather than
 270  * normalizing them to spaces.
 271  *
 272  * We always escape carriage returns, so that they're not converted to spaces
 273  * during attribute-value normalization and because displaying them as literals
 274  * is messy.
 275  */
 276 enum pcmk__xml_escape_type {
 277     /*!
 278      * For text nodes.
 279      * * Escape \c '<', \c '>', and \c '&' using entity references.
 280      * * Do not escape \c '\n' and \c '\t'.
 281      * * Escape other non-printing characters using character references.
 282      */
 283     pcmk__xml_escape_text,
 284 
 285     /*!
 286      * For attribute values.
 287      * * Escape \c '<', \c '>', \c '&', and \c '"' using entity references.
 288      * * Escape \c '\n', \c '\t', and other non-printing characters using
 289      *   character references.
 290      */
 291     pcmk__xml_escape_attr,
 292 
 293     /* @COMPAT Drop escaping of at least '\n' and '\t' for
 294      * pcmk__xml_escape_attr_pretty when openstack-info, openstack-floating-ip,
 295      * and openstack-virtual-ip resource agents no longer depend on it.
 296      *
 297      * At time of writing, openstack-info may set a multiline value for the
 298      * openstack_ports node attribute. The other two agents query the value and
 299      * require it to be on one line with no spaces.
 300      */
 301     /*!
 302      * For attribute values displayed in text output delimited by double quotes.
 303      * * Escape \c '\n' as \c "\\n"
 304      * * Escape \c '\r' as \c "\\r"
 305      * * Escape \c '\t' as \c "\\t"
 306      * * Escape \c '"' as \c "\\""
 307      */
 308     pcmk__xml_escape_attr_pretty,
 309 };
 310 
 311 bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type);
 312 char *pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type);
 313 
 314 /*!
 315  * \internal
 316  * \brief Get the root directory to scan XML artefacts of given kind for
 317  *
 318  * \param[in] ns governs the hierarchy nesting against the inherent root dir
 319  *
 320  * \return root directory to scan XML artefacts of given kind for
 321  */
 322 char *
 323 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns);
 324 
 325 /*!
 326  * \internal
 327  * \brief Get the fully unwrapped path to particular XML artifact (RNG/XSLT)
 328  *
 329  * \param[in] ns       denotes path forming details (parent dir, suffix)
 330  * \param[in] filespec symbolic file specification to be combined with
 331  *                     #artefact_ns to form the final path
 332  * \return unwrapped path to particular XML artifact (RNG/XSLT)
 333  */
 334 char *pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns,
 335                               const char *filespec);
 336 
 337 /*!
 338  * \internal
 339  * \brief Retrieve the value of the \c PCMK_XA_ID XML attribute
 340  *
 341  * \param[in] xml  XML element to check
 342  *
 343  * \return Value of the \c PCMK_XA_ID attribute (may be \c NULL)
 344  */
 345 static inline const char *
 346 pcmk__xe_id(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348     return crm_element_value(xml, PCMK_XA_ID);
 349 }
 350 
 351 /*!
 352  * \internal
 353  * \brief Check whether an XML element is of a particular type
 354  *
 355  * \param[in] xml   XML element to compare
 356  * \param[in] name  XML element name to compare
 357  *
 358  * \return \c true if \p xml is of type \p name, otherwise \c false
 359  */
 360 static inline bool
 361 pcmk__xe_is(const xmlNode *xml, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 362 {
 363     return (xml != NULL) && (xml->name != NULL) && (name != NULL)
 364            && (strcmp((const char *) xml->name, name) == 0);
 365 }
 366 
 367 /*!
 368  * \internal
 369  * \brief Return first non-text child node of an XML node
 370  *
 371  * \param[in] parent  XML node to check
 372  *
 373  * \return First non-text child node of \p parent (or NULL if none)
 374  */
 375 static inline xmlNode *
 376 pcmk__xml_first_child(const xmlNode *parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 377 {
 378     xmlNode *child = (parent? parent->children : NULL);
 379 
 380     while (child && (child->type == XML_TEXT_NODE)) {
 381         child = child->next;
 382     }
 383     return child;
 384 }
 385 
 386 /*!
 387  * \internal
 388  * \brief Return next non-text sibling node of an XML node
 389  *
 390  * \param[in] child  XML node to check
 391  *
 392  * \return Next non-text sibling of \p child (or NULL if none)
 393  */
 394 static inline xmlNode *
 395 pcmk__xml_next(const xmlNode *child)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397     xmlNode *next = (child? child->next : NULL);
 398 
 399     while (next && (next->type == XML_TEXT_NODE)) {
 400         next = next->next;
 401     }
 402     return next;
 403 }
 404 
 405 /*!
 406  * \internal
 407  * \brief Return next non-text sibling element of an XML element
 408  *
 409  * \param[in] child  XML element to check
 410  *
 411  * \return Next sibling element of \p child (or NULL if none)
 412  */
 413 static inline xmlNode *
 414 pcmk__xe_next(const xmlNode *child)
     /* [previous][next][first][last][top][bottom][index][help] */
 415 {
 416     xmlNode *next = child? child->next : NULL;
 417 
 418     while (next && (next->type != XML_ELEMENT_NODE)) {
 419         next = next->next;
 420     }
 421     return next;
 422 }
 423 
 424 xmlNode *pcmk__xe_create(xmlNode *parent, const char *name);
 425 xmlNode *pcmk__xml_copy(xmlNode *parent, xmlNode *src);
 426 xmlNode *pcmk__xe_next_same(const xmlNode *node);
 427 
 428 void pcmk__xe_set_content(xmlNode *node, const char *format, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 429     G_GNUC_PRINTF(2, 3);
 430 
 431 /*!
 432  * \internal
 433  * \enum pcmk__xa_flags
 434  * \brief Flags for operations affecting XML attributes
 435  */
 436 enum pcmk__xa_flags {
 437     //! Flag has no effect
 438     pcmk__xaf_none          = 0U,
 439 
 440     //! Don't overwrite existing values
 441     pcmk__xaf_no_overwrite  = (1U << 0),
 442 
 443     /*!
 444      * Treat values as score updates where possible (see
 445      * \c pcmk__xe_set_score())
 446      */
 447     pcmk__xaf_score_update  = (1U << 1),
 448 };
 449 
 450 int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags);
 451 
 452 /*!
 453  * \internal
 454  * \brief Like pcmk__xe_set_props, but takes a va_list instead of
 455  *        arguments directly.
 456  *
 457  * \param[in,out] node   XML to add attributes to
 458  * \param[in]     pairs  NULL-terminated list of name/value pairs to add
 459  */
 460 void
 461 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs);
 462 
 463 /*!
 464  * \internal
 465  * \brief Add a NULL-terminated list of name/value pairs to the given
 466  *        XML node as properties.
 467  *
 468  * \param[in,out] node XML node to add properties to
 469  * \param[in]     ...  NULL-terminated list of name/value pairs
 470  *
 471  * \note A NULL name terminates the arguments; a NULL value will be skipped.
 472  */
 473 void
 474 pcmk__xe_set_props(xmlNodePtr node, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 475 G_GNUC_NULL_TERMINATED;
 476 
 477 /*!
 478  * \internal
 479  * \brief Get first attribute of an XML element
 480  *
 481  * \param[in] xe  XML element to check
 482  *
 483  * \return First attribute of \p xe (or NULL if \p xe is NULL or has none)
 484  */
 485 static inline xmlAttr *
 486 pcmk__xe_first_attr(const xmlNode *xe)
 487 {
 488     return (xe == NULL)? NULL : xe->properties;
 489 }
 490 
 491 /*!
 492  * \internal
 493  * \brief Extract the ID attribute from an XML element
 494  *
 495  * \param[in] xpath String to search
 496  * \param[in] node  Node to get the ID for
 497  *
 498  * \return ID attribute of \p node in xpath string \p xpath
 499  */
 500 char *
 501 pcmk__xpath_node_id(const char *xpath, const char *node);
 502 
 503 /*!
 504  * \internal
 505  * \brief Print an informational message if an xpath query returned multiple
 506  *        items with the same ID.
 507  *
 508  * \param[in,out] out       The output object
 509  * \param[in]     search    The xpath search result, most typically the result of
 510  *                          calling cib->cmds->query().
 511  * \param[in]     name      The name searched for
 512  */
 513 void
 514 pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search,
 515                                  const char *name);
 516 
 517 /* internal XML-related utilities */
 518 
 519 enum xml_private_flags {
 520      pcmk__xf_none        = 0x0000,
 521      pcmk__xf_dirty       = 0x0001,
 522      pcmk__xf_deleted     = 0x0002,
 523      pcmk__xf_created     = 0x0004,
 524      pcmk__xf_modified    = 0x0008,
 525 
 526      pcmk__xf_tracking    = 0x0010,
 527      pcmk__xf_processed   = 0x0020,
 528      pcmk__xf_skip        = 0x0040,
 529      pcmk__xf_moved       = 0x0080,
 530 
 531      pcmk__xf_acl_enabled = 0x0100,
 532      pcmk__xf_acl_read    = 0x0200,
 533      pcmk__xf_acl_write   = 0x0400,
 534      pcmk__xf_acl_deny    = 0x0800,
 535 
 536      pcmk__xf_acl_create  = 0x1000,
 537      pcmk__xf_acl_denied  = 0x2000,
 538      pcmk__xf_lazy        = 0x4000,
 539 };
 540 
 541 void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag);
 542 
 543 /*!
 544  * \internal
 545  * \brief Iterate over child elements of \p xml
 546  *
 547  * This function iterates over the children of \p xml, performing the
 548  * callback function \p handler on each node.  If the callback returns
 549  * a value other than pcmk_rc_ok, the iteration stops and the value is
 550  * returned.  It is therefore possible that not all children will be
 551  * visited.
 552  *
 553  * \param[in,out] xml                 The starting XML node.  Can be NULL.
 554  * \param[in]     child_element_name  The name that the node must match in order
 555  *                                    for \p handler to be run.  If NULL, all
 556  *                                    child elements will match.
 557  * \param[in]     handler             The callback function.
 558  * \param[in,out] userdata            User data to pass to the callback function.
 559  *                                    Can be NULL.
 560  *
 561  * \return Standard Pacemaker return code
 562  */
 563 int
 564 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
 565                        int (*handler)(xmlNode *xml, void *userdata),
 566                        void *userdata);
 567 
 568 bool pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
 569                             void *user_data);
 570 
 571 static inline const char *
 572 pcmk__xml_attr_value(const xmlAttr *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
 573 {
 574     return ((attr == NULL) || (attr->children == NULL))? NULL
 575            : (const char *) attr->children->content;
 576 }
 577 
 578 // @COMPAT Remove when v1 patchsets are removed
 579 xmlNode *pcmk__diff_v1_xml_object(xmlNode *left, xmlNode *right, bool suppress);
 580 
 581 // @COMPAT Drop when PCMK__XE_PROMOTABLE_LEGACY is removed
 582 static inline const char *
 583 pcmk__map_element_name(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 584 {
 585     if (xml == NULL) {
 586         return NULL;
 587     } else if (pcmk__xe_is(xml, PCMK__XE_PROMOTABLE_LEGACY)) {
 588         return PCMK_XE_CLONE;
 589     } else {
 590         return (const char *) xml->name;
 591     }
 592 }
 593 
 594 #endif // PCMK__XML_INTERNAL__H

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