root/lib/common/output.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__output_formatters
  2. pcmk__output_free
  3. pcmk__bare_output_new
  4. pcmk__output_new
  5. pcmk__register_format
  6. pcmk__register_formats
  7. pcmk__unregister_formats
  8. pcmk__call_message
  9. pcmk__register_message
  10. pcmk__register_messages
  11. pcmk__output_and_clear_error
  12. pcmk__xml_output_new
  13. pcmk__xml_output_finish
  14. pcmk__log_output_new
  15. pcmk__text_output_new

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

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