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

          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             : #include <crm/common/cmdline_internal.h>
      12             : 
      13             : #include <ctype.h>
      14             : #include <stdarg.h>
      15             : #include <stdint.h>
      16             : #include <stdlib.h>
      17             : #include <stdio.h>
      18             : 
      19             : typedef struct private_data_s {
      20             :     /* gathered in log_begin_list */
      21             :     GQueue/*<char*>*/ *prefixes;
      22             :     uint8_t log_level;
      23             :     const char *function;
      24             :     const char *file;
      25             :     uint32_t line;
      26             :     uint32_t tags;
      27             : } private_data_t;
      28             : 
      29             : /*!
      30             :  * \internal
      31             :  * \brief Log a message using output object's log level and filters
      32             :  *
      33             :  * \param[in] priv    Output object's private_data_t
      34             :  * \param[in] fmt     printf(3)-style format string
      35             :  * \param[in] args... Format string arguments
      36             :  */
      37             : #define logger(priv, fmt, args...) do {                                     \
      38             :         qb_log_from_external_source(pcmk__s((priv)->function, __func__),    \
      39             :             pcmk__s((priv)->file, __FILE__), fmt, (priv)->log_level,        \
      40             :             (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags,   \
      41             :             ##args);                                                        \
      42             :     } while (0);
      43             : 
      44             : /*!
      45             :  * \internal
      46             :  * \brief Log a message using an explicit log level and output object's filters
      47             :  *
      48             :  * \param[in] priv    Output object's private_data_t
      49             :  * \param[in] level   Log level
      50             :  * \param[in] fmt     printf(3)-style format string
      51             :  * \param[in] ap      Variadic arguments
      52             :  */
      53             : #define logger_va(priv, level, fmt, ap) do {                                \
      54             :         qb_log_from_external_source_va(pcmk__s((priv)->function, __func__), \
      55             :             pcmk__s((priv)->file, __FILE__), fmt, level,                    \
      56             :             (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags,   \
      57             :             ap);                                                            \
      58             :     } while (0);
      59             : 
      60             : static void
      61           0 : log_subprocess_output(pcmk__output_t *out, int exit_status,
      62             :                       const char *proc_stdout, const char *proc_stderr) {
      63             :     /* This function intentionally left blank */
      64           0 : }
      65             : 
      66             : static void
      67           0 : log_free_priv(pcmk__output_t *out) {
      68           0 :     private_data_t *priv = NULL;
      69             : 
      70           0 :     if (out == NULL || out->priv == NULL) {
      71           0 :         return;
      72             :     }
      73             : 
      74           0 :     priv = out->priv;
      75             : 
      76           0 :     g_queue_free(priv->prefixes);
      77           0 :     free(priv);
      78           0 :     out->priv = NULL;
      79             : }
      80             : 
      81             : static bool
      82           0 : log_init(pcmk__output_t *out) {
      83           0 :     private_data_t *priv = NULL;
      84             : 
      85           0 :     CRM_ASSERT(out != NULL);
      86             : 
      87             :     /* If log_init was previously called on this output struct, just return. */
      88           0 :     if (out->priv != NULL) {
      89           0 :         return true;
      90             :     }
      91             : 
      92           0 :     out->priv = calloc(1, sizeof(private_data_t));
      93           0 :     if (out->priv == NULL) {
      94           0 :          return false;
      95             :     }
      96             : 
      97           0 :     priv = out->priv;
      98             : 
      99           0 :     priv->prefixes = g_queue_new();
     100           0 :     priv->log_level = LOG_INFO;
     101             : 
     102           0 :     return true;
     103             : }
     104             : 
     105             : static void
     106           0 : log_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
     107             :     /* This function intentionally left blank */
     108           0 : }
     109             : 
     110             : static void
     111           0 : log_reset(pcmk__output_t *out) {
     112           0 :     CRM_ASSERT(out != NULL);
     113             : 
     114           0 :     out->dest = freopen(NULL, "w", out->dest);
     115           0 :     CRM_ASSERT(out->dest != NULL);
     116             : 
     117           0 :     log_free_priv(out);
     118           0 :     log_init(out);
     119           0 : }
     120             : 
     121             : static void
     122           0 : log_version(pcmk__output_t *out, bool extended) {
     123           0 :     private_data_t *priv = NULL;
     124             : 
     125           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     126           0 :     priv = out->priv;
     127             : 
     128           0 :     if (extended) {
     129           0 :         logger(priv, "Pacemaker %s (Build: %s): %s",
     130             :                PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
     131             :     } else {
     132           0 :         logger(priv, "Pacemaker " PACEMAKER_VERSION);
     133           0 :         logger(priv, "Written by Andrew Beekhof and "
     134             :                      "the Pacemaker project contributors");
     135             :     }
     136           0 : }
     137             : 
     138             : G_GNUC_PRINTF(2, 3)
     139             : static void
     140           0 : log_err(pcmk__output_t *out, const char *format, ...)
     141             : {
     142             :     va_list ap;
     143           0 :     private_data_t *priv = NULL;
     144             : 
     145           0 :     CRM_ASSERT((out != NULL) && (out->priv != NULL));
     146           0 :     priv = out->priv;
     147             : 
     148             :     /* Error output does not get indented, to separate it from other
     149             :      * potentially indented list output.
     150             :      */
     151           0 :     va_start(ap, format);
     152           0 :     logger_va(priv, LOG_ERR, format, ap);
     153           0 :     va_end(ap);
     154           0 : }
     155             : 
     156             : static void
     157           0 : log_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     158           0 :     xmlNodePtr node = NULL;
     159           0 :     private_data_t *priv = NULL;
     160             : 
     161           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     162           0 :     priv = out->priv;
     163             : 
     164           0 :     node = pcmk__xe_create(NULL, name);
     165           0 :     pcmk__xe_set_content(node, "%s", buf);
     166           0 :     do_crm_log_xml(priv->log_level, name, node);
     167           0 :     free(node);
     168           0 : }
     169             : 
     170             : G_GNUC_PRINTF(4, 5)
     171             : static void
     172           0 : log_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
     173             :                const char *format, ...) {
     174           0 :     int len = 0;
     175             :     va_list ap;
     176           0 :     char* buffer = NULL;
     177           0 :     private_data_t *priv = NULL;
     178             : 
     179           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     180           0 :     priv = out->priv;
     181             : 
     182           0 :     va_start(ap, format);
     183           0 :     len = vasprintf(&buffer, format, ap);
     184           0 :     CRM_ASSERT(len >= 0);
     185           0 :     va_end(ap);
     186             : 
     187             :     /* Don't skip empty prefixes,
     188             :      * otherwise there will be mismatch
     189             :      * in the log_end_list */
     190           0 :     if(strcmp(buffer, "") == 0) {
     191             :         /* nothing */
     192             :     }
     193             : 
     194           0 :     g_queue_push_tail(priv->prefixes, buffer);
     195           0 : }
     196             : 
     197             : G_GNUC_PRINTF(3, 4)
     198             : static void
     199           0 : log_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
     200           0 :     int len = 0;
     201             :     va_list ap;
     202           0 :     private_data_t *priv = NULL;
     203           0 :     char prefix[LINE_MAX] = { 0 };
     204           0 :     int offset = 0;
     205           0 :     char* buffer = NULL;
     206             : 
     207           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     208           0 :     priv = out->priv;
     209             : 
     210           0 :     for (GList* gIter = priv->prefixes->head; gIter; gIter = gIter->next) {
     211           0 :         if (strcmp(prefix, "") != 0) {
     212           0 :             offset += snprintf(prefix + offset, LINE_MAX - offset, ": %s", (char *)gIter->data);
     213             :         } else {
     214           0 :             offset = snprintf(prefix, LINE_MAX, "%s", (char *)gIter->data);
     215             :         }
     216             :     }
     217             : 
     218           0 :     va_start(ap, format);
     219           0 :     len = vasprintf(&buffer, format, ap);
     220           0 :     CRM_ASSERT(len >= 0);
     221           0 :     va_end(ap);
     222             : 
     223           0 :     if (strcmp(buffer, "") != 0) { /* We don't want empty messages */
     224           0 :         if ((name != NULL) && (strcmp(name, "") != 0)) {
     225           0 :             if (strcmp(prefix, "") != 0) {
     226           0 :                 logger(priv, "%s: %s: %s", prefix, name, buffer);
     227             :             } else {
     228           0 :                 logger(priv, "%s: %s", name, buffer);
     229             :             }
     230             :         } else {
     231           0 :             if (strcmp(prefix, "") != 0) {
     232           0 :                 logger(priv, "%s: %s", prefix, buffer);
     233             :             } else {
     234           0 :                 logger(priv, "%s", buffer);
     235             :             }
     236             :         }
     237             :     }
     238           0 :     free(buffer);
     239           0 : }
     240             : 
     241             : static void
     242           0 : log_end_list(pcmk__output_t *out) {
     243           0 :     private_data_t *priv = NULL;
     244             : 
     245           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     246           0 :     priv = out->priv;
     247             : 
     248           0 :     if (priv->prefixes == NULL) {
     249           0 :       return;
     250             :     }
     251           0 :     CRM_ASSERT(priv->prefixes->tail != NULL);
     252             : 
     253           0 :     free((char *)priv->prefixes->tail->data);
     254           0 :     g_queue_pop_tail(priv->prefixes);
     255             : }
     256             : 
     257             : G_GNUC_PRINTF(2, 3)
     258             : static int
     259           0 : log_info(pcmk__output_t *out, const char *format, ...)
     260             : {
     261             :     va_list ap;
     262           0 :     private_data_t *priv = NULL;
     263             : 
     264           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     265           0 :     priv = out->priv;
     266             : 
     267             :     /* Informational output does not get indented, to separate it from other
     268             :      * potentially indented list output.
     269             :      */
     270           0 :     va_start(ap, format);
     271           0 :     logger_va(priv, priv->log_level, format, ap);
     272           0 :     va_end(ap);
     273             : 
     274           0 :     return pcmk_rc_ok;
     275             : }
     276             : 
     277             : G_GNUC_PRINTF(2, 3)
     278             : static int
     279           0 : log_transient(pcmk__output_t *out, const char *format, ...)
     280             : {
     281             :     va_list ap;
     282           0 :     private_data_t *priv = NULL;
     283             : 
     284           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     285           0 :     priv = out->priv;
     286             : 
     287           0 :     va_start(ap, format);
     288           0 :     logger_va(priv, QB_MAX(priv->log_level, LOG_DEBUG), format, ap);
     289           0 :     va_end(ap);
     290             : 
     291           0 :     return pcmk_rc_ok;
     292             : }
     293             : 
     294             : static bool
     295           0 : log_is_quiet(pcmk__output_t *out) {
     296           0 :     return false;
     297             : }
     298             : 
     299             : static void
     300           0 : log_spacer(pcmk__output_t *out) {
     301             :     /* This function intentionally left blank */
     302           0 : }
     303             : 
     304             : static void
     305           0 : log_progress(pcmk__output_t *out, bool end) {
     306             :     /* This function intentionally left blank */
     307           0 : }
     308             : 
     309             : static void
     310           0 : log_prompt(const char *prompt, bool echo, char **dest) {
     311             :     /* This function intentionally left blank */
     312           0 : }
     313             : 
     314             : pcmk__output_t *
     315           0 : pcmk__mk_log_output(char **argv) {
     316           0 :     pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
     317             : 
     318           0 :     if (retval == NULL) {
     319           0 :         return NULL;
     320             :     }
     321             : 
     322           0 :     retval->fmt_name = "log";
     323           0 :     retval->request = pcmk__quote_cmdline(argv);
     324             : 
     325           0 :     retval->init = log_init;
     326           0 :     retval->free_priv = log_free_priv;
     327           0 :     retval->finish = log_finish;
     328           0 :     retval->reset = log_reset;
     329             : 
     330           0 :     retval->register_message = pcmk__register_message;
     331           0 :     retval->message = pcmk__call_message;
     332             : 
     333           0 :     retval->subprocess_output = log_subprocess_output;
     334           0 :     retval->version = log_version;
     335           0 :     retval->info = log_info;
     336           0 :     retval->transient = log_transient;
     337           0 :     retval->err = log_err;
     338           0 :     retval->output_xml = log_output_xml;
     339             : 
     340           0 :     retval->begin_list = log_begin_list;
     341           0 :     retval->list_item = log_list_item;
     342           0 :     retval->end_list = log_end_list;
     343             : 
     344           0 :     retval->is_quiet = log_is_quiet;
     345           0 :     retval->spacer = log_spacer;
     346           0 :     retval->progress = log_progress;
     347           0 :     retval->prompt = log_prompt;
     348             : 
     349           0 :     return retval;
     350             : }
     351             : 
     352             : /*!
     353             :  * \internal
     354             :  * \brief Get the log level for a log output object
     355             :  *
     356             :  * This returns 0 if the output object is not of log format.
     357             :  *
     358             :  * \param[in] out  Output object
     359             :  *
     360             :  * \return Current log level for \p out
     361             :  */
     362             : uint8_t
     363           0 : pcmk__output_get_log_level(const pcmk__output_t *out)
     364             : {
     365           0 :     CRM_ASSERT(out != NULL);
     366             : 
     367           0 :     if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) {
     368           0 :         private_data_t *priv = out->priv;
     369             : 
     370           0 :         CRM_ASSERT(priv != NULL);
     371           0 :         return priv->log_level;
     372             :     }
     373           0 :     return 0;
     374             : }
     375             : 
     376             : /*!
     377             :  * \internal
     378             :  * \brief Set the log level for a log output object
     379             :  *
     380             :  * This does nothing if the output object is not of log format.
     381             :  *
     382             :  * \param[in,out] out        Output object
     383             :  * \param[in]     log_level  Log level constant (\c LOG_ERR, etc.) to use
     384             :  *
     385             :  * \note \c LOG_INFO is used by default for new \c pcmk__output_t objects.
     386             :  * \note Almost all formatted output messages respect this setting. However,
     387             :  *       <tt>out->err</tt> always logs at \c LOG_ERR.
     388             :  */
     389             : void
     390           0 : pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level)
     391             : {
     392           0 :     CRM_ASSERT(out != NULL);
     393             : 
     394           0 :     if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) {
     395           0 :         private_data_t *priv = out->priv;
     396             : 
     397           0 :         CRM_ASSERT(priv != NULL);
     398           0 :         priv->log_level = log_level;
     399             :     }
     400           0 : }
     401             : 
     402             : /*!
     403             :  * \internal
     404             :  * \brief Set the file, function, line, and tags used to filter log output
     405             :  *
     406             :  * This does nothing if the output object is not of log format.
     407             :  *
     408             :  * \param[in,out] out       Output object
     409             :  * \param[in]     file      File name to filter with (or NULL for default)
     410             :  * \param[in]     function  Function name to filter with (or NULL for default)
     411             :  * \param[in]     line      Line number to filter with (or 0 for default)
     412             :  * \param[in]     tags      Tags to filter with (or 0 for none)
     413             :  *
     414             :  * \note Custom filters should generally be used only in short areas of a single
     415             :  *       function. When done, callers should call this function again with
     416             :  *       NULL/0 arguments to reset the filters.
     417             :  */
     418             : void
     419           0 : pcmk__output_set_log_filter(pcmk__output_t *out, const char *file,
     420             :                             const char *function, uint32_t line, uint32_t tags)
     421             : {
     422           0 :     CRM_ASSERT(out != NULL);
     423             : 
     424           0 :     if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) {
     425           0 :         private_data_t *priv = out->priv;
     426             : 
     427           0 :         CRM_ASSERT(priv != NULL);
     428           0 :         priv->file = file;
     429           0 :         priv->function = function;
     430           0 :         priv->line = line;
     431           0 :         priv->tags = tags;
     432             :     }
     433           0 : }

Generated by: LCOV version 1.14