diff --git a/lib/common/patchset_display.c b/lib/common/patchset_display.c index 0855c6e829..937798c6e0 100644 --- a/lib/common/patchset_display.c +++ b/lib/common/patchset_display.c @@ -1,452 +1,326 @@ /* * 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) + * \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] 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) +xml_show_patchset(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; + if (format != 2) { + crm_err("Unknown patch format: %d", format); + return pcmk_rc_bad_xml_patch; } + + return xml_show_patchset(out, patchset); } /*! * \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; + if (format != 2) { + crm_err("Unknown patch format: %d", format); + return pcmk_rc_bad_xml_patch; } + + return xml_show_patchset(out, patchset); } /*! * \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); }