diff --git a/include/crm/common/xml_compat.h b/include/crm/common/xml_compat.h index aa507320f6..921ece7111 100644 --- a/include/crm/common/xml_compat.h +++ b/include/crm/common/xml_compat.h @@ -1,87 +1,84 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__CRM_COMMON_XML_COMPAT__H #define PCMK__CRM_COMMON_XML_COMPAT__H #include // gboolean #include // xmlNode #include // crm_xml_add() #include // PCMK_XE_CLONE #ifdef __cplusplus extern "C" { #endif /** * \file * \brief Deprecated Pacemaker XML API * \ingroup core * \deprecated Do not include this header directly. The XML APIs in this * header, and the header itself, will be removed in a future * release. */ //! \deprecated Do not use (will be removed in a future release) #define XML_PARANOIA_CHECKS 0 //! \deprecated This function will be removed in a future release xmlDoc *getDocPtr(xmlNode *node); //! \deprecated This function will be removed in a future release xmlNode *find_entity(xmlNode *parent, const char *node_name, const char *id); //! \deprecated This function will be removed in a future release char *xml_get_path(const xmlNode *xml); //! \deprecated This function will be removed in a future release void xml_log_changes(uint8_t level, const char *function, const xmlNode *xml); -//! \deprecated This function will be removed in a future release -void xml_log_patchset(uint8_t level, const char *function, const xmlNode *xml); - // NOTE: sbd (as of at least 1.5.2) uses this //! \deprecated Use name member directly static inline const char * crm_element_name(const xmlNode *xml) { return (xml == NULL)? NULL : (const char *) xml->name; } // NOTE: sbd (as of at least 1.5.2) uses this //! \deprecated Do not use Pacemaker for general-purpose XML manipulation xmlNode *copy_xml(xmlNode *src_node); // NOTE: sbd (as of at least 1.5.2) uses this //! \deprecated Do not use gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs); // NOTE: sbd (as of at least 1.5.2) uses this //! \deprecated Call \c crm_log_init() or \c crm_log_cli_init() instead void crm_xml_init(void); //! \deprecated Exit with \c crm_exit() instead void crm_xml_cleanup(void); //! \deprecated Do not use Pacemaker for general-purpose XML manipulation void pcmk_free_xml_subtree(xmlNode *xml); // NOTE: sbd (as of at least 1.5.2) uses this //! \deprecated Do not use Pacemaker for general-purpose XML manipulation void free_xml(xmlNode *child); //! \deprecated Do not use Pacemaker for general-purpose XML manipulation xmlNode *expand_idref(xmlNode *input, xmlNode *top); #ifdef __cplusplus } #endif #endif // PCMK__CRM_COMMON_XML_COMPAT__H diff --git a/lib/common/patchset_display.c b/lib/common/patchset_display.c index 1351c86071..0855c6e829 100644 --- a/lib/common/patchset_display.c +++ b/lib/common/patchset_display.c @@ -1,530 +1,452 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include "crmcommon_private.h" /*! * \internal * \brief Output an XML patchset header * * This function parses a header from an XML patchset (an \p XML_ATTR_DIFF * element and its children). * * All header lines contain three integers separated by dots, of the form * {0}.{1}.{2}: * * \p {0}: \c PCMK_XA_ADMIN_EPOCH * * \p {1}: \c PCMK_XA_EPOCH * * \p {2}: \c PCMK_XA_NUM_UPDATES * * Lines containing \p "---" describe removals and end with the patch format * number. Lines containing \p "+++" describe additions and end with the patch * digest. * * \param[in,out] out Output object * \param[in] patchset XML patchset to output * * \return Standard Pacemaker return code * * \note This function produces output only for text-like formats. */ static int xml_show_patchset_header(pcmk__output_t *out, const xmlNode *patchset) { int rc = pcmk_rc_no_output; int add[] = { 0, 0, 0 }; int del[] = { 0, 0, 0 }; xml_patch_versions(patchset, add, del); if ((add[0] != del[0]) || (add[1] != del[1]) || (add[2] != del[2])) { const char *fmt = crm_element_value(patchset, PCMK_XA_FORMAT); const char *digest = crm_element_value(patchset, PCMK__XA_DIGEST); out->info(out, "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt); rc = out->info(out, "Diff: +++ %d.%d.%d %s", add[0], add[1], add[2], digest); } else if ((add[0] != 0) || (add[1] != 0) || (add[2] != 0)) { rc = out->info(out, "Local-only Change: %d.%d.%d", add[0], add[1], add[2]); } return rc; } /*! * \internal * \brief Output a user-friendly form of XML additions or removals * * \param[in,out] out Output object * \param[in] prefix String to prepend to every line of output * \param[in] data XML node to output * \param[in] depth Current indentation level * \param[in] options Group of \p pcmk__xml_fmt_options flags * * \return Standard Pacemaker return code * * \note This function produces output only for text-like formats. */ static int xml_show_patchset_v1_recursive(pcmk__output_t *out, const char *prefix, const xmlNode *data, int depth, uint32_t options) { if ((data->children == NULL) || (crm_element_value(data, PCMK__XA_CRM_DIFF_MARKER) != NULL)) { // Found a change; clear the pcmk__xml_fmt_diff_short option if set options &= ~pcmk__xml_fmt_diff_short; if (pcmk_is_set(options, pcmk__xml_fmt_diff_plus)) { prefix = PCMK__XML_PREFIX_CREATED; } else { // pcmk_is_set(options, pcmk__xml_fmt_diff_minus) prefix = PCMK__XML_PREFIX_DELETED; } } if (pcmk_is_set(options, pcmk__xml_fmt_diff_short)) { int rc = pcmk_rc_no_output; // Keep looking for the actual change for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL; child = pcmk__xml_next(child)) { int temp_rc = xml_show_patchset_v1_recursive(out, prefix, child, depth + 1, options); rc = pcmk__output_select_rc(rc, temp_rc); } return rc; } return pcmk__xml_show(out, prefix, data, depth, options |pcmk__xml_fmt_open |pcmk__xml_fmt_children |pcmk__xml_fmt_close); } /*! * \internal * \brief Output a user-friendly form of an XML patchset (format 1) * * This function parses an XML patchset (an \p XML_ATTR_DIFF element and its * children) into a user-friendly combined diff output. * * \param[in,out] out Output object * \param[in] patchset XML patchset to output * \param[in] options Group of \p pcmk__xml_fmt_options flags * * \return Standard Pacemaker return code * * \note This function produces output only for text-like formats. */ static int xml_show_patchset_v1(pcmk__output_t *out, const xmlNode *patchset, uint32_t options) { const xmlNode *removed = NULL; const xmlNode *added = NULL; const xmlNode *child = NULL; bool is_first = true; int rc = xml_show_patchset_header(out, patchset); /* It's not clear whether "- " or "+ " ever does *not* get overridden by * PCMK__XML_PREFIX_DELETED or PCMK__XML_PREFIX_CREATED in practice. * However, v1 patchsets can only exist during rolling upgrades from * Pacemaker 1.1.11, so not worth worrying about. */ removed = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_REMOVED, NULL, NULL); for (child = pcmk__xml_first_child(removed); child != NULL; child = pcmk__xml_next(child)) { int temp_rc = xml_show_patchset_v1_recursive(out, "- ", child, 0, options |pcmk__xml_fmt_diff_minus); rc = pcmk__output_select_rc(rc, temp_rc); if (is_first) { is_first = false; } else { rc = pcmk__output_select_rc(rc, out->info(out, " --- ")); } } is_first = true; added = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_ADDED, NULL, NULL); for (child = pcmk__xml_first_child(added); child != NULL; child = pcmk__xml_next(child)) { int temp_rc = xml_show_patchset_v1_recursive(out, "+ ", child, 0, options |pcmk__xml_fmt_diff_plus); rc = pcmk__output_select_rc(rc, temp_rc); if (is_first) { is_first = false; } else { rc = pcmk__output_select_rc(rc, out->info(out, " +++ ")); } } return rc; } /*! * \internal * \brief Output a user-friendly form of an XML patchset (format 2) * * This function parses an XML patchset (an \p XML_ATTR_DIFF element and its * children) into a user-friendly combined diff output. * * \param[in,out] out Output object * \param[in] patchset XML patchset to output * * \return Standard Pacemaker return code * * \note This function produces output only for text-like formats. */ static int xml_show_patchset_v2(pcmk__output_t *out, const xmlNode *patchset) { int rc = xml_show_patchset_header(out, patchset); int temp_rc = pcmk_rc_no_output; for (const xmlNode *change = pcmk__xe_first_child(patchset, NULL, NULL, NULL); change != NULL; change = pcmk__xe_next(change)) { const char *op = crm_element_value(change, PCMK_XA_OPERATION); const char *xpath = crm_element_value(change, PCMK_XA_PATH); if (op == NULL) { continue; } if (strcmp(op, PCMK_VALUE_CREATE) == 0) { char *prefix = crm_strdup_printf(PCMK__XML_PREFIX_CREATED " %s: ", xpath); temp_rc = pcmk__xml_show(out, prefix, change->children, 0, pcmk__xml_fmt_pretty|pcmk__xml_fmt_open); rc = pcmk__output_select_rc(rc, temp_rc); // Overwrite all except the first two characters with spaces for (char *ch = prefix + 2; *ch != '\0'; ch++) { *ch = ' '; } temp_rc = pcmk__xml_show(out, prefix, change->children, 0, pcmk__xml_fmt_pretty |pcmk__xml_fmt_children |pcmk__xml_fmt_close); rc = pcmk__output_select_rc(rc, temp_rc); free(prefix); } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) { const char *position = crm_element_value(change, PCMK_XE_POSITION); temp_rc = out->info(out, PCMK__XML_PREFIX_MOVED " %s moved to offset %s", xpath, position); rc = pcmk__output_select_rc(rc, temp_rc); } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) { xmlNode *clist = pcmk__xe_first_child(change, PCMK_XE_CHANGE_LIST, NULL, NULL); GString *buffer_set = NULL; GString *buffer_unset = NULL; for (const xmlNode *child = pcmk__xe_first_child(clist, NULL, NULL, NULL); child != NULL; child = pcmk__xe_next(child)) { const char *name = crm_element_value(child, PCMK_XA_NAME); op = crm_element_value(child, PCMK_XA_OPERATION); if (op == NULL) { continue; } if (strcmp(op, "set") == 0) { const char *value = crm_element_value(child, PCMK_XA_VALUE); pcmk__add_separated_word(&buffer_set, 256, "@", ", "); pcmk__g_strcat(buffer_set, name, "=", value, NULL); } else if (strcmp(op, "unset") == 0) { pcmk__add_separated_word(&buffer_unset, 256, "@", ", "); g_string_append(buffer_unset, name); } } if (buffer_set != NULL) { temp_rc = out->info(out, "+ %s: %s", xpath, buffer_set->str); rc = pcmk__output_select_rc(rc, temp_rc); g_string_free(buffer_set, TRUE); } if (buffer_unset != NULL) { temp_rc = out->info(out, "-- %s: %s", xpath, buffer_unset->str); rc = pcmk__output_select_rc(rc, temp_rc); g_string_free(buffer_unset, TRUE); } } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) { int position = -1; crm_element_value_int(change, PCMK_XE_POSITION, &position); if (position >= 0) { temp_rc = out->info(out, "-- %s (%d)", xpath, position); } else { temp_rc = out->info(out, "-- %s", xpath); } rc = pcmk__output_select_rc(rc, temp_rc); } } return rc; } /*! * \internal * \brief Output a user-friendly form of an XML patchset * * This function parses an XML patchset (an \p XML_ATTR_DIFF element and its * children) into a user-friendly combined diff output. * * \param[in,out] out Output object * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * * \note \p args should contain the following: * -# XML patchset */ PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *") static int xml_patchset_default(pcmk__output_t *out, va_list args) { const xmlNode *patchset = va_arg(args, const xmlNode *); int format = 1; if (patchset == NULL) { crm_trace("Empty patch"); return pcmk_rc_no_output; } crm_element_value_int(patchset, PCMK_XA_FORMAT, &format); switch (format) { case 1: return xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty); case 2: return xml_show_patchset_v2(out, patchset); default: crm_err("Unknown patch format: %d", format); return pcmk_rc_bad_xml_patch; } } /*! * \internal * \brief Output a user-friendly form of an XML patchset * * This function parses an XML patchset (an \p XML_ATTR_DIFF element and its * children) into a user-friendly combined diff output. * * \param[in,out] out Output object * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * * \note \p args should contain the following: * -# XML patchset */ PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *") static int xml_patchset_log(pcmk__output_t *out, va_list args) { static struct qb_log_callsite *patchset_cs = NULL; const xmlNode *patchset = va_arg(args, const xmlNode *); uint8_t log_level = pcmk__output_get_log_level(out); int format = 1; if (log_level == LOG_NEVER) { return pcmk_rc_no_output; } if (patchset == NULL) { crm_trace("Empty patch"); return pcmk_rc_no_output; } if (patchset_cs == NULL) { patchset_cs = qb_log_callsite_get(__func__, __FILE__, "xml-patchset", log_level, __LINE__, crm_trace_nonlog); } if (!crm_is_callsite_active(patchset_cs, log_level, crm_trace_nonlog)) { // Nothing would be logged, so skip all the work return pcmk_rc_no_output; } crm_element_value_int(patchset, PCMK_XA_FORMAT, &format); switch (format) { case 1: if (log_level < LOG_DEBUG) { return xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty |pcmk__xml_fmt_diff_short); } return xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty); case 2: return xml_show_patchset_v2(out, patchset); default: crm_err("Unknown patch format: %d", format); return pcmk_rc_bad_xml_patch; } } /*! * \internal * \brief Output an XML patchset * * This function outputs an XML patchset (an \p XML_ATTR_DIFF element and its * children) without modification, as a CDATA block. * * \param[in,out] out Output object * \param[in] args Message-specific arguments * * \return Standard Pacemaker return code * * \note \p args should contain the following: * -# XML patchset */ PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *") static int xml_patchset_xml(pcmk__output_t *out, va_list args) { const xmlNode *patchset = va_arg(args, const xmlNode *); if (patchset != NULL) { GString *buf = g_string_sized_new(1024); pcmk__xml_string(patchset, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buf, 0); out->output_xml(out, PCMK_XE_XML_PATCHSET, buf->str); g_string_free(buf, TRUE); return pcmk_rc_ok; } crm_trace("Empty patch"); return pcmk_rc_no_output; } static pcmk__message_entry_t fmt_functions[] = { { "xml-patchset", "default", xml_patchset_default }, { "xml-patchset", "log", xml_patchset_log }, { "xml-patchset", "xml", xml_patchset_xml }, { NULL, NULL, NULL } }; /*! * \internal * \brief Register the formatting functions for XML patchsets * * \param[in,out] out Output object */ void pcmk__register_patchset_messages(pcmk__output_t *out) { pcmk__register_messages(out, fmt_functions); } - -// Deprecated functions kept only for backward API compatibility -// LCOV_EXCL_START - -#include - -void -xml_log_patchset(uint8_t log_level, const char *function, - const xmlNode *patchset) -{ - /* This function has some duplication relative to the message functions. - * This way, we can maintain the const xmlNode * in the signature. The - * message functions must be non-const. They have to support XML output - * objects, which must make a copy of a the patchset, requiring a non-const - * function call. - * - * In contrast, this legacy function doesn't need to support XML output. - */ - static struct qb_log_callsite *patchset_cs = NULL; - - pcmk__output_t *out = NULL; - int format = 1; - int rc = pcmk_rc_no_output; - - switch (log_level) { - case LOG_NEVER: - return; - case LOG_STDOUT: - CRM_CHECK(pcmk__text_output_new(&out, NULL) == pcmk_rc_ok, return); - break; - default: - if (patchset_cs == NULL) { - patchset_cs = qb_log_callsite_get(__func__, __FILE__, - "xml-patchset", log_level, - __LINE__, crm_trace_nonlog); - } - if (!crm_is_callsite_active(patchset_cs, log_level, - crm_trace_nonlog)) { - return; - } - CRM_CHECK(pcmk__log_output_new(&out) == pcmk_rc_ok, return); - pcmk__output_set_log_level(out, log_level); - break; - } - - if (patchset == NULL) { - // Should come after the LOG_NEVER check - crm_trace("Empty patch"); - goto done; - } - - crm_element_value_int(patchset, PCMK_XA_FORMAT, &format); - switch (format) { - case 1: - if (log_level < LOG_DEBUG) { - rc = xml_show_patchset_v1(out, patchset, - pcmk__xml_fmt_pretty - |pcmk__xml_fmt_diff_short); - } else { // Note: LOG_STDOUT > LOG_DEBUG - rc = xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty); - } - break; - case 2: - rc = xml_show_patchset_v2(out, patchset); - break; - default: - crm_err("Unknown patch format: %d", format); - rc = pcmk_rc_bad_xml_patch; - break; - } - -done: - out->finish(out, pcmk_rc2exitc(rc), true, NULL); - pcmk__output_free(out); -} - -// LCOV_EXCL_STOP -// End deprecated API