LCOV - code coverage report
Current view: top level - common - output.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 62 124 50.0 %
Date: 2024-05-07 11:09:47 Functions: 9 14 64.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2019-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 <crm/common/util.h>
      13             : #include <crm/common/xml.h>
      14             : #include <libxml/tree.h>
      15             : 
      16             : #include "crmcommon_private.h"
      17             : 
      18             : static GHashTable *formatters = NULL;
      19             : 
      20             : #if defined(PCMK__UNIT_TESTING)
      21             : // LCOV_EXCL_START
      22             : GHashTable *
      23             : pcmk__output_formatters(void) {
      24             :     return formatters;
      25             : }
      26             : // LCOV_EXCL_STOP
      27             : #endif
      28             : 
      29             : void
      30         111 : pcmk__output_free(pcmk__output_t *out) {
      31         111 :     if (out == NULL) {
      32           0 :         return;
      33             :     }
      34             : 
      35         111 :     out->free_priv(out);
      36             : 
      37         111 :     if (out->messages != NULL) {
      38         110 :         g_hash_table_destroy(out->messages);
      39             :     }
      40             : 
      41         111 :     g_free(out->request);
      42         111 :     free(out);
      43             : }
      44             : 
      45             : /*!
      46             :  * \internal
      47             :  * \brief Create a new \p pcmk__output_t structure
      48             :  *
      49             :  * This function does not register any message functions with the newly created
      50             :  * object.
      51             :  *
      52             :  * \param[in,out] out       Where to store the new output object
      53             :  * \param[in]     fmt_name  How to format output
      54             :  * \param[in]     filename  Where to write formatted output. This can be a
      55             :  *                          filename (the file will be overwritten if it already
      56             :  *                          exists), or \p NULL or \p "-" for stdout. For no
      57             :  *                          output, pass a filename of \p "/dev/null".
      58             :  * \param[in]     argv      List of command line arguments
      59             :  *
      60             :  * \return Standard Pacemaker return code
      61             :  */
      62             : int
      63           0 : pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name,
      64             :                       const char *filename, char **argv)
      65             : {
      66           0 :     pcmk__output_factory_t create = NULL;
      67             : 
      68           0 :     CRM_ASSERT(formatters != NULL && out != NULL);
      69             : 
      70             :     /* If no name was given, just try "text".  It's up to each tool to register
      71             :      * what it supports so this also may not be valid.
      72             :      */
      73           0 :     if (fmt_name == NULL) {
      74           0 :         create = g_hash_table_lookup(formatters, "text");
      75             :     } else {
      76           0 :         create = g_hash_table_lookup(formatters, fmt_name);
      77             :     }
      78             : 
      79           0 :     if (create == NULL) {
      80           0 :         return pcmk_rc_unknown_format;
      81             :     }
      82             : 
      83           0 :     *out = create(argv);
      84           0 :     if (*out == NULL) {
      85           0 :         return ENOMEM;
      86             :     }
      87             : 
      88           0 :     if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
      89           0 :         (*out)->dest = stdout;
      90             :     } else {
      91           0 :         (*out)->dest = fopen(filename, "w");
      92           0 :         if ((*out)->dest == NULL) {
      93           0 :             pcmk__output_free(*out);
      94           0 :             *out = NULL;
      95           0 :             return errno;
      96             :         }
      97             :     }
      98             : 
      99           0 :     (*out)->quiet = false;
     100           0 :     (*out)->messages = pcmk__strkey_table(free, NULL);
     101             : 
     102           0 :     if ((*out)->init(*out) == false) {
     103           0 :         pcmk__output_free(*out);
     104           0 :         return ENOMEM;
     105             :     }
     106             : 
     107           0 :     setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
     108             : 
     109           0 :     return pcmk_rc_ok;
     110             : }
     111             : 
     112             : int
     113         114 : pcmk__output_new(pcmk__output_t **out, const char *fmt_name,
     114             :                  const char *filename, char **argv)
     115             : {
     116         114 :     int rc = pcmk__bare_output_new(out, fmt_name, filename, argv);
     117             : 
     118         112 :     if (rc == pcmk_rc_ok) {
     119             :         // Register libcrmcommon messages
     120         108 :         pcmk__register_option_messages(*out);
     121         108 :         pcmk__register_patchset_messages(*out);
     122             :     }
     123         112 :     return rc;
     124             : }
     125             : 
     126             : int
     127         317 : pcmk__register_format(GOptionGroup *group, const char *name,
     128             :                       pcmk__output_factory_t create,
     129             :                       const GOptionEntry *options)
     130             : {
     131         317 :     char *name_copy = NULL;
     132             : 
     133         317 :     CRM_ASSERT(create != NULL && !pcmk__str_empty(name));
     134             : 
     135         313 :     name_copy = strdup(name);
     136         313 :     if (name_copy == NULL) {
     137           0 :         return ENOMEM;
     138             :     }
     139             : 
     140         313 :     if (formatters == NULL) {
     141         113 :         formatters = pcmk__strkey_table(free, NULL);
     142             :     }
     143             : 
     144         313 :     if (options != NULL && group != NULL) {
     145          78 :         g_option_group_add_entries(group, options);
     146             :     }
     147             : 
     148         313 :     g_hash_table_insert(formatters, name_copy, create);
     149         313 :     return pcmk_rc_ok;
     150             : }
     151             : 
     152             : void
     153         128 : pcmk__register_formats(GOptionGroup *group,
     154             :                        const pcmk__supported_format_t *formats)
     155             : {
     156         128 :     if (formats == NULL) {
     157           2 :         return;
     158             :     }
     159         406 :     for (const pcmk__supported_format_t *entry = formats; entry->name != NULL;
     160         280 :          entry++) {
     161         281 :         pcmk__register_format(group, entry->name, entry->create, entry->options);
     162             :     }
     163             : }
     164             : 
     165             : void
     166          66 : pcmk__unregister_formats(void) {
     167          66 :     if (formatters != NULL) {
     168          65 :         g_hash_table_destroy(formatters);
     169          65 :         formatters = NULL;
     170             :     }
     171          66 : }
     172             : 
     173             : int
     174          24 : pcmk__call_message(pcmk__output_t *out, const char *message_id, ...) {
     175             :     va_list args;
     176          24 :     int rc = pcmk_rc_ok;
     177             :     pcmk__message_fn_t fn;
     178             : 
     179          24 :     CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id));
     180             : 
     181          22 :     fn = g_hash_table_lookup(out->messages, message_id);
     182          22 :     if (fn == NULL) {
     183           4 :         crm_debug("Called unknown output message '%s' for format '%s'",
     184             :                   message_id, out->fmt_name);
     185           4 :         return EINVAL;
     186             :     }
     187             : 
     188          18 :     va_start(args, message_id);
     189          18 :     rc = fn(out, args);
     190          18 :     va_end(args);
     191             : 
     192          18 :     return rc;
     193             : }
     194             : 
     195             : void
     196        4053 : pcmk__register_message(pcmk__output_t *out, const char *message_id,
     197             :                        pcmk__message_fn_t fn) {
     198        4053 :     CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id) && fn != NULL);
     199             : 
     200        4048 :     g_hash_table_replace(out->messages, pcmk__str_copy(message_id), fn);
     201        4048 : }
     202             : 
     203             : void
     204         319 : pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
     205             : {
     206        6099 :     for (const pcmk__message_entry_t *entry = table; entry->message_id != NULL;
     207        5780 :          entry++) {
     208        5781 :         if (pcmk__strcase_any_of(entry->fmt_name, "default", out->fmt_name, NULL)) {
     209        4046 :             pcmk__register_message(out, entry->message_id, entry->fn);
     210             :         }
     211             :     }
     212         318 : }
     213             : 
     214             : void
     215          39 : pcmk__output_and_clear_error(GError **error, pcmk__output_t *out)
     216             : {
     217          39 :     if (error == NULL || *error == NULL) {
     218          38 :         return;
     219             :     }
     220             : 
     221           1 :     if (out != NULL) {
     222           1 :         out->err(out, "%s: %s", g_get_prgname(), (*error)->message);
     223             :     } else {
     224           0 :         fprintf(stderr, "%s: %s\n", g_get_prgname(), (*error)->message);
     225             :     }
     226             : 
     227           1 :     g_clear_error(error);
     228             : }
     229             : 
     230             : /*!
     231             :  * \internal
     232             :  * \brief Create an XML-only output object
     233             :  *
     234             :  * Create an output object that supports only the XML format, and free
     235             :  * existing XML if supplied (particularly useful for libpacemaker public API
     236             :  * functions that want to free any previous result supplied by the caller).
     237             :  *
     238             :  * \param[out]     out  Where to put newly created output object
     239             :  * \param[in,out]  xml  If \c *xml is non-NULL, this will be freed
     240             :  *
     241             :  * \return Standard Pacemaker return code
     242             :  */
     243             : int
     244           0 : pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml) {
     245           0 :     pcmk__supported_format_t xml_format[] = {
     246             :         PCMK__SUPPORTED_FORMAT_XML,
     247             :         { NULL, NULL, NULL }
     248             :     };
     249             : 
     250           0 :     if (xml == NULL) {
     251           0 :         return EINVAL;
     252             :     }
     253             : 
     254           0 :     if (*xml != NULL) {
     255           0 :         xmlFreeNode(*xml);
     256           0 :         *xml = NULL;
     257             :     }
     258           0 :     pcmk__register_formats(NULL, xml_format);
     259           0 :     return pcmk__output_new(out, "xml", NULL, NULL);
     260             : }
     261             : 
     262             : /*!
     263             :  * \internal
     264             :  * \brief  Finish and free an XML-only output object
     265             :  *
     266             :  * \param[in,out] out         Output object to free
     267             :  * \param[in]     exit_status The exit value of the whole program
     268             :  * \param[out]    xml         If not NULL, where to store XML output
     269             :  */
     270             : void
     271           0 : pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status,
     272             :                         xmlNodePtr *xml)
     273             : {
     274           0 :     if (out == NULL) {
     275           0 :         return;
     276             :     }
     277             : 
     278           0 :     out->finish(out, exit_status, FALSE, (void **) xml);
     279           0 :     pcmk__output_free(out);
     280             : }
     281             : 
     282             : /*!
     283             :  * \internal
     284             :  * \brief Create a new output object using the "log" format
     285             :  *
     286             :  * \param[out] out  Where to store newly allocated output object
     287             :  *
     288             :  * \return Standard Pacemaker return code
     289             :  */
     290             : int
     291           0 : pcmk__log_output_new(pcmk__output_t **out)
     292             : {
     293           0 :     int rc = pcmk_rc_ok;
     294           0 :     const char* argv[] = { "", NULL };
     295           0 :     pcmk__supported_format_t formats[] = {
     296             :         PCMK__SUPPORTED_FORMAT_LOG,
     297             :         { NULL, NULL, NULL }
     298             :     };
     299             : 
     300           0 :     pcmk__register_formats(NULL, formats);
     301           0 :     rc = pcmk__output_new(out, "log", NULL, (char **) argv);
     302           0 :     if ((rc != pcmk_rc_ok) || (*out == NULL)) {
     303           0 :         crm_err("Can't log certain messages due to internal error: %s",
     304             :                 pcmk_rc_str(rc));
     305           0 :         return rc;
     306             :     }
     307           0 :     return pcmk_rc_ok;
     308             : }
     309             : 
     310             : /*!
     311             :  * \internal
     312             :  * \brief Create a new output object using the "text" format
     313             :  *
     314             :  * \param[out] out       Where to store newly allocated output object
     315             :  * \param[in]  filename  Name of output destination file
     316             :  *
     317             :  * \return Standard Pacemaker return code
     318             :  */
     319             : int
     320           0 : pcmk__text_output_new(pcmk__output_t **out, const char *filename)
     321             : {
     322           0 :     int rc = pcmk_rc_ok;
     323           0 :     const char* argv[] = { "", NULL };
     324           0 :     pcmk__supported_format_t formats[] = {
     325             :         PCMK__SUPPORTED_FORMAT_TEXT,
     326             :         { NULL, NULL, NULL }
     327             :     };
     328             : 
     329           0 :     pcmk__register_formats(NULL, formats);
     330           0 :     rc = pcmk__output_new(out, "text", filename, (char **) argv);
     331           0 :     if ((rc != pcmk_rc_ok) || (*out == NULL)) {
     332           0 :         crm_err("Can't create text output object to internal error: %s",
     333             :                 pcmk_rc_str(rc));
     334           0 :         return rc;
     335             :     }
     336           0 :     return pcmk_rc_ok;
     337             : }

Generated by: LCOV version 1.14