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__xml_first_child
  2. pcmk__xml_next
  3. pcmk__xml_attr_value

   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__CRM_COMMON_XML_INTERNAL__H
  11 #define PCMK__CRM_COMMON_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 
  21 #include <crm/crm.h>  /* transitively imports qblog.h */
  22 #include <crm/common/output_internal.h>
  23 #include <crm/common/xml_names.h>             // PCMK_XA_ID, PCMK_XE_CLONE
  24 
  25 // This file is a wrapper for other xml_*_internal.h headers
  26 #include <crm/common/xml_comment_internal.h>
  27 #include <crm/common/xml_element_internal.h>
  28 #include <crm/common/xml_idref_internal.h>
  29 #include <crm/common/xml_io_internal.h>
  30 #include <crm/common/xml_names_internal.h>
  31 
  32 #include <libxml/relaxng.h>
  33 
  34 #ifdef __cplusplus
  35 extern "C" {
  36 #endif
  37 
  38 /*!
  39  * \brief Base for directing lib{xml2,xslt} log into standard libqb backend
  40  *
  41  * This macro implements the core of what can be needed for directing
  42  * libxml2 or libxslt error messaging into standard, preconfigured
  43  * libqb-backed log stream.
  44  *
  45  * It's a bit unfortunate that libxml2 (and more sparsely, also libxslt)
  46  * emits a single message by chunks (location is emitted separatedly from
  47  * the message itself), so we have to take the effort to combine these
  48  * chunks back to single message.  Whether to do this or not is driven
  49  * with \p dechunk toggle.
  50  *
  51  * The form of a macro was chosen for implicit deriving of __FILE__, etc.
  52  * and also because static dechunking buffer should be differentiated per
  53  * library (here we assume different functions referring to this macro
  54  * will not ever be using both at once), preferably also per-library
  55  * context of use to avoid clashes altogether.
  56  *
  57  * Note that we cannot use qb_logt, because callsite data have to be known
  58  * at the moment of compilation, which it is not always the case -- xml_log
  59  * (and unfortunately there's no clear explanation of the fail to compile).
  60  *
  61  * Also note that there's no explicit guard against said libraries producing
  62  * never-newline-terminated chunks (which would just keep consuming memory),
  63  * as it's quite improbable.  Termination of the program in between the
  64  * same-message chunks will raise a flag with valgrind and the likes, though.
  65  *
  66  * And lastly, regarding how dechunking combines with other non-message
  67  * parameters -- for \p priority, most important running specification
  68  * wins (possibly elevated to LOG_ERR in case of nonconformance with the
  69  * newline-termination "protocol"), \p dechunk is expected to always be
  70  * on once it was at the start, and the rest (\p postemit and \p prefix)
  71  * are picked directly from the last chunk entry finalizing the message
  72  * (also reasonable to always have it the same with all related entries).
  73  *
  74  * \param[in] priority Syslog priority for the message to be logged
  75  * \param[in] dechunk  Whether to dechunk new-line terminated message
  76  * \param[in] postemit Code to be executed once message is sent out
  77  * \param[in] prefix   How to prefix the message or NULL for raw passing
  78  * \param[in] fmt      Format string as with printf-like functions
  79  * \param[in] ap       Variable argument list to supplement \p fmt format string
  80  */
  81 #define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)        \
  82 do {                                                                            \
  83     if (!(dechunk) && (prefix) == NULL) {  /* quick pass */                     \
  84         qb_log_from_external_source_va(__func__, __FILE__, (fmt),               \
  85                                        (priority), __LINE__, 0, (ap));          \
  86         (void) (postemit);                                                      \
  87     } else {                                                                    \
  88         int CXLB_len = 0;                                                       \
  89         char *CXLB_buf = NULL;                                                  \
  90         static int CXLB_buffer_len = 0;                                         \
  91         static char *CXLB_buffer = NULL;                                        \
  92         static uint8_t CXLB_priority = 0;                                       \
  93                                                                                 \
  94         CXLB_len = vasprintf(&CXLB_buf, (fmt), (ap));                           \
  95                                                                                 \
  96         if (CXLB_len <= 0 || CXLB_buf[CXLB_len - 1] == '\n' || !(dechunk)) {    \
  97             if (CXLB_len < 0) {                                                 \
  98                 CXLB_buf = (char *) "LOG CORRUPTION HAZARD"; /*we don't modify*/\
  99                 CXLB_priority = QB_MIN(CXLB_priority, LOG_ERR);                 \
 100             } else if (CXLB_len > 0 /* && (dechunk) */                          \
 101                        && CXLB_buf[CXLB_len - 1] == '\n') {                     \
 102                 CXLB_buf[CXLB_len - 1] = '\0';                                  \
 103             }                                                                   \
 104             if (CXLB_buffer) {                                                  \
 105                 qb_log_from_external_source(__func__, __FILE__, "%s%s%s",       \
 106                                             CXLB_priority, __LINE__, 0,         \
 107                                             (prefix) != NULL ? (prefix) : "",   \
 108                                             CXLB_buffer, CXLB_buf);             \
 109                 free(CXLB_buffer);                                              \
 110             } else {                                                            \
 111                 qb_log_from_external_source(__func__, __FILE__, "%s%s",         \
 112                                             (priority), __LINE__, 0,            \
 113                                             (prefix) != NULL ? (prefix) : "",   \
 114                                             CXLB_buf);                          \
 115             }                                                                   \
 116             if (CXLB_len < 0) {                                                 \
 117                 CXLB_buf = NULL;  /* restore temporary override */              \
 118             }                                                                   \
 119             CXLB_buffer = NULL;                                                 \
 120             CXLB_buffer_len = 0;                                                \
 121             (void) (postemit);                                                  \
 122                                                                                 \
 123         } else if (CXLB_buffer == NULL) {                                       \
 124             CXLB_buffer_len = CXLB_len;                                         \
 125             CXLB_buffer = CXLB_buf;                                             \
 126             CXLB_buf = NULL;                                                    \
 127             CXLB_priority = (priority);  /* remember as a running severest */   \
 128                                                                                 \
 129         } else {                                                                \
 130             CXLB_buffer = realloc(CXLB_buffer, 1 + CXLB_buffer_len + CXLB_len); \
 131             memcpy(CXLB_buffer + CXLB_buffer_len, CXLB_buf, CXLB_len);          \
 132             CXLB_buffer_len += CXLB_len;                                        \
 133             CXLB_buffer[CXLB_buffer_len] = '\0';                                \
 134             CXLB_priority = QB_MIN(CXLB_priority, (priority));  /* severest? */ \
 135         }                                                                       \
 136         free(CXLB_buf);                                                         \
 137     }                                                                           \
 138 } while (0)
 139 
 140 /*
 141  * \enum pcmk__xml_fmt_options
 142  * \brief Bit flags to control format in XML logs and dumps
 143  */
 144 enum pcmk__xml_fmt_options {
 145     //! Exclude certain XML attributes (for calculating digests)
 146     pcmk__xml_fmt_filtered   = (1 << 0),
 147 
 148     //! Include indentation and newlines
 149     pcmk__xml_fmt_pretty     = (1 << 1),
 150 
 151     //! Include the opening tag of an XML element, and include XML comments
 152     pcmk__xml_fmt_open       = (1 << 3),
 153 
 154     //! Include the children of an XML element
 155     pcmk__xml_fmt_children   = (1 << 4),
 156 
 157     //! Include the closing tag of an XML element
 158     pcmk__xml_fmt_close      = (1 << 5),
 159 
 160     // @COMPAT Can we start including text nodes unconditionally?
 161     //! Include XML text nodes
 162     pcmk__xml_fmt_text       = (1 << 6),
 163 };
 164 
 165 void pcmk__xml_init(void);
 166 void pcmk__xml_cleanup(void);
 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 enum pcmk__xml_artefact_ns {
 198     pcmk__xml_artefact_ns_legacy_rng = 1,
 199     pcmk__xml_artefact_ns_legacy_xslt,
 200     pcmk__xml_artefact_ns_base_rng,
 201     pcmk__xml_artefact_ns_base_xslt,
 202 };
 203 
 204 void pcmk__strip_xml_text(xmlNode *xml);
 205 
 206 GString *pcmk__element_xpath(const xmlNode *xml);
 207 
 208 /*!
 209  * \internal
 210  * \enum pcmk__xml_escape_type
 211  * \brief Indicators of which XML characters to escape
 212  *
 213  * XML allows the escaping of special characters by replacing them with entity
 214  * references (for example, <tt>"&quot;"</tt>) or character references (for
 215  * example, <tt>"&#13;"</tt>).
 216  *
 217  * The special characters <tt>'&'</tt> (except as the beginning of an entity
 218  * reference) and <tt>'<'</tt> are not allowed in their literal forms in XML
 219  * character data. Character data is non-markup text (for example, the content
 220  * of a text node). <tt>'>'</tt> is allowed under most circumstances; we escape
 221  * it for safety and symmetry.
 222  *
 223  * For more details, see the "Character Data and Markup" section of the XML
 224  * spec, currently section 2.4:
 225  * https://www.w3.org/TR/xml/#dt-markup
 226  *
 227  * Attribute values are handled specially.
 228  * * If an attribute value is delimited by single quotes, then single quotes
 229  *   must be escaped within the value.
 230  * * Similarly, if an attribute value is delimited by double quotes, then double
 231  *   quotes must be escaped within the value.
 232  * * A conformant XML processor replaces a literal whitespace character (tab,
 233  *   newline, carriage return, space) in an attribute value with a space
 234  *   (\c '#x20') character. However, a reference to a whitespace character (for
 235  *   example, \c "&#x0A;" for \c '\n') does not get replaced.
 236  *   * For more details, see the "Attribute-Value Normalization" section of the
 237  *     XML spec, currently section 3.3.3. Note that the default attribute type
 238  *     is CDATA; we don't deal with NMTOKENS, etc.:
 239  *     https://www.w3.org/TR/xml/#AVNormalize
 240  *
 241  * Pacemaker always delimits attribute values with double quotes, so there's no
 242  * need to escape single quotes.
 243  *
 244  * Newlines and tabs should be escaped in attribute values when XML is
 245  * serialized to text, so that future parsing preserves them rather than
 246  * normalizing them to spaces.
 247  *
 248  * We always escape carriage returns, so that they're not converted to spaces
 249  * during attribute-value normalization and because displaying them as literals
 250  * is messy.
 251  */
 252 enum pcmk__xml_escape_type {
 253     /*!
 254      * For text nodes.
 255      * * Escape \c '<', \c '>', and \c '&' using entity references.
 256      * * Do not escape \c '\n' and \c '\t'.
 257      * * Escape other non-printing characters using character references.
 258      */
 259     pcmk__xml_escape_text,
 260 
 261     /*!
 262      * For attribute values.
 263      * * Escape \c '<', \c '>', \c '&', and \c '"' using entity references.
 264      * * Escape \c '\n', \c '\t', and other non-printing characters using
 265      *   character references.
 266      */
 267     pcmk__xml_escape_attr,
 268 
 269     /* @COMPAT Drop escaping of at least '\n' and '\t' for
 270      * pcmk__xml_escape_attr_pretty when openstack-info, openstack-floating-ip,
 271      * and openstack-virtual-ip resource agents no longer depend on it.
 272      *
 273      * At time of writing, openstack-info may set a multiline value for the
 274      * openstack_ports node attribute. The other two agents query the value and
 275      * require it to be on one line with no spaces.
 276      */
 277     /*!
 278      * For attribute values displayed in text output delimited by double quotes.
 279      * * Escape \c '\n' as \c "\\n"
 280      * * Escape \c '\r' as \c "\\r"
 281      * * Escape \c '\t' as \c "\\t"
 282      * * Escape \c '"' as \c "\\""
 283      */
 284     pcmk__xml_escape_attr_pretty,
 285 };
 286 
 287 bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type);
 288 char *pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type);
 289 
 290 /*!
 291  * \internal
 292  * \brief Get the root directory to scan XML artefacts of given kind for
 293  *
 294  * \param[in] ns governs the hierarchy nesting against the inherent root dir
 295  *
 296  * \return root directory to scan XML artefacts of given kind for
 297  */
 298 char *
 299 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns);
 300 
 301 /*!
 302  * \internal
 303  * \brief Get the fully unwrapped path to particular XML artifact (RNG/XSLT)
 304  *
 305  * \param[in] ns       denotes path forming details (parent dir, suffix)
 306  * \param[in] filespec symbolic file specification to be combined with
 307  *                     #artefact_ns to form the final path
 308  * \return unwrapped path to particular XML artifact (RNG/XSLT)
 309  */
 310 char *pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns,
 311                               const char *filespec);
 312 
 313 /*!
 314  * \internal
 315  * \brief Return first non-text child node of an XML node
 316  *
 317  * \param[in] parent  XML node to check
 318  *
 319  * \return First non-text child node of \p parent (or NULL if none)
 320  */
 321 static inline xmlNode *
 322 pcmk__xml_first_child(const xmlNode *parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 323 {
 324     xmlNode *child = (parent? parent->children : NULL);
 325 
 326     while (child && (child->type == XML_TEXT_NODE)) {
 327         child = child->next;
 328     }
 329     return child;
 330 }
 331 
 332 /*!
 333  * \internal
 334  * \brief Return next non-text sibling node of an XML node
 335  *
 336  * \param[in] child  XML node to check
 337  *
 338  * \return Next non-text sibling of \p child (or NULL if none)
 339  */
 340 static inline xmlNode *
 341 pcmk__xml_next(const xmlNode *child)
     /* [previous][next][first][last][top][bottom][index][help] */
 342 {
 343     xmlNode *next = (child? child->next : NULL);
 344 
 345     while (next && (next->type == XML_TEXT_NODE)) {
 346         next = next->next;
 347     }
 348     return next;
 349 }
 350 
 351 void pcmk__xml_free(xmlNode *xml);
 352 void pcmk__xml_free_doc(xmlDoc *doc);
 353 xmlNode *pcmk__xml_copy(xmlNode *parent, xmlNode *src);
 354 
 355 /*!
 356  * \internal
 357  * \enum pcmk__xa_flags
 358  * \brief Flags for operations affecting XML attributes
 359  */
 360 enum pcmk__xa_flags {
 361     //! Flag has no effect
 362     pcmk__xaf_none          = 0U,
 363 
 364     //! Don't overwrite existing values
 365     pcmk__xaf_no_overwrite  = (1U << 0),
 366 
 367     /*!
 368      * Treat values as score updates where possible (see
 369      * \c pcmk__xe_set_score())
 370      */
 371     pcmk__xaf_score_update  = (1U << 1),
 372 };
 373 
 374 void pcmk__xml_sanitize_id(char *id);
 375 
 376 /*!
 377  * \internal
 378  * \brief Extract the ID attribute from an XML element
 379  *
 380  * \param[in] xpath String to search
 381  * \param[in] node  Node to get the ID for
 382  *
 383  * \return ID attribute of \p node in xpath string \p xpath
 384  */
 385 char *
 386 pcmk__xpath_node_id(const char *xpath, const char *node);
 387 
 388 /*!
 389  * \internal
 390  * \brief Print an informational message if an xpath query returned multiple
 391  *        items with the same ID.
 392  *
 393  * \param[in,out] out       The output object
 394  * \param[in]     search    The xpath search result, most typically the result of
 395  *                          calling cib->cmds->query().
 396  * \param[in]     name      The name searched for
 397  */
 398 void
 399 pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search,
 400                                  const char *name);
 401 
 402 /* internal XML-related utilities */
 403 
 404 enum xml_private_flags {
 405      pcmk__xf_none        = 0x0000,
 406      pcmk__xf_dirty       = 0x0001,
 407      pcmk__xf_deleted     = 0x0002,
 408      pcmk__xf_created     = 0x0004,
 409      pcmk__xf_modified    = 0x0008,
 410 
 411      pcmk__xf_tracking    = 0x0010,
 412      pcmk__xf_processed   = 0x0020,
 413      pcmk__xf_skip        = 0x0040,
 414      pcmk__xf_moved       = 0x0080,
 415 
 416      pcmk__xf_acl_enabled = 0x0100,
 417      pcmk__xf_acl_read    = 0x0200,
 418      pcmk__xf_acl_write   = 0x0400,
 419      pcmk__xf_acl_deny    = 0x0800,
 420 
 421      pcmk__xf_acl_create  = 0x1000,
 422      pcmk__xf_acl_denied  = 0x2000,
 423      pcmk__xf_lazy        = 0x4000,
 424 };
 425 
 426 void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag);
 427 
 428 bool pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
 429                             void *user_data);
 430 
 431 static inline const char *
 432 pcmk__xml_attr_value(const xmlAttr *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
 433 {
 434     return ((attr == NULL) || (attr->children == NULL))? NULL
 435            : (const char *) attr->children->content;
 436 }
 437 
 438 /*!
 439  * \internal
 440  * \brief Check whether a given CIB element was modified in a CIB patchset
 441  *
 442  * \param[in] patchset  CIB XML patchset
 443  * \param[in] element   XML tag of CIB element to check (\c NULL is equivalent
 444  *                      to \c PCMK_XE_CIB). Supported values include any CIB
 445  *                      element supported by \c pcmk__cib_abs_xpath_for().
 446  *
 447  * \return \c true if \p element was modified, or \c false otherwise
 448  */
 449 bool pcmk__cib_element_in_patchset(const xmlNode *patchset,
 450                                    const char *element);
 451 
 452 #ifdef __cplusplus
 453 }
 454 #endif
 455 
 456 #endif // PCMK__CRM_COMMON_XML_INTERNAL__H

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