LCOV - code coverage report
Current view: top level - common - xml_display.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 137 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2004-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             : #include <crm_internal.h>
      11             : 
      12             : #include <libxml/tree.h>
      13             : 
      14             : #include <crm/crm.h>
      15             : #include <crm/common/xml.h>
      16             : #include <crm/common/xml_internal.h>  // PCMK__XML_LOG_BASE, etc.
      17             : #include "crmcommon_private.h"
      18             : 
      19             : static int show_xml_node(pcmk__output_t *out, GString *buffer,
      20             :                          const char *prefix, const xmlNode *data, int depth,
      21             :                          uint32_t options);
      22             : 
      23             : // Log an XML library error
      24             : void
      25           0 : pcmk__log_xmllib_err(void *ctx, const char *fmt, ...)
      26             : {
      27             :     va_list ap;
      28             : 
      29           0 :     va_start(ap, fmt);
      30           0 :     pcmk__if_tracing(
      31             :         {
      32             :             PCMK__XML_LOG_BASE(LOG_ERR, TRUE,
      33             :                                crm_abort(__FILE__, __PRETTY_FUNCTION__,
      34             :                                          __LINE__, "xml library error", TRUE,
      35             :                                          TRUE),
      36             :                                "XML Error: ", fmt, ap);
      37             :         },
      38             :         {
      39             :             PCMK__XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
      40             :         }
      41             :     );
      42           0 :     va_end(ap);
      43           0 : }
      44             : 
      45             : /*!
      46             :  * \internal
      47             :  * \brief Output an XML comment with depth-based indentation
      48             :  *
      49             :  * \param[in,out] out      Output object
      50             :  * \param[in]     data     XML node to output
      51             :  * \param[in]     depth    Current indentation level
      52             :  * \param[in]     options  Group of \p pcmk__xml_fmt_options flags
      53             :  *
      54             :  * \return Standard Pacemaker return code
      55             :  *
      56             :  * \note This currently produces output only for text-like output objects.
      57             :  */
      58             : static int
      59           0 : show_xml_comment(pcmk__output_t *out, const xmlNode *data, int depth,
      60             :                  uint32_t options)
      61             : {
      62           0 :     if (pcmk_is_set(options, pcmk__xml_fmt_open)) {
      63           0 :         int width = pcmk_is_set(options, pcmk__xml_fmt_pretty)? (2 * depth) : 0;
      64             : 
      65           0 :         return out->info(out, "%*s<!--%s-->",
      66           0 :                          width, "", (const char *) data->content);
      67             :     }
      68           0 :     return pcmk_rc_no_output;
      69             : }
      70             : 
      71             : /*!
      72             :  * \internal
      73             :  * \brief Output an XML element in a formatted way
      74             :  *
      75             :  * \param[in,out] out      Output object
      76             :  * \param[in,out] buffer   Where to build output strings
      77             :  * \param[in]     prefix   String to prepend to every line of output
      78             :  * \param[in]     data     XML node to output
      79             :  * \param[in]     depth    Current indentation level
      80             :  * \param[in]     options  Group of \p pcmk__xml_fmt_options flags
      81             :  *
      82             :  * \return Standard Pacemaker return code
      83             :  *
      84             :  * \note This is a recursive helper function for \p show_xml_node().
      85             :  * \note This currently produces output only for text-like output objects.
      86             :  * \note \p buffer may be overwritten many times. The caller is responsible for
      87             :  *       freeing it using \p g_string_free() but should not rely on its
      88             :  *       contents.
      89             :  */
      90             : static int
      91           0 : show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix,
      92             :                  const xmlNode *data, int depth, uint32_t options)
      93             : {
      94           0 :     int spaces = pcmk_is_set(options, pcmk__xml_fmt_pretty)? (2 * depth) : 0;
      95           0 :     int rc = pcmk_rc_no_output;
      96             : 
      97           0 :     if (pcmk_is_set(options, pcmk__xml_fmt_open)) {
      98           0 :         const char *hidden = crm_element_value(data, PCMK__XA_HIDDEN);
      99             : 
     100             :         g_string_truncate(buffer, 0);
     101             : 
     102           0 :         for (int lpc = 0; lpc < spaces; lpc++) {
     103             :             g_string_append_c(buffer, ' ');
     104             :         }
     105           0 :         pcmk__g_strcat(buffer, "<", data->name, NULL);
     106             : 
     107           0 :         for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
     108           0 :              attr = attr->next) {
     109           0 :             xml_node_private_t *nodepriv = attr->_private;
     110           0 :             const char *p_name = (const char *) attr->name;
     111           0 :             const char *p_value = pcmk__xml_attr_value(attr);
     112           0 :             gchar *p_copy = NULL;
     113             : 
     114           0 :             if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
     115           0 :                 continue;
     116             :             }
     117             : 
     118             :             // @COMPAT Remove when v1 patchsets are removed
     119           0 :             if (pcmk_any_flags_set(options,
     120             :                                    pcmk__xml_fmt_diff_plus
     121             :                                    |pcmk__xml_fmt_diff_minus)
     122           0 :                 && (strcmp(PCMK__XA_CRM_DIFF_MARKER, p_name) == 0)) {
     123           0 :                 continue;
     124             :             }
     125             : 
     126           0 :             if ((hidden != NULL) && (p_name[0] != '\0')
     127           0 :                 && (strstr(hidden, p_name) != NULL)) {
     128             : 
     129           0 :                 p_value = "*****";
     130             : 
     131             :             } else {
     132           0 :                 p_copy = pcmk__xml_escape(p_value, true);
     133           0 :                 p_value = p_copy;
     134             :             }
     135             : 
     136           0 :             pcmk__g_strcat(buffer, " ", p_name, "=\"",
     137             :                            pcmk__s(p_value, "<null>"), "\"", NULL);
     138           0 :             g_free(p_copy);
     139             :         }
     140             : 
     141           0 :         if ((data->children != NULL)
     142           0 :             && pcmk_is_set(options, pcmk__xml_fmt_children)) {
     143           0 :             g_string_append_c(buffer, '>');
     144             : 
     145             :         } else {
     146           0 :             g_string_append(buffer, "/>");
     147             :         }
     148             : 
     149           0 :         rc = out->info(out, "%s%s%s",
     150           0 :                        pcmk__s(prefix, ""), pcmk__str_empty(prefix)? "" : " ",
     151             :                        buffer->str);
     152             :     }
     153             : 
     154           0 :     if (data->children == NULL) {
     155           0 :         return rc;
     156             :     }
     157             : 
     158           0 :     if (pcmk_is_set(options, pcmk__xml_fmt_children)) {
     159           0 :         for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
     160           0 :              child = pcmk__xml_next(child)) {
     161             : 
     162           0 :             int temp_rc = show_xml_node(out, buffer, prefix, child, depth + 1,
     163             :                                         options
     164             :                                         |pcmk__xml_fmt_open
     165             :                                         |pcmk__xml_fmt_close);
     166           0 :             rc = pcmk__output_select_rc(rc, temp_rc);
     167             :         }
     168             :     }
     169             : 
     170           0 :     if (pcmk_is_set(options, pcmk__xml_fmt_close)) {
     171           0 :         int temp_rc = out->info(out, "%s%s%*s</%s>",
     172             :                                 pcmk__s(prefix, ""),
     173           0 :                                 pcmk__str_empty(prefix)? "" : " ",
     174           0 :                                 spaces, "", data->name);
     175           0 :         rc = pcmk__output_select_rc(rc, temp_rc);
     176             :     }
     177             : 
     178           0 :     return rc;
     179             : }
     180             : 
     181             : /*!
     182             :  * \internal
     183             :  * \brief Output an XML element or comment in a formatted way
     184             :  *
     185             :  * \param[in,out] out      Output object
     186             :  * \param[in,out] buffer   Where to build output strings
     187             :  * \param[in]     prefix   String to prepend to every line of output
     188             :  * \param[in]     data     XML node to log
     189             :  * \param[in]     depth    Current indentation level
     190             :  * \param[in]     options  Group of \p pcmk__xml_fmt_options flags
     191             :  *
     192             :  * \return Standard Pacemaker return code
     193             :  *
     194             :  * \note This is a recursive helper function for \p pcmk__xml_show().
     195             :  * \note This currently produces output only for text-like output objects.
     196             :  * \note \p buffer may be overwritten many times. The caller is responsible for
     197             :  *       freeing it using \p g_string_free() but should not rely on its
     198             :  *       contents.
     199             :  */
     200             : static int
     201           0 : show_xml_node(pcmk__output_t *out, GString *buffer, const char *prefix,
     202             :               const xmlNode *data, int depth, uint32_t options)
     203             : {
     204           0 :     switch (data->type) {
     205           0 :         case XML_COMMENT_NODE:
     206           0 :             return show_xml_comment(out, data, depth, options);
     207           0 :         case XML_ELEMENT_NODE:
     208           0 :             return show_xml_element(out, buffer, prefix, data, depth, options);
     209           0 :         default:
     210           0 :             return pcmk_rc_no_output;
     211             :     }
     212             : }
     213             : 
     214             : /*!
     215             :  * \internal
     216             :  * \brief Output an XML element or comment in a formatted way
     217             :  *
     218             :  * \param[in,out] out        Output object
     219             :  * \param[in]     prefix     String to prepend to every line of output
     220             :  * \param[in]     data       XML node to output
     221             :  * \param[in]     depth      Current nesting level
     222             :  * \param[in]     options    Group of \p pcmk__xml_fmt_options flags
     223             :  *
     224             :  * \return Standard Pacemaker return code
     225             :  *
     226             :  * \note This currently produces output only for text-like output objects.
     227             :  */
     228             : int
     229           0 : pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data,
     230             :                int depth, uint32_t options)
     231             : {
     232           0 :     int rc = pcmk_rc_no_output;
     233           0 :     GString *buffer = NULL;
     234             : 
     235           0 :     CRM_ASSERT(out != NULL);
     236           0 :     CRM_CHECK(depth >= 0, depth = 0);
     237             : 
     238           0 :     if (data == NULL) {
     239           0 :         return rc;
     240             :     }
     241             : 
     242             :     /* Allocate a buffer once, for show_xml_node() to truncate and reuse in
     243             :      * recursive calls
     244             :      */
     245           0 :     buffer = g_string_sized_new(1024);
     246           0 :     rc = show_xml_node(out, buffer, prefix, data, depth, options);
     247           0 :     g_string_free(buffer, TRUE);
     248             : 
     249           0 :     return rc;
     250             : }
     251             : 
     252             : /*!
     253             :  * \internal
     254             :  * \brief Output XML portions that have been marked as changed
     255             :  *
     256             :  * \param[in,out] out      Output object
     257             :  * \param[in]     data     XML node to output
     258             :  * \param[in]     depth    Current indentation level
     259             :  * \param[in]     options  Group of \p pcmk__xml_fmt_options flags
     260             :  *
     261             :  * \note This is a recursive helper for \p pcmk__xml_show_changes(), showing
     262             :  *       changes to \p data and its children.
     263             :  * \note This currently produces output only for text-like output objects.
     264             :  */
     265             : static int
     266           0 : show_xml_changes_recursive(pcmk__output_t *out, const xmlNode *data, int depth,
     267             :                            uint32_t options)
     268             : {
     269             :     /* @COMPAT: When log_data_element() is removed, we can remove the options
     270             :      * argument here and instead hard-code pcmk__xml_log_pretty.
     271             :      */
     272           0 :     xml_node_private_t *nodepriv = (xml_node_private_t *) data->_private;
     273           0 :     int rc = pcmk_rc_no_output;
     274           0 :     int temp_rc = pcmk_rc_no_output;
     275             : 
     276           0 :     if (pcmk_all_flags_set(nodepriv->flags, pcmk__xf_dirty|pcmk__xf_created)) {
     277             :         // Newly created
     278           0 :         return pcmk__xml_show(out, PCMK__XML_PREFIX_CREATED, data, depth,
     279             :                               options
     280             :                               |pcmk__xml_fmt_open
     281             :                               |pcmk__xml_fmt_children
     282             :                               |pcmk__xml_fmt_close);
     283             :     }
     284             : 
     285           0 :     if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
     286             :         // Modified or moved
     287           0 :         bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
     288           0 :         int spaces = pretty? (2 * depth) : 0;
     289           0 :         const char *prefix = PCMK__XML_PREFIX_MODIFIED;
     290             : 
     291           0 :         if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
     292           0 :             prefix = PCMK__XML_PREFIX_MOVED;
     293             :         }
     294             : 
     295             :         // Log opening tag
     296           0 :         rc = pcmk__xml_show(out, prefix, data, depth,
     297             :                             options|pcmk__xml_fmt_open);
     298             : 
     299             :         // Log changes to attributes
     300           0 :         for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
     301           0 :              attr = attr->next) {
     302           0 :             const char *name = (const char *) attr->name;
     303             : 
     304           0 :             nodepriv = attr->_private;
     305             : 
     306           0 :             if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
     307           0 :                 const char *value = pcmk__xml_attr_value(attr);
     308             : 
     309           0 :                 temp_rc = out->info(out, "%s %*s @%s=%s",
     310             :                                     PCMK__XML_PREFIX_DELETED, spaces, "", name,
     311             :                                     value);
     312             : 
     313           0 :             } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
     314           0 :                 const char *value = pcmk__xml_attr_value(attr);
     315             : 
     316           0 :                 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
     317           0 :                     prefix = PCMK__XML_PREFIX_CREATED;
     318             : 
     319           0 :                 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_modified)) {
     320           0 :                     prefix = PCMK__XML_PREFIX_MODIFIED;
     321             : 
     322           0 :                 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
     323           0 :                     prefix = PCMK__XML_PREFIX_MOVED;
     324             : 
     325             :                 } else {
     326           0 :                     prefix = PCMK__XML_PREFIX_MODIFIED;
     327             :                 }
     328             : 
     329           0 :                 temp_rc = out->info(out, "%s %*s @%s=%s",
     330             :                                     prefix, spaces, "", name, value);
     331             :             }
     332           0 :             rc = pcmk__output_select_rc(rc, temp_rc);
     333             :         }
     334             : 
     335             :         // Log changes to children
     336           0 :         for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
     337           0 :              child = pcmk__xml_next(child)) {
     338           0 :             temp_rc = show_xml_changes_recursive(out, child, depth + 1,
     339             :                                                  options);
     340           0 :             rc = pcmk__output_select_rc(rc, temp_rc);
     341             :         }
     342             : 
     343             :         // Log closing tag
     344           0 :         temp_rc = pcmk__xml_show(out, PCMK__XML_PREFIX_MODIFIED, data, depth,
     345             :                                  options|pcmk__xml_fmt_close);
     346           0 :         return pcmk__output_select_rc(rc, temp_rc);
     347             :     }
     348             : 
     349             :     // This node hasn't changed, but check its children
     350           0 :     for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
     351           0 :          child = pcmk__xml_next(child)) {
     352           0 :         temp_rc = show_xml_changes_recursive(out, child, depth + 1, options);
     353           0 :         rc = pcmk__output_select_rc(rc, temp_rc);
     354             :     }
     355           0 :     return rc;
     356             : }
     357             : 
     358             : /*!
     359             :  * \internal
     360             :  * \brief Output changes to an XML node and any children
     361             :  *
     362             :  * \param[in,out] out  Output object
     363             :  * \param[in]     xml  XML node to output
     364             :  *
     365             :  * \return Standard Pacemaker return code
     366             :  *
     367             :  * \note This currently produces output only for text-like output objects.
     368             :  */
     369             : int
     370           0 : pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml)
     371             : {
     372           0 :     xml_doc_private_t *docpriv = NULL;
     373           0 :     int rc = pcmk_rc_no_output;
     374           0 :     int temp_rc = pcmk_rc_no_output;
     375             : 
     376           0 :     CRM_ASSERT(out != NULL);
     377           0 :     CRM_ASSERT(xml != NULL);
     378           0 :     CRM_ASSERT(xml->doc != NULL);
     379             : 
     380           0 :     docpriv = xml->doc->_private;
     381           0 :     if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
     382           0 :         return rc;
     383             :     }
     384             : 
     385           0 :     for (const GList *iter = docpriv->deleted_objs; iter != NULL;
     386           0 :          iter = iter->next) {
     387           0 :         const pcmk__deleted_xml_t *deleted_obj = iter->data;
     388             : 
     389           0 :         if (deleted_obj->position >= 0) {
     390           0 :             temp_rc = out->info(out, PCMK__XML_PREFIX_DELETED " %s (%d)",
     391           0 :                                 deleted_obj->path, deleted_obj->position);
     392             :         } else {
     393           0 :             temp_rc = out->info(out, PCMK__XML_PREFIX_DELETED " %s",
     394           0 :                                 deleted_obj->path);
     395             :         }
     396           0 :         rc = pcmk__output_select_rc(rc, temp_rc);
     397             :     }
     398             : 
     399           0 :     temp_rc = show_xml_changes_recursive(out, xml, 0, pcmk__xml_fmt_pretty);
     400           0 :     return pcmk__output_select_rc(rc, temp_rc);
     401             : }
     402             : 
     403             : // Deprecated functions kept only for backward API compatibility
     404             : // LCOV_EXCL_START
     405             : 
     406             : #include <crm/common/logging_compat.h>
     407             : #include <crm/common/xml_compat.h>
     408             : 
     409             : void
     410             : log_data_element(int log_level, const char *file, const char *function,
     411             :                  int line, const char *prefix, const xmlNode *data, int depth,
     412             :                  int legacy_options)
     413             : {
     414             :     uint32_t options = 0;
     415             :     pcmk__output_t *out = NULL;
     416             : 
     417             :     // Confine log_level to uint8_t range
     418             :     log_level = pcmk__clip_log_level(log_level);
     419             : 
     420             :     if (data == NULL) {
     421             :         do_crm_log(log_level, "%s%sNo data to dump as XML",
     422             :                    pcmk__s(prefix, ""), pcmk__str_empty(prefix)? "" : " ");
     423             :         return;
     424             :     }
     425             : 
     426             :     switch (log_level) {
     427             :         case LOG_NEVER:
     428             :             return;
     429             :         case LOG_STDOUT:
     430             :             CRM_CHECK(pcmk__text_output_new(&out, NULL) == pcmk_rc_ok, return);
     431             :             break;
     432             :         default:
     433             :             CRM_CHECK(pcmk__log_output_new(&out) == pcmk_rc_ok, return);
     434             :             pcmk__output_set_log_level(out, log_level);
     435             :             break;
     436             :     }
     437             : 
     438             :     /* Map xml_log_options to pcmk__xml_fmt_options so that we can go ahead and
     439             :      * start using the pcmk__xml_fmt_options in all the internal functions.
     440             :      *
     441             :      * xml_log_option_dirty_add and xml_log_option_diff_all are ignored by
     442             :      * internal code and only used here, so they don't need to be addressed.
     443             :      */
     444             :     if (pcmk_is_set(legacy_options, xml_log_option_filtered)) {
     445             :         options |= pcmk__xml_fmt_filtered;
     446             :     }
     447             :     if (pcmk_is_set(legacy_options, xml_log_option_formatted)) {
     448             :         options |= pcmk__xml_fmt_pretty;
     449             :     }
     450             :     if (pcmk_is_set(legacy_options, xml_log_option_open)) {
     451             :         options |= pcmk__xml_fmt_open;
     452             :     }
     453             :     if (pcmk_is_set(legacy_options, xml_log_option_children)) {
     454             :         options |= pcmk__xml_fmt_children;
     455             :     }
     456             :     if (pcmk_is_set(legacy_options, xml_log_option_close)) {
     457             :         options |= pcmk__xml_fmt_close;
     458             :     }
     459             :     if (pcmk_is_set(legacy_options, xml_log_option_text)) {
     460             :         options |= pcmk__xml_fmt_text;
     461             :     }
     462             :     if (pcmk_is_set(legacy_options, xml_log_option_diff_plus)) {
     463             :         options |= pcmk__xml_fmt_diff_plus;
     464             :     }
     465             :     if (pcmk_is_set(legacy_options, xml_log_option_diff_minus)) {
     466             :         options |= pcmk__xml_fmt_diff_minus;
     467             :     }
     468             :     if (pcmk_is_set(legacy_options, xml_log_option_diff_short)) {
     469             :         options |= pcmk__xml_fmt_diff_short;
     470             :     }
     471             : 
     472             :     // Log element based on options
     473             :     if (pcmk_is_set(legacy_options, xml_log_option_dirty_add)) {
     474             :         CRM_CHECK(depth >= 0, depth = 0);
     475             :         show_xml_changes_recursive(out, data, depth, options);
     476             :         goto done;
     477             :     }
     478             : 
     479             :     if (pcmk_is_set(options, pcmk__xml_fmt_pretty)
     480             :         && ((data->children == NULL)
     481             :             || (crm_element_value(data, PCMK__XA_CRM_DIFF_MARKER) != NULL))) {
     482             : 
     483             :         if (pcmk_is_set(options, pcmk__xml_fmt_diff_plus)) {
     484             :             legacy_options |= xml_log_option_diff_all;
     485             :             prefix = PCMK__XML_PREFIX_CREATED;
     486             : 
     487             :         } else if (pcmk_is_set(options, pcmk__xml_fmt_diff_minus)) {
     488             :             legacy_options |= xml_log_option_diff_all;
     489             :             prefix = PCMK__XML_PREFIX_DELETED;
     490             :         }
     491             :     }
     492             : 
     493             :     if (pcmk_is_set(options, pcmk__xml_fmt_diff_short)
     494             :         && !pcmk_is_set(legacy_options, xml_log_option_diff_all)) {
     495             : 
     496             :         if (!pcmk_any_flags_set(options,
     497             :                                 pcmk__xml_fmt_diff_plus
     498             :                                 |pcmk__xml_fmt_diff_minus)) {
     499             :             // Nothing will ever be logged
     500             :             goto done;
     501             :         }
     502             : 
     503             :         // Keep looking for the actual change
     504             :         for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
     505             :              child = pcmk__xml_next(child)) {
     506             :             log_data_element(log_level, file, function, line, prefix, child,
     507             :                              depth + 1, options);
     508             :         }
     509             : 
     510             :     } else {
     511             :         pcmk__xml_show(out, prefix, data, depth,
     512             :                        options
     513             :                        |pcmk__xml_fmt_open
     514             :                        |pcmk__xml_fmt_children
     515             :                        |pcmk__xml_fmt_close);
     516             :     }
     517             : 
     518             : done:
     519             :     out->finish(out, CRM_EX_OK, true, NULL);
     520             :     pcmk__output_free(out);
     521             : }
     522             : 
     523             : void
     524             : xml_log_changes(uint8_t log_level, const char *function, const xmlNode *xml)
     525             : {
     526             :     pcmk__output_t *out = NULL;
     527             :     int rc = pcmk_rc_ok;
     528             : 
     529             :     switch (log_level) {
     530             :         case LOG_NEVER:
     531             :             return;
     532             :         case LOG_STDOUT:
     533             :             CRM_CHECK(pcmk__text_output_new(&out, NULL) == pcmk_rc_ok, return);
     534             :             break;
     535             :         default:
     536             :             CRM_CHECK(pcmk__log_output_new(&out) == pcmk_rc_ok, return);
     537             :             pcmk__output_set_log_level(out, log_level);
     538             :             break;
     539             :     }
     540             :     rc = pcmk__xml_show_changes(out, xml);
     541             :     out->finish(out, pcmk_rc2exitc(rc), true, NULL);
     542             :     pcmk__output_free(out);
     543             : }
     544             : 
     545             : // LCOV_EXCL_STOP
     546             : // End deprecated API

Generated by: LCOV version 1.14