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_is
  2. pcmk__xml_first_child
  3. pcmk__xml_next
  4. pcmk__xe_first_child
  5. pcmk__xe_next
  6. pcmk__xe_set_props
  7. pcmk__xml_attr_value

   1 /*
   2  * Copyright 2017-2023 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 <stdio.h>
  19 #  include <string.h>
  20 
  21 #  include <crm/crm.h>  /* transitively imports qblog.h */
  22 #  include <crm/common/output_internal.h>
  23 
  24 #  include <libxml/relaxng.h>
  25 
  26 /*!
  27  * \brief Base for directing lib{xml2,xslt} log into standard libqb backend
  28  *
  29  * This macro implements the core of what can be needed for directing
  30  * libxml2 or libxslt error messaging into standard, preconfigured
  31  * libqb-backed log stream.
  32  *
  33  * It's a bit unfortunate that libxml2 (and more sparsely, also libxslt)
  34  * emits a single message by chunks (location is emitted separatedly from
  35  * the message itself), so we have to take the effort to combine these
  36  * chunks back to single message.  Whether to do this or not is driven
  37  * with \p dechunk toggle.
  38  *
  39  * The form of a macro was chosen for implicit deriving of __FILE__, etc.
  40  * and also because static dechunking buffer should be differentiated per
  41  * library (here we assume different functions referring to this macro
  42  * will not ever be using both at once), preferably also per-library
  43  * context of use to avoid clashes altogether.
  44  *
  45  * Note that we cannot use qb_logt, because callsite data have to be known
  46  * at the moment of compilation, which it is not always the case -- xml_log
  47  * (and unfortunately there's no clear explanation of the fail to compile).
  48  *
  49  * Also note that there's no explicit guard against said libraries producing
  50  * never-newline-terminated chunks (which would just keep consuming memory),
  51  * as it's quite improbable.  Termination of the program in between the
  52  * same-message chunks will raise a flag with valgrind and the likes, though.
  53  *
  54  * And lastly, regarding how dechunking combines with other non-message
  55  * parameters -- for \p priority, most important running specification
  56  * wins (possibly elevated to LOG_ERR in case of nonconformance with the
  57  * newline-termination "protocol"), \p dechunk is expected to always be
  58  * on once it was at the start, and the rest (\p postemit and \p prefix)
  59  * are picked directly from the last chunk entry finalizing the message
  60  * (also reasonable to always have it the same with all related entries).
  61  *
  62  * \param[in] priority Syslog priority for the message to be logged
  63  * \param[in] dechunk  Whether to dechunk new-line terminated message
  64  * \param[in] postemit Code to be executed once message is sent out
  65  * \param[in] prefix   How to prefix the message or NULL for raw passing
  66  * \param[in] fmt      Format string as with printf-like functions
  67  * \param[in] ap       Variable argument list to supplement \p fmt format string
  68  */
  69 #define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)        \
  70 do {                                                                            \
  71     if (!(dechunk) && (prefix) == NULL) {  /* quick pass */                     \
  72         qb_log_from_external_source_va(__func__, __FILE__, (fmt),               \
  73                                        (priority), __LINE__, 0, (ap));          \
  74         (void) (postemit);                                                      \
  75     } else {                                                                    \
  76         int CXLB_len = 0;                                                       \
  77         char *CXLB_buf = NULL;                                                  \
  78         static int CXLB_buffer_len = 0;                                         \
  79         static char *CXLB_buffer = NULL;                                        \
  80         static uint8_t CXLB_priority = 0;                                       \
  81                                                                                 \
  82         CXLB_len = vasprintf(&CXLB_buf, (fmt), (ap));                           \
  83                                                                                 \
  84         if (CXLB_len <= 0 || CXLB_buf[CXLB_len - 1] == '\n' || !(dechunk)) {    \
  85             if (CXLB_len < 0) {                                                 \
  86                 CXLB_buf = (char *) "LOG CORRUPTION HAZARD"; /*we don't modify*/\
  87                 CXLB_priority = QB_MIN(CXLB_priority, LOG_ERR);                 \
  88             } else if (CXLB_len > 0 /* && (dechunk) */                          \
  89                        && CXLB_buf[CXLB_len - 1] == '\n') {                     \
  90                 CXLB_buf[CXLB_len - 1] = '\0';                                  \
  91             }                                                                   \
  92             if (CXLB_buffer) {                                                  \
  93                 qb_log_from_external_source(__func__, __FILE__, "%s%s%s",       \
  94                                             CXLB_priority, __LINE__, 0,         \
  95                                             (prefix) != NULL ? (prefix) : "",   \
  96                                             CXLB_buffer, CXLB_buf);             \
  97                 free(CXLB_buffer);                                              \
  98             } else {                                                            \
  99                 qb_log_from_external_source(__func__, __FILE__, "%s%s",         \
 100                                             (priority), __LINE__, 0,            \
 101                                             (prefix) != NULL ? (prefix) : "",   \
 102                                             CXLB_buf);                          \
 103             }                                                                   \
 104             if (CXLB_len < 0) {                                                 \
 105                 CXLB_buf = NULL;  /* restore temporary override */              \
 106             }                                                                   \
 107             CXLB_buffer = NULL;                                                 \
 108             CXLB_buffer_len = 0;                                                \
 109             (void) (postemit);                                                  \
 110                                                                                 \
 111         } else if (CXLB_buffer == NULL) {                                       \
 112             CXLB_buffer_len = CXLB_len;                                         \
 113             CXLB_buffer = CXLB_buf;                                             \
 114             CXLB_buf = NULL;                                                    \
 115             CXLB_priority = (priority);  /* remember as a running severest */   \
 116                                                                                 \
 117         } else {                                                                \
 118             CXLB_buffer = realloc(CXLB_buffer, 1 + CXLB_buffer_len + CXLB_len); \
 119             memcpy(CXLB_buffer + CXLB_buffer_len, CXLB_buf, CXLB_len);          \
 120             CXLB_buffer_len += CXLB_len;                                        \
 121             CXLB_buffer[CXLB_buffer_len] = '\0';                                \
 122             CXLB_priority = QB_MIN(CXLB_priority, (priority));  /* severest? */ \
 123         }                                                                       \
 124         free(CXLB_buf);                                                         \
 125     }                                                                           \
 126 } while (0)
 127 
 128 /*
 129  * \enum pcmk__xml_fmt_options
 130  * \brief Bit flags to control format in XML logs and dumps
 131  */
 132 enum pcmk__xml_fmt_options {
 133     //! Exclude certain XML attributes (for calculating digests)
 134     pcmk__xml_fmt_filtered   = (1 << 0),
 135 
 136     //! Include indentation and newlines
 137     pcmk__xml_fmt_pretty     = (1 << 1),
 138 
 139     //! Include the opening tag of an XML element, and include XML comments
 140     pcmk__xml_fmt_open       = (1 << 3),
 141 
 142     //! Include the children of an XML element
 143     pcmk__xml_fmt_children   = (1 << 4),
 144 
 145     //! Include the closing tag of an XML element
 146     pcmk__xml_fmt_close      = (1 << 5),
 147 
 148     //! Include XML text nodes
 149     pcmk__xml_fmt_text       = (1 << 6),
 150 
 151     // @COMPAT Remove when v1 patchsets are removed
 152     //! Log a created XML subtree
 153     pcmk__xml_fmt_diff_plus  = (1 << 7),
 154 
 155     // @COMPAT Remove when v1 patchsets are removed
 156     //! Log a removed XML subtree
 157     pcmk__xml_fmt_diff_minus = (1 << 8),
 158 
 159     // @COMPAT Remove when v1 patchsets are removed
 160     //! Log a minimal version of an XML diff (only showing the changes)
 161     pcmk__xml_fmt_diff_short = (1 << 9),
 162 };
 163 
 164 int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data,
 165                    int depth, uint32_t options);
 166 int pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml);
 167 
 168 /* XML search strings for guest, remote and pacemaker_remote nodes */
 169 
 170 /* search string to find CIB resources entries for cluster nodes */
 171 #define PCMK__XP_MEMBER_NODE_CONFIG \
 172     "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_NODES \
 173     "/" XML_CIB_TAG_NODE "[not(@type) or @type='member']"
 174 
 175 /* search string to find CIB resources entries for guest nodes */
 176 #define PCMK__XP_GUEST_NODE_CONFIG \
 177     "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE \
 178     "//" XML_TAG_META_SETS "//" XML_CIB_TAG_NVPAIR \
 179     "[@name='" XML_RSC_ATTR_REMOTE_NODE "']"
 180 
 181 /* search string to find CIB resources entries for remote nodes */
 182 #define PCMK__XP_REMOTE_NODE_CONFIG \
 183     "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE \
 184     "[@type='remote'][@provider='pacemaker']"
 185 
 186 /* search string to find CIB node status entries for pacemaker_remote nodes */
 187 #define PCMK__XP_REMOTE_NODE_STATUS \
 188     "//" XML_TAG_CIB "//" XML_CIB_TAG_STATUS "//" XML_CIB_TAG_STATE \
 189     "[@" XML_NODE_IS_REMOTE "='true']"
 190 /*!
 191  * \internal
 192  * \brief Serialize XML (using libxml) into provided descriptor
 193  *
 194  * \param[in] fd  File descriptor to (piece-wise) write to
 195  * \param[in] cur XML subtree to proceed
 196  * 
 197  * \return a standard Pacemaker return code
 198  */
 199 int pcmk__xml2fd(int fd, xmlNode *cur);
 200 
 201 enum pcmk__xml_artefact_ns {
 202     pcmk__xml_artefact_ns_legacy_rng = 1,
 203     pcmk__xml_artefact_ns_legacy_xslt,
 204     pcmk__xml_artefact_ns_base_rng,
 205     pcmk__xml_artefact_ns_base_xslt,
 206 };
 207 
 208 void pcmk__strip_xml_text(xmlNode *xml);
 209 const char *pcmk__xe_add_last_written(xmlNode *xe);
 210 
 211 xmlNode *pcmk__xe_match(const xmlNode *parent, const char *node_name,
 212                         const char *attr_n, const char *attr_v);
 213 
 214 void pcmk__xe_remove_matching_attrs(xmlNode *element,
 215                                     bool (*match)(xmlAttrPtr, void *),
 216                                     void *user_data);
 217 
 218 GString *pcmk__element_xpath(const xmlNode *xml);
 219 
 220 /*!
 221  * \internal
 222  * \brief Get the root directory to scan XML artefacts of given kind for
 223  *
 224  * \param[in] ns governs the hierarchy nesting against the inherent root dir
 225  *
 226  * \return root directory to scan XML artefacts of given kind for
 227  */
 228 char *
 229 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns);
 230 
 231 /*!
 232  * \internal
 233  * \brief Get the fully unwrapped path to particular XML artifact (RNG/XSLT)
 234  *
 235  * \param[in] ns       denotes path forming details (parent dir, suffix)
 236  * \param[in] filespec symbolic file specification to be combined with
 237  *                     #artefact_ns to form the final path
 238  * \return unwrapped path to particular XML artifact (RNG/XSLT)
 239  */
 240 char *pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns,
 241                               const char *filespec);
 242 
 243 /*!
 244  * \internal
 245  * \brief Check whether an XML element is of a particular type
 246  *
 247  * \param[in] xml   XML element to compare
 248  * \param[in] name  XML element name to compare
 249  *
 250  * \return \c true if \p xml is of type \p name, otherwise \c false
 251  */
 252 static inline bool
 253 pcmk__xe_is(const xmlNode *xml, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 254 {
 255     return (xml != NULL) && (xml->name != NULL) && (name != NULL)
 256            && (strcmp((const char *) xml->name, name) == 0);
 257 }
 258 
 259 /*!
 260  * \internal
 261  * \brief Return first non-text child node of an XML node
 262  *
 263  * \param[in] parent  XML node to check
 264  *
 265  * \return First non-text child node of \p parent (or NULL if none)
 266  */
 267 static inline xmlNode *
 268 pcmk__xml_first_child(const xmlNode *parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270     xmlNode *child = (parent? parent->children : NULL);
 271 
 272     while (child && (child->type == XML_TEXT_NODE)) {
 273         child = child->next;
 274     }
 275     return child;
 276 }
 277 
 278 /*!
 279  * \internal
 280  * \brief Return next non-text sibling node of an XML node
 281  *
 282  * \param[in] child  XML node to check
 283  *
 284  * \return Next non-text sibling of \p child (or NULL if none)
 285  */
 286 static inline xmlNode *
 287 pcmk__xml_next(const xmlNode *child)
     /* [previous][next][first][last][top][bottom][index][help] */
 288 {
 289     xmlNode *next = (child? child->next : NULL);
 290 
 291     while (next && (next->type == XML_TEXT_NODE)) {
 292         next = next->next;
 293     }
 294     return next;
 295 }
 296 
 297 /*!
 298  * \internal
 299  * \brief Return first non-text child element of an XML node
 300  *
 301  * \param[in] parent  XML node to check
 302  *
 303  * \return First child element of \p parent (or NULL if none)
 304  */
 305 static inline xmlNode *
 306 pcmk__xe_first_child(const xmlNode *parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 307 {
 308     xmlNode *child = (parent? parent->children : NULL);
 309 
 310     while (child && (child->type != XML_ELEMENT_NODE)) {
 311         child = child->next;
 312     }
 313     return child;
 314 }
 315 
 316 /*!
 317  * \internal
 318  * \brief Return next non-text sibling element of an XML element
 319  *
 320  * \param[in] child  XML element to check
 321  *
 322  * \return Next sibling element of \p child (or NULL if none)
 323  */
 324 static inline xmlNode *
 325 pcmk__xe_next(const xmlNode *child)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327     xmlNode *next = child? child->next : NULL;
 328 
 329     while (next && (next->type != XML_ELEMENT_NODE)) {
 330         next = next->next;
 331     }
 332     return next;
 333 }
 334 
 335 /*!
 336  * \internal
 337  * \brief Like pcmk__xe_set_props, but takes a va_list instead of
 338  *        arguments directly.
 339  *
 340  * \param[in,out] node   XML to add attributes to
 341  * \param[in]     pairs  NULL-terminated list of name/value pairs to add
 342  */
 343 void
 344 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs);
 345 
 346 /*!
 347  * \internal
 348  * \brief Add a NULL-terminated list of name/value pairs to the given
 349  *        XML node as properties.
 350  *
 351  * \param[in,out] node XML node to add properties to
 352  * \param[in]     ...  NULL-terminated list of name/value pairs
 353  *
 354  * \note A NULL name terminates the arguments; a NULL value will be skipped.
 355  */
 356 void
 357 pcmk__xe_set_props(xmlNodePtr node, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 358 G_GNUC_NULL_TERMINATED;
 359 
 360 /*!
 361  * \internal
 362  * \brief Get first attribute of an XML element
 363  *
 364  * \param[in] xe  XML element to check
 365  *
 366  * \return First attribute of \p xe (or NULL if \p xe is NULL or has none)
 367  */
 368 static inline xmlAttr *
 369 pcmk__xe_first_attr(const xmlNode *xe)
 370 {
 371     return (xe == NULL)? NULL : xe->properties;
 372 }
 373 
 374 /*!
 375  * \internal
 376  * \brief Extract the ID attribute from an XML element
 377  *
 378  * \param[in] xpath String to search
 379  * \param[in] node  Node to get the ID for
 380  *
 381  * \return ID attribute of \p node in xpath string \p xpath
 382  */
 383 char *
 384 pcmk__xpath_node_id(const char *xpath, const char *node);
 385 
 386 /* internal XML-related utilities */
 387 
 388 enum xml_private_flags {
 389      pcmk__xf_none        = 0x0000,
 390      pcmk__xf_dirty       = 0x0001,
 391      pcmk__xf_deleted     = 0x0002,
 392      pcmk__xf_created     = 0x0004,
 393      pcmk__xf_modified    = 0x0008,
 394 
 395      pcmk__xf_tracking    = 0x0010,
 396      pcmk__xf_processed   = 0x0020,
 397      pcmk__xf_skip        = 0x0040,
 398      pcmk__xf_moved       = 0x0080,
 399 
 400      pcmk__xf_acl_enabled = 0x0100,
 401      pcmk__xf_acl_read    = 0x0200,
 402      pcmk__xf_acl_write   = 0x0400,
 403      pcmk__xf_acl_deny    = 0x0800,
 404 
 405      pcmk__xf_acl_create  = 0x1000,
 406      pcmk__xf_acl_denied  = 0x2000,
 407      pcmk__xf_lazy        = 0x4000,
 408 };
 409 
 410 void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag);
 411 
 412 /*!
 413  * \internal
 414  * \brief Iterate over child elements of \p xml
 415  *
 416  * This function iterates over the children of \p xml, performing the
 417  * callback function \p handler on each node.  If the callback returns
 418  * a value other than pcmk_rc_ok, the iteration stops and the value is
 419  * returned.  It is therefore possible that not all children will be
 420  * visited.
 421  *
 422  * \param[in,out] xml                 The starting XML node.  Can be NULL.
 423  * \param[in]     child_element_name  The name that the node must match in order
 424  *                                    for \p handler to be run.  If NULL, all
 425  *                                    child elements will match.
 426  * \param[in]     handler             The callback function.
 427  * \param[in,out] userdata            User data to pass to the callback function.
 428  *                                    Can be NULL.
 429  *
 430  * \return Standard Pacemaker return code
 431  */
 432 int
 433 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
 434                        int (*handler)(xmlNode *xml, void *userdata),
 435                        void *userdata);
 436 
 437 static inline const char *
 438 pcmk__xml_attr_value(const xmlAttr *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
 439 {
 440     return ((attr == NULL) || (attr->children == NULL))? NULL
 441            : (const char *) attr->children->content;
 442 }
 443 
 444 gboolean pcmk__validate_xml(xmlNode *xml_blob, const char *validation,
 445                             xmlRelaxNGValidityErrorFunc error_handler, 
 446                             void *error_handler_context);
 447 
 448 #endif // PCMK__XML_INTERNAL__H

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