diff --git a/lib/common/output_html.c b/lib/common/output_html.c index d17f7a8b81..332578e652 100644 --- a/lib/common/output_html.c +++ b/lib/common/output_html.c @@ -1,456 +1,451 @@ /* * Copyright 2019-2021 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. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif +#include #include #include #include #include #include -#include -#include #include -#include static const char *stylesheet_default = ".bold { font-weight: bold }\n" ".maint { color: blue }\n" ".offline { color: red }\n" ".online { color: green }\n" ".rsc-failed { color: red }\n" ".rsc-failure-ignored { color: yellow }\n" ".rsc-managed { color: yellow }\n" ".rsc-multiple { color: orange }\n" ".rsc-ok { color: green }\n" ".standby { color: orange }\n" ".warning { color: red, font-weight: bold }"; static gboolean cgi_output = FALSE; static char *stylesheet_link = NULL; static char *title = NULL; static GSList *extra_headers = NULL; GOptionEntry pcmk__html_output_entries[] = { { "html-cgi", 0, 0, G_OPTION_ARG_NONE, &cgi_output, "Add CGI headers (requires --output-as=html)", NULL }, { "html-stylesheet", 0, 0, G_OPTION_ARG_STRING, &stylesheet_link, "Link to an external stylesheet (requires --output-as=html)", "URI" }, { "html-title", 0, 0, G_OPTION_ARG_STRING, &title, "Specify a page title (requires --output-as=html)", "TITLE" }, { NULL } }; typedef struct private_data_s { xmlNode *root; GQueue *parent_q; GSList *errors; } private_data_t; static void html_free_priv(pcmk__output_t *out) { private_data_t *priv = out->priv; if (priv == NULL) { return; } xmlFreeNode(priv->root); g_queue_free(priv->parent_q); g_slist_free(priv->errors); free(priv); out->priv = NULL; } static bool html_init(pcmk__output_t *out) { private_data_t *priv = NULL; /* If html_init was previously called on this output struct, just return. */ if (out->priv != NULL) { return true; } else { out->priv = calloc(1, sizeof(private_data_t)); if (out->priv == NULL) { return false; } priv = out->priv; } priv->parent_q = g_queue_new(); priv->root = create_xml_node(NULL, "html"); xmlCreateIntSubset(priv->root->doc, (pcmkXmlStr) "html", NULL, NULL); crm_xml_add(priv->root, "lang", "en"); g_queue_push_tail(priv->parent_q, priv->root); priv->errors = NULL; pcmk__output_xml_create_parent(out, "body", NULL); return true; } static void add_error_node(gpointer data, gpointer user_data) { char *str = (char *) data; pcmk__output_t *out = (pcmk__output_t *) user_data; out->list_item(out, NULL, "%s", str); } static void html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) { private_data_t *priv = out->priv; htmlNodePtr head_node = NULL; htmlNodePtr charset_node = NULL; /* If root is NULL, html_init failed and we are being called from pcmk__output_free * in the pcmk__output_new path. */ if (priv == NULL || priv->root == NULL) { return; } if (cgi_output && print) { fprintf(out->dest, "Content-Type: text/html\n\n"); } /* Add the head node last - it's not needed earlier because it doesn't contain * anything else that the user could add, and we want it done last to pick up * any options that may have been given. */ head_node = xmlNewNode(NULL, (pcmkXmlStr) "head"); if (title != NULL ) { pcmk_create_xml_text_node(head_node, "title", title); } else if (out->request != NULL) { pcmk_create_xml_text_node(head_node, "title", out->request); } charset_node = create_xml_node(head_node, "meta"); crm_xml_add(charset_node, "charset", "utf-8"); /* Add any extra header nodes the caller might have created. */ for (int i = 0; i < g_slist_length(extra_headers); i++) { xmlAddChild(head_node, xmlCopyNode(g_slist_nth_data(extra_headers, i), 1)); } /* Stylesheets are included two different ways. The first is via a built-in * default (see the stylesheet_default const above). The second is via the * html-stylesheet option, and this should obviously be a link to a * stylesheet. The second can override the first. At least one should be * given. */ pcmk_create_xml_text_node(head_node, "style", stylesheet_default); if (stylesheet_link != NULL) { htmlNodePtr link_node = create_xml_node(head_node, "link"); pcmk__xe_set_props(link_node, "rel", "stylesheet", "href", stylesheet_link, NULL); } xmlAddPrevSibling(priv->root->children, head_node); if (g_slist_length(priv->errors) > 0) { out->begin_list(out, "Errors", NULL, NULL); g_slist_foreach(priv->errors, add_error_node, (gpointer) out); out->end_list(out); } if (print) { htmlDocDump(out->dest, priv->root->doc); } if (copy_dest != NULL) { *copy_dest = copy_xml(priv->root); } g_slist_free_full(extra_headers, (GDestroyNotify) xmlFreeNode); extra_headers = NULL; } static void html_reset(pcmk__output_t *out) { CRM_ASSERT(out != NULL); out->dest = freopen(NULL, "w", out->dest); CRM_ASSERT(out->dest != NULL); html_free_priv(out); html_init(out); } static void html_subprocess_output(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr) { char *rc_buf = NULL; CRM_ASSERT(out != NULL); rc_buf = crm_strdup_printf("Return code: %d", exit_status); pcmk__output_create_xml_text_node(out, "h2", "Command Output"); pcmk__output_create_html_node(out, "div", NULL, NULL, rc_buf); if (proc_stdout != NULL) { pcmk__output_create_html_node(out, "div", NULL, NULL, "Stdout"); pcmk__output_create_html_node(out, "div", NULL, "output", proc_stdout); } if (proc_stderr != NULL) { pcmk__output_create_html_node(out, "div", NULL, NULL, "Stderr"); pcmk__output_create_html_node(out, "div", NULL, "output", proc_stderr); } free(rc_buf); } static void html_version(pcmk__output_t *out, bool extended) { CRM_ASSERT(out != NULL); pcmk__output_create_xml_text_node(out, "h2", "Version Information"); pcmk__output_create_html_node(out, "div", NULL, NULL, "Program: Pacemaker"); pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Version: %s", PACEMAKER_VERSION)); pcmk__output_create_html_node(out, "div", NULL, NULL, "Author: Andrew Beekhof"); pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Build: %s", BUILD_VERSION)); pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Features: %s", CRM_FEATURES)); } G_GNUC_PRINTF(2, 3) static void html_err(pcmk__output_t *out, const char *format, ...) { private_data_t *priv = NULL; int len = 0; char *buf = NULL; va_list ap; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len >= 0); va_end(ap); priv->errors = g_slist_append(priv->errors, buf); } G_GNUC_PRINTF(2, 3) static void html_info(pcmk__output_t *out, const char *format, ...) { /* This function intentially left blank */ } static void html_output_xml(pcmk__output_t *out, const char *name, const char *buf) { htmlNodePtr node = NULL; CRM_ASSERT(out != NULL); node = pcmk__output_create_html_node(out, "pre", NULL, NULL, buf); crm_xml_add(node, "lang", "xml"); } G_GNUC_PRINTF(4, 5) static void html_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format, ...) { int q_len = 0; private_data_t *priv = NULL; xmlNodePtr node = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; /* If we are already in a list (the queue depth is always at least * one because of the element), first create a
  • element * to hold the

    and the new list. */ q_len = g_queue_get_length(priv->parent_q); if (q_len > 2) { pcmk__output_xml_create_parent(out, "li", NULL); } if (format != NULL) { va_list ap; char *buf = NULL; int len; va_start(ap, format); len = vasprintf(&buf, format, ap); va_end(ap); CRM_ASSERT(len >= 0); if (q_len > 2) { pcmk__output_create_xml_text_node(out, "h3", buf); } else { pcmk__output_create_xml_text_node(out, "h2", buf); } free(buf); } node = pcmk__output_xml_create_parent(out, "ul", NULL); g_queue_push_tail(priv->parent_q, node); } G_GNUC_PRINTF(3, 4) static void html_list_item(pcmk__output_t *out, const char *name, const char *format, ...) { htmlNodePtr item_node = NULL; va_list ap; char *buf = NULL; int len; CRM_ASSERT(out != NULL); va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len >= 0); va_end(ap); item_node = pcmk__output_create_xml_text_node(out, "li", buf); free(buf); if (name != NULL) { crm_xml_add(item_node, "class", name); } } static void html_increment_list(pcmk__output_t *out) { /* This function intentially left blank */ } static void html_end_list(pcmk__output_t *out) { private_data_t *priv = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; /* Remove the
      tag. */ g_queue_pop_tail(priv->parent_q); pcmk__output_xml_pop_parent(out); /* Remove the
    • created for nested lists. */ if (g_queue_get_length(priv->parent_q) > 2) { pcmk__output_xml_pop_parent(out); } } static bool html_is_quiet(pcmk__output_t *out) { return false; } static void html_spacer(pcmk__output_t *out) { CRM_ASSERT(out != NULL); pcmk__output_create_xml_node(out, "br", NULL); } static void html_progress(pcmk__output_t *out, bool end) { /* This function intentially left blank */ } pcmk__output_t * pcmk__mk_html_output(char **argv) { pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t)); if (retval == NULL) { return NULL; } retval->fmt_name = "html"; retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv); retval->init = html_init; retval->free_priv = html_free_priv; retval->finish = html_finish; retval->reset = html_reset; retval->register_message = pcmk__register_message; retval->message = pcmk__call_message; retval->subprocess_output = html_subprocess_output; retval->version = html_version; retval->info = html_info; retval->err = html_err; retval->output_xml = html_output_xml; retval->begin_list = html_begin_list; retval->list_item = html_list_item; retval->increment_list = html_increment_list; retval->end_list = html_end_list; retval->is_quiet = html_is_quiet; retval->spacer = html_spacer; retval->progress = html_progress; retval->prompt = pcmk__text_prompt; return retval; } xmlNodePtr pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id, const char *class_name, const char *text) { htmlNodePtr node = NULL; CRM_ASSERT(out != NULL); node = pcmk__output_create_xml_text_node(out, element_name, text); if (class_name != NULL) { crm_xml_add(node, "class", class_name); } if (id != NULL) { crm_xml_add(node, "id", id); } return node; } void pcmk__html_add_header(const char *name, ...) { htmlNodePtr header_node; va_list ap; va_start(ap, name); header_node = xmlNewNode(NULL, (pcmkXmlStr) name); while (1) { char *key = va_arg(ap, char *); char *value; if (key == NULL) { break; } value = va_arg(ap, char *); crm_xml_add(header_node, key, value); } extra_headers = g_slist_append(extra_headers, header_node); va_end(ap); } diff --git a/lib/common/output_log.c b/lib/common/output_log.c index 4e216241ba..06029f9ce9 100644 --- a/lib/common/output_log.c +++ b/lib/common/output_log.c @@ -1,320 +1,313 @@ /* * Copyright 2019-2021 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. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif + +#include #include -#include #include #include #include -#include -#include -#include -#include - GOptionEntry pcmk__log_output_entries[] = { { NULL } }; typedef struct private_data_s { /* gathered in log_begin_list */ GQueue/**/ *prefixes; int 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 = out->priv; if (priv == NULL) { return; } g_queue_free(priv->prefixes); free(priv); out->priv = NULL; } static bool log_init(pcmk__output_t *out) { private_data_t *priv = 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"); } } 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 void 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); } 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 = argv == NULL ? NULL : g_strjoinv(" ", 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->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; } void pcmk__output_set_log_level(pcmk__output_t *out, int log_level) { private_data_t *priv = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); if (!pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) { return; } priv = out->priv; priv->log_level = log_level; } diff --git a/lib/common/output_none.c b/lib/common/output_none.c index 214a67989f..f5aab5446c 100644 --- a/lib/common/output_none.c +++ b/lib/common/output_none.c @@ -1,149 +1,150 @@ /* * Copyright 2019-2021 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 + #include #include #include -#include GOptionEntry pcmk__none_output_entries[] = { { NULL } }; static void none_free_priv(pcmk__output_t *out) { /* This function intentionally left blank */ } static bool none_init(pcmk__output_t *out) { return true; } static void none_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) { /* This function intentionally left blank */ } static void none_reset(pcmk__output_t *out) { CRM_ASSERT(out != NULL); none_free_priv(out); none_init(out); } static void none_subprocess_output(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr) { /* This function intentionally left blank */ } static void none_version(pcmk__output_t *out, bool extended) { /* This function intentionally left blank */ } G_GNUC_PRINTF(2, 3) static void none_err(pcmk__output_t *out, const char *format, ...) { /* This function intentionally left blank */ } G_GNUC_PRINTF(2, 3) static void none_info(pcmk__output_t *out, const char *format, ...) { /* This function intentionally left blank */ } static void none_output_xml(pcmk__output_t *out, const char *name, const char *buf) { /* This function intentionally left blank */ } G_GNUC_PRINTF(4, 5) static void none_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format, ...) { /* This function intentionally left blank */ } G_GNUC_PRINTF(3, 4) static void none_list_item(pcmk__output_t *out, const char *id, const char *format, ...) { /* This function intentionally left blank */ } static void none_increment_list(pcmk__output_t *out) { /* This function intentionally left blank */ } static void none_end_list(pcmk__output_t *out) { /* This function intentionally left blank */ } static bool none_is_quiet(pcmk__output_t *out) { return out->quiet; } static void none_spacer(pcmk__output_t *out) { /* This function intentionally left blank */ } static void none_progress(pcmk__output_t *out, bool end) { /* This function intentionally left blank */ } static void none_prompt(const char *prompt, bool echo, char **dest) { /* This function intentionally left blank */ } pcmk__output_t * pcmk__mk_none_output(char **argv) { pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t)); if (retval == NULL) { return NULL; } retval->fmt_name = "none"; retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv); retval->init = none_init; retval->free_priv = none_free_priv; retval->finish = none_finish; retval->reset = none_reset; retval->register_message = pcmk__register_message; retval->message = pcmk__call_message; retval->subprocess_output = none_subprocess_output; retval->version = none_version; retval->info = none_info; retval->err = none_err; retval->output_xml = none_output_xml; retval->begin_list = none_begin_list; retval->list_item = none_list_item; retval->increment_list = none_increment_list; retval->end_list = none_end_list; retval->is_quiet = none_is_quiet; retval->spacer = none_spacer; retval->progress = none_progress; retval->prompt = none_prompt; return retval; } diff --git a/lib/common/output_text.c b/lib/common/output_text.c index e53ea8a838..40bc88823a 100644 --- a/lib/common/output_text.c +++ b/lib/common/output_text.c @@ -1,429 +1,432 @@ /* * Copyright 2019-2021 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 #include #include #include -#include -#include +#include static gboolean fancy = FALSE; GOptionEntry pcmk__text_output_entries[] = { { "text-fancy", 0, 0, G_OPTION_ARG_NONE, &fancy, "Use more highly formatted output (requires --output-as=text)", NULL }, { NULL } }; typedef struct text_list_data_s { unsigned int len; char *singular_noun; char *plural_noun; } text_list_data_t; typedef struct private_data_s { GQueue *parent_q; } private_data_t; static void text_free_priv(pcmk__output_t *out) { private_data_t *priv = out->priv; if (priv == NULL) { return; } g_queue_free(priv->parent_q); free(priv); out->priv = NULL; } static bool text_init(pcmk__output_t *out) { private_data_t *priv = NULL; /* If text_init was previously called on this output struct, just return. */ if (out->priv != NULL) { return true; } else { out->priv = calloc(1, sizeof(private_data_t)); if (out->priv == NULL) { return false; } priv = out->priv; } priv->parent_q = g_queue_new(); return true; } static void text_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) { fflush(out->dest); } static void text_reset(pcmk__output_t *out) { CRM_ASSERT(out != NULL); if (out->dest != stdout) { out->dest = freopen(NULL, "w", out->dest); } CRM_ASSERT(out->dest != NULL); text_free_priv(out); text_init(out); } static void text_subprocess_output(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr) { CRM_ASSERT(out != NULL); if (proc_stdout != NULL) { fprintf(out->dest, "%s\n", proc_stdout); } if (proc_stderr != NULL) { fprintf(out->dest, "%s\n", proc_stderr); } } static void text_version(pcmk__output_t *out, bool extended) { CRM_ASSERT(out != NULL); if (extended) { fprintf(out->dest, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES); } else { fprintf(out->dest, "Pacemaker %s\n", PACEMAKER_VERSION); fprintf(out->dest, "Written by Andrew Beekhof\n"); } } G_GNUC_PRINTF(2, 3) static void text_err(pcmk__output_t *out, const char *format, ...) { va_list ap; 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 = vfprintf(stderr, format, ap); CRM_ASSERT(len >= 0); va_end(ap); /* Add a newline. */ fprintf(stderr, "\n"); } G_GNUC_PRINTF(2, 3) static void text_info(pcmk__output_t *out, const char *format, ...) { va_list ap; int len = 0; CRM_ASSERT(out != NULL); if (out->is_quiet(out)) { return; } va_start(ap, format); /* Informational output does not get indented, to separate it from other * potentially indented list output. */ len = vfprintf(out->dest, format, ap); CRM_ASSERT(len >= 0); va_end(ap); /* Add a newline. */ fprintf(out->dest, "\n"); } static void text_output_xml(pcmk__output_t *out, const char *name, const char *buf) { CRM_ASSERT(out != NULL); pcmk__indented_printf(out, "%s", buf); } G_GNUC_PRINTF(4, 5) static void text_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format, ...) { private_data_t *priv = NULL; text_list_data_t *new_list = NULL; va_list ap; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; va_start(ap, format); if (fancy && format) { pcmk__indented_vprintf(out, format, ap); fprintf(out->dest, ":\n"); } va_end(ap); new_list = calloc(1, sizeof(text_list_data_t)); new_list->len = 0; new_list->singular_noun = singular_noun == NULL ? NULL : strdup(singular_noun); new_list->plural_noun = plural_noun == NULL ? NULL : strdup(plural_noun); g_queue_push_tail(priv->parent_q, new_list); } G_GNUC_PRINTF(3, 4) static void text_list_item(pcmk__output_t *out, const char *id, const char *format, ...) { va_list ap; CRM_ASSERT(out != NULL); va_start(ap, format); if (fancy) { if (id != NULL) { /* Not really a good way to do this all in one call, so make it two. * The first handles the indentation and list styling. The second * just prints right after that one. */ pcmk__indented_printf(out, "%s: ", id); vfprintf(out->dest, format, ap); } else { pcmk__indented_vprintf(out, format, ap); } } else { pcmk__indented_vprintf(out, format, ap); } fputc('\n', out->dest); fflush(out->dest); va_end(ap); out->increment_list(out); } static void text_increment_list(pcmk__output_t *out) { private_data_t *priv = NULL; gpointer tail; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; tail = g_queue_peek_tail(priv->parent_q); CRM_ASSERT(tail != NULL); ((text_list_data_t *) tail)->len++; } static void text_end_list(pcmk__output_t *out) { private_data_t *priv = NULL; text_list_data_t *node = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; node = g_queue_pop_tail(priv->parent_q); if (node->singular_noun != NULL && node->plural_noun != NULL) { if (node->len == 1) { pcmk__indented_printf(out, "%d %s found\n", node->len, node->singular_noun); } else { pcmk__indented_printf(out, "%d %s found\n", node->len, node->plural_noun); } } free(node); } static bool text_is_quiet(pcmk__output_t *out) { CRM_ASSERT(out != NULL); return out->quiet; } static void text_spacer(pcmk__output_t *out) { CRM_ASSERT(out != NULL); fprintf(out->dest, "\n"); } static void text_progress(pcmk__output_t *out, bool end) { CRM_ASSERT(out != NULL); if (out->dest == stdout) { fprintf(out->dest, "."); if (end) { fprintf(out->dest, "\n"); } } } pcmk__output_t * pcmk__mk_text_output(char **argv) { pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t)); if (retval == NULL) { return NULL; } retval->fmt_name = "text"; retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv); retval->init = text_init; retval->free_priv = text_free_priv; retval->finish = text_finish; retval->reset = text_reset; retval->register_message = pcmk__register_message; retval->message = pcmk__call_message; retval->subprocess_output = text_subprocess_output; retval->version = text_version; retval->info = text_info; retval->err = text_err; retval->output_xml = text_output_xml; retval->begin_list = text_begin_list; retval->list_item = text_list_item; retval->increment_list = text_increment_list; retval->end_list = text_end_list; retval->is_quiet = text_is_quiet; retval->spacer = text_spacer; retval->progress = text_progress; retval->prompt = pcmk__text_prompt; return retval; } G_GNUC_PRINTF(2, 0) void pcmk__formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) { int len = 0; CRM_ASSERT(out != NULL); len = vfprintf(out->dest, format, args); CRM_ASSERT(len >= 0); } G_GNUC_PRINTF(2, 3) void pcmk__formatted_printf(pcmk__output_t *out, const char *format, ...) { va_list ap; CRM_ASSERT(out != NULL); va_start(ap, format); pcmk__formatted_vprintf(out, format, ap); va_end(ap); } G_GNUC_PRINTF(2, 0) void pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) { CRM_ASSERT(out != NULL); + if (!pcmk__str_eq(out->fmt_name, "text", pcmk__str_none)) { + return; + } + if (fancy) { int level = 0; private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); level = g_queue_get_length(priv->parent_q); for (int i = 0; i < level; i++) { fprintf(out->dest, " "); } if (level > 0) { fprintf(out->dest, "* "); } } pcmk__formatted_vprintf(out, format, args); } G_GNUC_PRINTF(2, 3) void pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) { va_list ap; CRM_ASSERT(out != NULL); va_start(ap, format); pcmk__indented_vprintf(out, format, ap); va_end(ap); } void pcmk__text_prompt(const char *prompt, bool echo, char **dest) { int rc = 0; struct termios settings; tcflag_t orig_c_lflag = 0; CRM_ASSERT(prompt != NULL); CRM_ASSERT(dest != NULL); if (!echo) { rc = tcgetattr(0, &settings); if (rc == 0) { orig_c_lflag = settings.c_lflag; settings.c_lflag &= ~ECHO; rc = tcsetattr(0, TCSANOW, &settings); } } if (rc == 0) { fprintf(stderr, "%s: ", prompt); if (*dest != NULL) { free(*dest); *dest = NULL; } #if SSCANF_HAS_M rc = scanf("%ms", dest); #else *dest = calloc(1, 1024); rc = scanf("%1023s", *dest); #endif fprintf(stderr, "\n"); } if (rc < 1) { free(*dest); *dest = NULL; } if (orig_c_lflag != 0) { settings.c_lflag = orig_c_lflag; rc = tcsetattr(0, TCSANOW, &settings); } } diff --git a/lib/common/output_xml.c b/lib/common/output_xml.c index de3a89f35b..ab4da37421 100644 --- a/lib/common/output_xml.c +++ b/lib/common/output_xml.c @@ -1,516 +1,522 @@ /* * Copyright 2019-2021 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. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif - -#ifndef PCMK__CONFIG_H -# define PCMK__CONFIG_H -# include -#endif +#include #include #include #include #include #include -#include #include -#include -#include static gboolean legacy_xml = FALSE; static gboolean simple_list = FALSE; static gboolean substitute = FALSE; GOptionEntry pcmk__xml_output_entries[] = { { "xml-legacy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &legacy_xml, NULL, NULL }, { "xml-simple-list", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &simple_list, NULL, NULL }, { "xml-substitute", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &substitute, NULL, NULL }, { NULL } }; typedef struct subst_s { const char *from; const char *to; } subst_t; static subst_t substitutions[] = { { "Active Resources", "resources" }, { "Full List of Resources", "resources" }, { "Inactive Resources", "resources" }, { "Cluster Summary", "summary" }, { "Failed Resource Actions", "failures" }, { "Fencing History", "fence_history" }, { "Migration Summary", "node_history" }, { "Operations", "node_history" }, { "Negative Location Constraints", "bans" }, { "Node Attributes", "node_attributes" }, { "Resource Config", "resource_config" }, { "Resource Operations", "operations" }, { NULL, NULL } }; typedef struct private_data_s { xmlNode *root; GQueue *parent_q; GSList *errors; bool legacy_xml; } private_data_t; static void xml_free_priv(pcmk__output_t *out) { private_data_t *priv = out->priv; if (priv == NULL) { return; } free_xml(priv->root); g_queue_free(priv->parent_q); g_slist_free(priv->errors); free(priv); out->priv = NULL; } static bool xml_init(pcmk__output_t *out) { private_data_t *priv = NULL; /* If xml_init was previously called on this output struct, just return. */ if (out->priv != NULL) { return true; } else { out->priv = calloc(1, sizeof(private_data_t)); if (out->priv == NULL) { return false; } priv = out->priv; } if (legacy_xml) { priv->root = create_xml_node(NULL, "crm_mon"); crm_xml_add(priv->root, "version", PACEMAKER_VERSION); } else { priv->root = create_xml_node(NULL, "pacemaker-result"); crm_xml_add(priv->root, "api-version", PCMK__API_VERSION); if (out->request != NULL) { crm_xml_add(priv->root, "request", out->request); } } priv->parent_q = g_queue_new(); priv->errors = NULL; g_queue_push_tail(priv->parent_q, priv->root); /* Copy this from the file-level variable. This means that it is only settable * as a command line option, and that pcmk__output_new must be called after all * command line processing is completed. */ priv->legacy_xml = legacy_xml; return true; } static void add_error_node(gpointer data, gpointer user_data) { char *str = (char *) data; xmlNodePtr node = (xmlNodePtr) user_data; pcmk_create_xml_text_node(node, "error", str); } static void xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) { private_data_t *priv = NULL; xmlNodePtr node; CRM_ASSERT(out != NULL); priv = out->priv; /* If root is NULL, xml_init failed and we are being called from pcmk__output_free * in the pcmk__output_new path. */ if (priv == NULL || priv->root == NULL) { return; } if (legacy_xml) { GSList *node = priv->errors; if (exit_status != CRM_EX_OK) { fprintf(stderr, "%s\n", crm_exit_str(exit_status)); } while (node != NULL) { fprintf(stderr, "%s\n", (char *) node->data); node = node->next; } } else { char *rc_as_str = crm_itoa(exit_status); node = create_xml_node(priv->root, "status"); pcmk__xe_set_props(node, "code", rc_as_str, "message", crm_exit_str(exit_status), NULL); if (g_slist_length(priv->errors) > 0) { xmlNodePtr errors_node = create_xml_node(node, "errors"); g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node); } free(rc_as_str); } if (print) { char *buf = dump_xml_formatted_with_text(priv->root); fprintf(out->dest, "%s", buf); fflush(out->dest); free(buf); } if (copy_dest != NULL) { *copy_dest = copy_xml(priv->root); } } static void xml_reset(pcmk__output_t *out) { CRM_ASSERT(out != NULL); out->dest = freopen(NULL, "w", out->dest); CRM_ASSERT(out->dest != NULL); xml_free_priv(out); xml_init(out); } static void xml_subprocess_output(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr) { xmlNodePtr node, child_node; char *rc_as_str = NULL; CRM_ASSERT(out != NULL); rc_as_str = crm_itoa(exit_status); node = pcmk__output_xml_create_parent(out, "command", "code", rc_as_str, NULL); if (proc_stdout != NULL) { child_node = pcmk_create_xml_text_node(node, "output", proc_stdout); crm_xml_add(child_node, "source", "stdout"); } if (proc_stderr != NULL) { child_node = pcmk_create_xml_text_node(node, "output", proc_stderr); crm_xml_add(child_node, "source", "stderr"); } pcmk__output_xml_add_node(out, node); free(rc_as_str); } static void xml_version(pcmk__output_t *out, bool extended) { CRM_ASSERT(out != NULL); pcmk__output_create_xml_node(out, "version", "program", "Pacemaker", "version", PACEMAKER_VERSION, "author", "Andrew Beekhof", "build", BUILD_VERSION, "features", CRM_FEATURES, NULL); } G_GNUC_PRINTF(2, 3) static void xml_err(pcmk__output_t *out, const char *format, ...) { private_data_t *priv = NULL; int len = 0; char *buf = NULL; va_list ap; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len > 0); va_end(ap); priv->errors = g_slist_append(priv->errors, buf); } G_GNUC_PRINTF(2, 3) static void xml_info(pcmk__output_t *out, const char *format, ...) { /* This function intentially left blank */ } static void xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) { xmlNodePtr parent = NULL; xmlNodePtr cdata_node = NULL; CRM_ASSERT(out != NULL); parent = pcmk__output_create_xml_node(out, name, NULL); cdata_node = xmlNewCDataBlock(getDocPtr(parent), (pcmkXmlStr) buf, strlen(buf)); xmlAddChild(parent, cdata_node); } G_GNUC_PRINTF(4, 5) static void xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format, ...) { va_list ap; char *name = NULL; char *buf = NULL; int len; CRM_ASSERT(out != NULL); va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len >= 0); va_end(ap); if (substitute) { for (subst_t *s = substitutions; s->from != NULL; s++) { if (!strcmp(s->from, buf)) { name = g_strdup(s->to); break; } } } if (name == NULL) { name = g_ascii_strdown(buf, -1); } if (legacy_xml || simple_list) { pcmk__output_xml_create_parent(out, name, NULL); } else { pcmk__output_xml_create_parent(out, "list", "name", name, NULL); } g_free(name); free(buf); } G_GNUC_PRINTF(3, 4) static void xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) { xmlNodePtr item_node = NULL; va_list ap; char *buf = NULL; int len; CRM_ASSERT(out != NULL); va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len >= 0); va_end(ap); item_node = pcmk__output_create_xml_text_node(out, "item", buf); if (name != NULL) { crm_xml_add(item_node, "name", name); } free(buf); } static void xml_increment_list(pcmk__output_t *out) { /* This function intentially left blank */ } static void xml_end_list(pcmk__output_t *out) { private_data_t *priv = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; if (priv->legacy_xml || simple_list) { g_queue_pop_tail(priv->parent_q); } else { char *buf = NULL; xmlNodePtr node; node = g_queue_pop_tail(priv->parent_q); buf = crm_strdup_printf("%lu", xmlChildElementCount(node)); crm_xml_add(node, "count", buf); free(buf); } } static bool xml_is_quiet(pcmk__output_t *out) { return false; } static void xml_spacer(pcmk__output_t *out) { /* This function intentionally left blank */ } static void xml_progress(pcmk__output_t *out, bool end) { /* This function intentionally left blank */ } pcmk__output_t * pcmk__mk_xml_output(char **argv) { pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t)); if (retval == NULL) { return NULL; } retval->fmt_name = "xml"; retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv); retval->init = xml_init; retval->free_priv = xml_free_priv; retval->finish = xml_finish; retval->reset = xml_reset; retval->register_message = pcmk__register_message; retval->message = pcmk__call_message; retval->subprocess_output = xml_subprocess_output; retval->version = xml_version; retval->info = xml_info; retval->err = xml_err; retval->output_xml = xml_output_xml; retval->begin_list = xml_begin_list; retval->list_item = xml_list_item; retval->increment_list = xml_increment_list; retval->end_list = xml_end_list; retval->is_quiet = xml_is_quiet; retval->spacer = xml_spacer; retval->progress = xml_progress; retval->prompt = pcmk__text_prompt; return retval; } xmlNodePtr pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name, ...) { va_list args; xmlNodePtr node = NULL; CRM_ASSERT(out != NULL); node = pcmk__output_create_xml_node(out, name, NULL); va_start(args, name); pcmk__xe_set_propv(node, args); va_end(args); pcmk__output_xml_push_parent(out, node); return node; } void pcmk__output_xml_add_node(pcmk__output_t *out, xmlNodePtr node) { private_data_t *priv = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); CRM_ASSERT(node != NULL); + if (!pcmk__str_any_of(out->fmt_name, "xml", "html", NULL)) { + return; + } + priv = out->priv; xmlAddChild(g_queue_peek_tail(priv->parent_q), node); } xmlNodePtr pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) { xmlNodePtr node = NULL; private_data_t *priv = NULL; va_list args; CRM_ASSERT(out != NULL && out->priv != NULL); + CRM_ASSERT(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL)); + priv = out->priv; node = create_xml_node(g_queue_peek_tail(priv->parent_q), name); va_start(args, name); pcmk__xe_set_propv(node, args); va_end(args); return node; } xmlNodePtr pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) { xmlNodePtr node = NULL; CRM_ASSERT(out != NULL); node = pcmk__output_create_xml_node(out, name, NULL); xmlNodeSetContent(node, (pcmkXmlStr) content); return node; } void pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) { private_data_t *priv = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); CRM_ASSERT(parent != NULL); + if (!pcmk__str_any_of(out->fmt_name, "xml", "html", NULL)) { + return; + } + priv = out->priv; g_queue_push_tail(priv->parent_q, parent); } void pcmk__output_xml_pop_parent(pcmk__output_t *out) { private_data_t *priv = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); + if (!pcmk__str_any_of(out->fmt_name, "xml", "html", NULL)) { + return; + } + priv = out->priv; CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0); g_queue_pop_tail(priv->parent_q); } xmlNodePtr pcmk__output_xml_peek_parent(pcmk__output_t *out) { private_data_t *priv = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); + CRM_ASSERT(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL)); + priv = out->priv; /* If queue is empty NULL will be returned */ return g_queue_peek_tail(priv->parent_q); } diff --git a/tools/crm_mon_curses.c b/tools/crm_mon_curses.c index 0066c3322a..535a23db0e 100644 --- a/tools/crm_mon_curses.c +++ b/tools/crm_mon_curses.c @@ -1,516 +1,514 @@ /* * Copyright 2019-2021 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 #include #include #include -#include -#include #include #include #include #include #include "crm_mon.h" #if CURSES_ENABLED GOptionEntry crm_mon_curses_output_entries[] = { { NULL } }; typedef struct curses_list_data_s { unsigned int len; char *singular_noun; char *plural_noun; } curses_list_data_t; typedef struct private_data_s { GQueue *parent_q; } private_data_t; static void curses_free_priv(pcmk__output_t *out) { private_data_t *priv = out->priv; if (priv == NULL) { return; } g_queue_free(priv->parent_q); free(priv); out->priv = NULL; } static bool curses_init(pcmk__output_t *out) { private_data_t *priv = NULL; /* If curses_init was previously called on this output struct, just return. */ if (out->priv != NULL) { return true; } else { out->priv = calloc(1, sizeof(private_data_t)); if (out->priv == NULL) { return false; } priv = out->priv; } priv->parent_q = g_queue_new(); initscr(); cbreak(); noecho(); return true; } static void curses_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) { CRM_ASSERT(out != NULL); echo(); nocbreak(); endwin(); } static void curses_reset(pcmk__output_t *out) { CRM_ASSERT(out != NULL); curses_free_priv(out); curses_init(out); } static void curses_subprocess_output(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr) { CRM_ASSERT(out != NULL); if (proc_stdout != NULL) { printw("%s\n", proc_stdout); } if (proc_stderr != NULL) { printw("%s\n", proc_stderr); } clrtoeol(); refresh(); } /* curses_version is defined in curses.h, so we can't use that name here. * Note that this function prints out via text, not with curses. */ static void curses_ver(pcmk__output_t *out, bool extended) { CRM_ASSERT(out != NULL); if (extended) { printf("Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES); } else { printf("Pacemaker %s\n", PACEMAKER_VERSION); printf("Written by Andrew Beekhof\n"); } } G_GNUC_PRINTF(2, 3) static void curses_error(pcmk__output_t *out, const char *format, ...) { va_list ap; CRM_ASSERT(out != NULL); /* Informational output does not get indented, to separate it from other * potentially indented list output. */ va_start(ap, format); vw_printw(stdscr, format, ap); va_end(ap); /* Add a newline. */ addch('\n'); clrtoeol(); refresh(); sleep(2); } G_GNUC_PRINTF(2, 3) static void curses_info(pcmk__output_t *out, const char *format, ...) { va_list ap; CRM_ASSERT(out != NULL); if (out->is_quiet(out)) { return; } /* Informational output does not get indented, to separate it from other * potentially indented list output. */ va_start(ap, format); vw_printw(stdscr, format, ap); va_end(ap); /* Add a newline. */ addch('\n'); clrtoeol(); refresh(); } static void curses_output_xml(pcmk__output_t *out, const char *name, const char *buf) { CRM_ASSERT(out != NULL); curses_indented_printf(out, "%s", buf); } G_GNUC_PRINTF(4, 5) static void curses_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format, ...) { private_data_t *priv = NULL; curses_list_data_t *new_list = NULL; va_list ap; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; va_start(ap, format); curses_indented_vprintf(out, format, ap); printw(":\n"); va_end(ap); new_list = calloc(1, sizeof(curses_list_data_t)); new_list->len = 0; new_list->singular_noun = singular_noun == NULL ? NULL : strdup(singular_noun); new_list->plural_noun = plural_noun == NULL ? NULL : strdup(plural_noun); g_queue_push_tail(priv->parent_q, new_list); } G_GNUC_PRINTF(3, 4) static void curses_list_item(pcmk__output_t *out, const char *id, const char *format, ...) { va_list ap; CRM_ASSERT(out != NULL); va_start(ap, format); if (id != NULL) { curses_indented_printf(out, "%s: ", id); vw_printw(stdscr, format, ap); } else { curses_indented_vprintf(out, format, ap); } addch('\n'); va_end(ap); out->increment_list(out); } static void curses_increment_list(pcmk__output_t *out) { private_data_t *priv = NULL; gpointer tail; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; tail = g_queue_peek_tail(priv->parent_q); CRM_ASSERT(tail != NULL); ((curses_list_data_t *) tail)->len++; } static void curses_end_list(pcmk__output_t *out) { private_data_t *priv = NULL; curses_list_data_t *node = NULL; CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; node = g_queue_pop_tail(priv->parent_q); if (node->singular_noun != NULL && node->plural_noun != NULL) { if (node->len == 1) { curses_indented_printf(out, "%d %s found\n", node->len, node->singular_noun); } else { curses_indented_printf(out, "%d %s found\n", node->len, node->plural_noun); } } free(node); } static bool curses_is_quiet(pcmk__output_t *out) { CRM_ASSERT(out != NULL); return out->quiet; } static void curses_spacer(pcmk__output_t *out) { CRM_ASSERT(out != NULL); addch('\n'); } static void curses_progress(pcmk__output_t *out, bool end) { CRM_ASSERT(out != NULL); if (end) { printw(".\n"); } else { addch('.'); } } static void curses_prompt(const char *prompt, bool do_echo, char **dest) { int rc = OK; CRM_ASSERT(prompt != NULL); CRM_ASSERT(dest != NULL); /* This is backwards from the text version of this function on purpose. We * disable echo by default in curses_init, so we need to enable it here if * asked for. */ if (do_echo) { rc = echo(); } if (rc == OK) { printw("%s: ", prompt); if (*dest != NULL) { free(*dest); } *dest = calloc(1, 1024); /* On older systems, scanw is defined as taking a char * for its first argument, * while newer systems rightly want a const char *. Accomodate both here due * to building with -Werror. */ rc = scanw((NCURSES_CONST char *) "%1023s", *dest); addch('\n'); } if (rc < 1) { free(*dest); *dest = NULL; } if (do_echo) { noecho(); } } pcmk__output_t * crm_mon_mk_curses_output(char **argv) { pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t)); if (retval == NULL) { return NULL; } retval->fmt_name = "console"; retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv); retval->init = curses_init; retval->free_priv = curses_free_priv; retval->finish = curses_finish; retval->reset = curses_reset; retval->register_message = pcmk__register_message; retval->message = pcmk__call_message; retval->subprocess_output = curses_subprocess_output; retval->version = curses_ver; retval->err = curses_error; retval->info = curses_info; retval->output_xml = curses_output_xml; retval->begin_list = curses_begin_list; retval->list_item = curses_list_item; retval->increment_list = curses_increment_list; retval->end_list = curses_end_list; retval->is_quiet = curses_is_quiet; retval->spacer = curses_spacer; retval->progress = curses_progress; retval->prompt = curses_prompt; return retval; } G_GNUC_PRINTF(2, 0) void curses_formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) { vw_printw(stdscr, format, args); clrtoeol(); refresh(); } G_GNUC_PRINTF(2, 3) void curses_formatted_printf(pcmk__output_t *out, const char *format, ...) { va_list ap; va_start(ap, format); curses_formatted_vprintf(out, format, ap); va_end(ap); } G_GNUC_PRINTF(2, 0) void curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) { int level = 0; private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); level = g_queue_get_length(priv->parent_q); for (int i = 0; i < level; i++) { printw(" "); } if (level > 0) { printw("* "); } curses_formatted_vprintf(out, format, args); } G_GNUC_PRINTF(2, 3) void curses_indented_printf(pcmk__output_t *out, const char *format, ...) { va_list ap; va_start(ap, format); curses_indented_vprintf(out, format, ap); va_end(ap); } PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "gboolean", "gboolean") static int stonith_event_console(pcmk__output_t *out, va_list args) { stonith_history_t *event = va_arg(args, stonith_history_t *); gboolean full_history = va_arg(args, gboolean); gboolean later_succeeded = va_arg(args, gboolean); crm_time_t *crm_when = crm_time_new(NULL); char *buf = NULL; crm_time_set_timet(crm_when, &(event->completed)); buf = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone); switch (event->state) { case st_failed: curses_indented_printf(out, "%s of %s failed: delegate=%s, client=%s, origin=%s, %s='%s'%s\n", stonith_action_str(event->action), event->target, event->delegate ? event->delegate : "", event->client, event->origin, full_history ? "completed" : "last-failed", buf, later_succeeded ? " (a later attempt succeeded)" : ""); break; case st_done: curses_indented_printf(out, "%s of %s successful: delegate=%s, client=%s, origin=%s, %s='%s'\n", stonith_action_str(event->action), event->target, event->delegate ? event->delegate : "", event->client, event->origin, full_history ? "completed" : "last-successful", buf); break; default: curses_indented_printf(out, "%s of %s pending: client=%s, origin=%s\n", stonith_action_str(event->action), event->target, event->client, event->origin); break; } free(buf); crm_time_free(crm_when); return pcmk_rc_ok; } static pcmk__message_entry_t fmt_functions[] = { { "ban", "console", pe__ban_text }, { "bundle", "console", pe__bundle_text }, { "clone", "console", pe__clone_text }, { "cluster-counts", "console", pe__cluster_counts_text }, { "cluster-dc", "console", pe__cluster_dc_text }, { "cluster-options", "console", pe__cluster_options_text }, { "cluster-stack", "console", pe__cluster_stack_text }, { "cluster-summary", "console", pe__cluster_summary }, { "cluster-times", "console", pe__cluster_times_text }, { "failed-action", "console", pe__failed_action_text }, { "failed-fencing-list", "console", stonith__failed_history }, { "fencing-list", "console", stonith__history }, { "full-fencing-list", "console", stonith__full_history }, { "group", "console", pe__group_text }, { "maint-mode", "console", pe__cluster_maint_mode_text }, { "node", "console", pe__node_text }, { "node-attribute", "console", pe__node_attribute_text }, { "node-list", "console", pe__node_list_text }, { "op-history", "console", pe__op_history_text }, { "pending-fencing-list", "console", stonith__pending_actions }, { "primitive", "console", pe__resource_text }, { "resource-history", "console", pe__resource_history_text }, { "stonith-event", "console", stonith_event_console }, { "ticket", "console", pe__ticket_text }, { NULL, NULL, NULL } }; void crm_mon_register_messages(pcmk__output_t *out) { pcmk__register_messages(out, fmt_functions); } #else pcmk__output_t * crm_mon_mk_curses_output(char **argv) { /* curses was disabled in the build, so fall back to text. */ return pcmk__mk_text_output(argv); } G_GNUC_PRINTF(2, 0) void curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) { return; } G_GNUC_PRINTF(2, 3) void curses_indented_printf(pcmk__output_t *out, const char *format, ...) { return; } void crm_mon_register_messages(pcmk__output_t *out) { return; } #endif