summaryrefslogtreecommitdiffstats
path: root/lib/common/output_log.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common/output_log.c')
-rw-r--r--lib/common/output_log.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/lib/common/output_log.c b/lib/common/output_log.c
new file mode 100644
index 0000000..aca168d
--- /dev/null
+++ b/lib/common/output_log.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2019-2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+#include <crm/common/cmdline_internal.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+GOptionEntry pcmk__log_output_entries[] = {
+ { NULL }
+};
+
+typedef struct private_data_s {
+ /* gathered in log_begin_list */
+ GQueue/*<char*>*/ *prefixes;
+ uint8_t log_level;
+} private_data_t;
+
+static void
+log_subprocess_output(pcmk__output_t *out, int exit_status,
+ const char *proc_stdout, const char *proc_stderr) {
+ /* This function intentionally left blank */
+}
+
+static void
+log_free_priv(pcmk__output_t *out) {
+ private_data_t *priv = NULL;
+
+ if (out == NULL || out->priv == NULL) {
+ return;
+ }
+
+ priv = out->priv;
+
+ g_queue_free(priv->prefixes);
+ free(priv);
+ out->priv = NULL;
+}
+
+static bool
+log_init(pcmk__output_t *out) {
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT(out != NULL);
+
+ /* If log_init was previously called on this output struct, just return. */
+ if (out->priv != NULL) {
+ return true;
+ }
+
+ out->priv = calloc(1, sizeof(private_data_t));
+ if (out->priv == NULL) {
+ return false;
+ }
+
+ priv = out->priv;
+
+ priv->prefixes = g_queue_new();
+ priv->log_level = LOG_INFO;
+
+ return true;
+}
+
+static void
+log_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
+ /* This function intentionally left blank */
+}
+
+static void
+log_reset(pcmk__output_t *out) {
+ CRM_ASSERT(out != NULL);
+
+ out->dest = freopen(NULL, "w", out->dest);
+ CRM_ASSERT(out->dest != NULL);
+
+ log_free_priv(out);
+ log_init(out);
+}
+
+static void
+log_version(pcmk__output_t *out, bool extended) {
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ priv = out->priv;
+
+ if (extended) {
+ do_crm_log(priv->log_level, "Pacemaker %s (Build: %s): %s",
+ PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
+ } else {
+ do_crm_log(priv->log_level, "Pacemaker %s", PACEMAKER_VERSION);
+ do_crm_log(priv->log_level, "Written by Andrew Beekhof and"
+ "the Pacemaker project contributors");
+ }
+}
+
+G_GNUC_PRINTF(2, 3)
+static void
+log_err(pcmk__output_t *out, const char *format, ...) {
+ va_list ap;
+ char* buffer = NULL;
+ int len = 0;
+
+ CRM_ASSERT(out != NULL);
+
+ va_start(ap, format);
+ /* Informational output does not get indented, to separate it from other
+ * potentially indented list output.
+ */
+ len = vasprintf(&buffer, format, ap);
+ CRM_ASSERT(len >= 0);
+ va_end(ap);
+
+ crm_err("%s", buffer);
+
+ free(buffer);
+}
+
+static void
+log_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
+ xmlNodePtr node = NULL;
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ priv = out->priv;
+
+ node = create_xml_node(NULL, name);
+ xmlNodeSetContent(node, (pcmkXmlStr) buf);
+ do_crm_log_xml(priv->log_level, name, node);
+ free(node);
+}
+
+G_GNUC_PRINTF(4, 5)
+static void
+log_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
+ const char *format, ...) {
+ int len = 0;
+ va_list ap;
+ char* buffer = NULL;
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ priv = out->priv;
+
+ va_start(ap, format);
+ len = vasprintf(&buffer, format, ap);
+ CRM_ASSERT(len >= 0);
+ va_end(ap);
+
+ /* Don't skip empty prefixes,
+ * otherwise there will be mismatch
+ * in the log_end_list */
+ if(strcmp(buffer, "") == 0) {
+ /* nothing */
+ }
+
+ g_queue_push_tail(priv->prefixes, buffer);
+}
+
+G_GNUC_PRINTF(3, 4)
+static void
+log_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
+ int len = 0;
+ va_list ap;
+ private_data_t *priv = NULL;
+ char prefix[LINE_MAX] = { 0 };
+ int offset = 0;
+ char* buffer = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ priv = out->priv;
+
+ for (GList* gIter = priv->prefixes->head; gIter; gIter = gIter->next) {
+ if (strcmp(prefix, "") != 0) {
+ offset += snprintf(prefix + offset, LINE_MAX - offset, ": %s", (char *)gIter->data);
+ } else {
+ offset = snprintf(prefix, LINE_MAX, "%s", (char *)gIter->data);
+ }
+ }
+
+ va_start(ap, format);
+ len = vasprintf(&buffer, format, ap);
+ CRM_ASSERT(len >= 0);
+ va_end(ap);
+
+ if (strcmp(buffer, "") != 0) { /* We don't want empty messages */
+ if ((name != NULL) && (strcmp(name, "") != 0)) {
+ if (strcmp(prefix, "") != 0) {
+ do_crm_log(priv->log_level, "%s: %s: %s", prefix, name, buffer);
+ } else {
+ do_crm_log(priv->log_level, "%s: %s", name, buffer);
+ }
+ } else {
+ if (strcmp(prefix, "") != 0) {
+ do_crm_log(priv->log_level, "%s: %s", prefix, buffer);
+ } else {
+ do_crm_log(priv->log_level, "%s", buffer);
+ }
+ }
+ }
+ free(buffer);
+}
+
+static void
+log_end_list(pcmk__output_t *out) {
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ priv = out->priv;
+
+ if (priv->prefixes == NULL) {
+ return;
+ }
+ CRM_ASSERT(priv->prefixes->tail != NULL);
+
+ free((char *)priv->prefixes->tail->data);
+ g_queue_pop_tail(priv->prefixes);
+}
+
+G_GNUC_PRINTF(2, 3)
+static int
+log_info(pcmk__output_t *out, const char *format, ...) {
+ private_data_t *priv = NULL;
+ int len = 0;
+ va_list ap;
+ char* buffer = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ priv = out->priv;
+
+ va_start(ap, format);
+ len = vasprintf(&buffer, format, ap);
+ CRM_ASSERT(len >= 0);
+ va_end(ap);
+
+ do_crm_log(priv->log_level, "%s", buffer);
+
+ free(buffer);
+ return pcmk_rc_ok;
+}
+
+G_GNUC_PRINTF(2, 3)
+static int
+log_transient(pcmk__output_t *out, const char *format, ...)
+{
+ private_data_t *priv = NULL;
+ int len = 0;
+ va_list ap;
+ char *buffer = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ priv = out->priv;
+
+ va_start(ap, format);
+ len = vasprintf(&buffer, format, ap);
+ CRM_ASSERT(len >= 0);
+ va_end(ap);
+
+ do_crm_log(QB_MAX(priv->log_level, LOG_DEBUG), "%s", buffer);
+
+ free(buffer);
+ return pcmk_rc_ok;
+}
+
+static bool
+log_is_quiet(pcmk__output_t *out) {
+ return false;
+}
+
+static void
+log_spacer(pcmk__output_t *out) {
+ /* This function intentionally left blank */
+}
+
+static void
+log_progress(pcmk__output_t *out, bool end) {
+ /* This function intentionally left blank */
+}
+
+static void
+log_prompt(const char *prompt, bool echo, char **dest) {
+ /* This function intentionally left blank */
+}
+
+pcmk__output_t *
+pcmk__mk_log_output(char **argv) {
+ pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
+
+ if (retval == NULL) {
+ return NULL;
+ }
+
+ retval->fmt_name = "log";
+ retval->request = pcmk__quote_cmdline(argv);
+
+ retval->init = log_init;
+ retval->free_priv = log_free_priv;
+ retval->finish = log_finish;
+ retval->reset = log_reset;
+
+ retval->register_message = pcmk__register_message;
+ retval->message = pcmk__call_message;
+
+ retval->subprocess_output = log_subprocess_output;
+ retval->version = log_version;
+ retval->info = log_info;
+ retval->transient = log_transient;
+ retval->err = log_err;
+ retval->output_xml = log_output_xml;
+
+ retval->begin_list = log_begin_list;
+ retval->list_item = log_list_item;
+ retval->end_list = log_end_list;
+
+ retval->is_quiet = log_is_quiet;
+ retval->spacer = log_spacer;
+ retval->progress = log_progress;
+ retval->prompt = log_prompt;
+
+ return retval;
+}
+
+uint8_t
+pcmk__output_get_log_level(const pcmk__output_t *out)
+{
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT((out != NULL) && (out->priv != NULL));
+ CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return 0);
+
+ priv = out->priv;
+ return priv->log_level;
+}
+
+void
+pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level) {
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT(out != NULL && out->priv != NULL);
+ CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return);
+
+ priv = out->priv;
+ priv->log_level = log_level;
+}