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

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