diff options
Diffstat (limited to 'lib/common/output_xml.c')
-rw-r--r-- | lib/common/output_xml.c | 320 |
1 files changed, 209 insertions, 111 deletions
diff --git a/lib/common/output_xml.c b/lib/common/output_xml.c index ba61145..9b0d417 100644 --- a/lib/common/output_xml.c +++ b/lib/common/output_xml.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 the Pacemaker project contributors + * Copyright 2019-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,57 +14,59 @@ #include <stdlib.h> #include <stdio.h> #include <crm/crm.h> -#include <crm/common/output.h> -#include <crm/common/xml.h> -#include <crm/common/xml_internal.h> /* pcmk__xml2fd */ #include <glib.h> #include <crm/common/cmdline_internal.h> +#include <crm/common/output.h> #include <crm/common/xml.h> - -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 } -}; +#include <crm/common/xml_internal.h> // pcmk__xml2fd typedef struct subst_s { const char *from; const char *to; } subst_t; -static subst_t substitutions[] = { - { "Active Resources", "resources" }, - { "Assignment Scores", "allocations" }, - { "Assignment Scores and Utilization Information", "allocations_utilizations" }, - { "Cluster Summary", "summary" }, - { "Current cluster status", "cluster_status" }, - { "Executing Cluster Transition", "transition" }, - { "Failed Resource Actions", "failures" }, - { "Fencing History", "fence_history" }, - { "Full List of Resources", "resources" }, - { "Inactive Resources", "resources" }, - { "Migration Summary", "node_history" }, - { "Negative Location Constraints", "bans" }, - { "Node Attributes", "node_attributes" }, - { "Operations", "node_history" }, - { "Resource Config", "resource_config" }, - { "Resource Operations", "operations" }, - { "Revised Cluster Status", "revised_cluster_status" }, - { "Transition Summary", "actions" }, - { "Utilization Information", "utilizations" }, +static const subst_t substitutions[] = { + { "Active Resources", + PCMK_XE_RESOURCES, }, + { "Assignment Scores", + PCMK_XE_ALLOCATIONS, }, + { "Assignment Scores and Utilization Information", + PCMK_XE_ALLOCATIONS_UTILIZATIONS, }, + { "Cluster Summary", + PCMK_XE_SUMMARY, }, + { "Current cluster status", + PCMK_XE_CLUSTER_STATUS, }, + { "Executing Cluster Transition", + PCMK_XE_TRANSITION, }, + { "Failed Resource Actions", + PCMK_XE_FAILURES, }, + { "Fencing History", + PCMK_XE_FENCE_HISTORY, }, + { "Full List of Resources", + PCMK_XE_RESOURCES, }, + { "Inactive Resources", + PCMK_XE_RESOURCES, }, + { "Migration Summary", + PCMK_XE_NODE_HISTORY, }, + { "Negative Location Constraints", + PCMK_XE_BANS, }, + { "Node Attributes", + PCMK_XE_NODE_ATTRIBUTES, }, + { "Operations", + PCMK_XE_NODE_HISTORY, }, + { "Resource Config", + PCMK_XE_RESOURCE_CONFIG, }, + { "Resource Operations", + PCMK_XE_OPERATIONS, }, + { "Revised Cluster Status", + PCMK_XE_REVISED_CLUSTER_STATUS, }, + { "Timings", + PCMK_XE_TIMINGS, }, + { "Transition Summary", + PCMK_XE_ACTIONS, }, + { "Utilization Information", + PCMK_XE_UTILIZATIONS, }, { NULL, NULL } }; @@ -82,8 +84,46 @@ typedef struct private_data_s { GSList *errors; /* End members that must match the HTML version */ bool legacy_xml; + bool list_element; } private_data_t; +static bool +has_root_node(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + priv = out->priv; + return priv != NULL && priv->root != NULL; +} + +static void +add_root_node(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + /* has_root_node will assert if out is NULL, so no need to do it here */ + if (has_root_node(out)) { + return; + } + + priv = out->priv; + + if (priv->legacy_xml) { + priv->root = pcmk__xe_create(NULL, PCMK_XE_CRM_MON); + crm_xml_add(priv->root, PCMK_XA_VERSION, PACEMAKER_VERSION); + } else { + priv->root = pcmk__xe_create(NULL, PCMK_XE_PACEMAKER_RESULT); + crm_xml_add(priv->root, PCMK_XA_API_VERSION, PCMK__API_VERSION); + crm_xml_add(priv->root, PCMK_XA_REQUEST, + pcmk__s(out->request, "libpacemaker")); + } + + priv->parent_q = g_queue_new(); + g_queue_push_tail(priv->parent_q, priv->root); +} + static void xml_free_priv(pcmk__output_t *out) { private_data_t *priv = NULL; @@ -94,9 +134,16 @@ xml_free_priv(pcmk__output_t *out) { priv = out->priv; - free_xml(priv->root); - g_queue_free(priv->parent_q); - g_slist_free(priv->errors); + if (has_root_node(out)) { + free_xml(priv->root); + /* The elements of parent_q are xmlNodes that are a part of the + * priv->root document, so the above line already frees them. Don't + * call g_queue_free_full here. + */ + g_queue_free(priv->parent_q); + } + + g_slist_free_full(priv->errors, free); free(priv); out->priv = NULL; } @@ -119,36 +166,18 @@ xml_init(pcmk__output_t *out) { 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; + const char *str = (const char *) data; xmlNodePtr node = (xmlNodePtr) user_data; - pcmk_create_xml_text_node(node, "error", str); + + node = pcmk__xe_create(node, PCMK_XE_ERROR); + pcmk__xe_set_content(node, "%s", str); } static void @@ -159,14 +188,13 @@ xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_ 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) { + if (priv == NULL) { return; } - if (legacy_xml) { + add_root_node(out); + + if (priv->legacy_xml) { GSList *node = priv->errors; if (exit_status != CRM_EX_OK) { @@ -180,13 +208,14 @@ xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_ } else { char *rc_as_str = pcmk__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), + node = pcmk__xe_create(priv->root, PCMK_XE_STATUS); + pcmk__xe_set_props(node, + PCMK_XA_CODE, rc_as_str, + PCMK_XA_MESSAGE, crm_exit_str(exit_status), NULL); if (g_slist_length(priv->errors) > 0) { - xmlNodePtr errors_node = create_xml_node(node, "errors"); + xmlNodePtr errors_node = pcmk__xe_create(node, PCMK_XE_ERRORS); g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node); } @@ -198,7 +227,7 @@ xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_ } if (copy_dest != NULL) { - *copy_dest = copy_xml(priv->root); + *copy_dest = pcmk__xml_copy(NULL, priv->root); } } @@ -223,18 +252,20 @@ xml_subprocess_output(pcmk__output_t *out, int exit_status, rc_as_str = pcmk__itoa(exit_status); - node = pcmk__output_xml_create_parent(out, "command", - "code", rc_as_str, + node = pcmk__output_xml_create_parent(out, PCMK_XE_COMMAND, + PCMK_XA_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"); + child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT); + pcmk__xe_set_content(child_node, "%s", proc_stdout); + crm_xml_add(child_node, PCMK_XA_SOURCE, "stdout"); } if (proc_stderr != NULL) { - child_node = pcmk_create_xml_text_node(node, "output", proc_stderr); - crm_xml_add(child_node, "source", "stderr"); + child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT); + pcmk__xe_set_content(child_node, "%s", proc_stderr); + crm_xml_add(child_node, PCMK_XA_SOURCE, "stderr"); } free(rc_as_str); @@ -242,15 +273,16 @@ xml_subprocess_output(pcmk__output_t *out, int exit_status, static void xml_version(pcmk__output_t *out, bool extended) { + const char *author = "Andrew Beekhof and the Pacemaker project " + "contributors"; CRM_ASSERT(out != NULL); - pcmk__output_create_xml_node(out, "version", - "program", "Pacemaker", - "version", PACEMAKER_VERSION, - "author", "Andrew Beekhof and the " - "Pacemaker project contributors", - "build", BUILD_VERSION, - "features", CRM_FEATURES, + pcmk__output_create_xml_node(out, PCMK_XE_VERSION, + PCMK_XA_PROGRAM, "Pacemaker", + PCMK_XA_VERSION, PACEMAKER_VERSION, + PCMK_XA_AUTHOR, author, + PCMK_XA_BUILD, BUILD_VERSION, + PCMK_XA_FEATURES, CRM_FEATURES, NULL); } @@ -265,6 +297,8 @@ xml_err(pcmk__output_t *out, const char *format, ...) { CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; + add_root_node(out); + va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len > 0); @@ -302,20 +336,20 @@ xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plura char *name = NULL; char *buf = NULL; int len; + private_data_t *priv = NULL; - CRM_ASSERT(out != NULL); + 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); - if (substitute) { - for (subst_t *s = substitutions; s->from != NULL; s++) { - if (!strcmp(s->from, buf)) { - name = g_strdup(s->to); - break; - } + for (const subst_t *s = substitutions; s->from != NULL; s++) { + if (strcmp(s->from, buf) == 0) { + name = g_strdup(s->to); + break; } } @@ -323,12 +357,12 @@ xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plura 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, + if (priv->list_element) { + pcmk__output_xml_create_parent(out, PCMK_XE_LIST, + PCMK_XA_NAME, name, NULL); + } else { + pcmk__output_xml_create_parent(out, name, NULL); } g_free(name); @@ -350,10 +384,10 @@ xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) { CRM_ASSERT(len >= 0); va_end(ap); - item_node = pcmk__output_create_xml_text_node(out, "item", buf); + item_node = pcmk__output_create_xml_text_node(out, PCMK_XE_ITEM, buf); if (name != NULL) { - crm_xml_add(item_node, "name", name); + crm_xml_add(item_node, PCMK_XA_NAME, name); } free(buf); @@ -371,16 +405,18 @@ xml_end_list(pcmk__output_t *out) { CRM_ASSERT(out != NULL && out->priv != NULL); priv = out->priv; - if (priv->legacy_xml || simple_list) { - g_queue_pop_tail(priv->parent_q); - } else { + if (priv->list_element) { char *buf = NULL; xmlNodePtr node; + /* Do not free node here - it's still part of the document */ node = g_queue_pop_tail(priv->parent_q); buf = crm_strdup_printf("%lu", xmlChildElementCount(node)); - crm_xml_add(node, "count", buf); + crm_xml_add(node, PCMK_XA_COUNT, buf); free(buf); + } else { + /* Do not free this result - it's still part of the document */ + g_queue_pop_tail(priv->parent_q); } } @@ -465,13 +501,15 @@ pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node) { CRM_ASSERT(node != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return); + add_root_node(out); + priv = out->priv; parent = g_queue_peek_tail(priv->parent_q); // Shouldn't happen unless the caller popped priv->root CRM_CHECK(parent != NULL, return); - add_node_copy(parent, node); + pcmk__xml_copy(parent, node); } xmlNodePtr @@ -483,9 +521,11 @@ pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) { CRM_ASSERT(out != NULL && out->priv != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL); + add_root_node(out); + priv = out->priv; - node = create_xml_node(g_queue_peek_tail(priv->parent_q), name); + node = pcmk__xe_create(g_queue_peek_tail(priv->parent_q), name); va_start(args, name); pcmk__xe_set_propv(node, args); va_end(args); @@ -501,7 +541,7 @@ pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const c CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL); node = pcmk__output_create_xml_node(out, name, NULL); - xmlNodeSetContent(node, (pcmkXmlStr) content); + pcmk__xe_set_content(node, "%s", content); return node; } @@ -513,6 +553,8 @@ pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) { CRM_ASSERT(parent != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return); + add_root_node(out); + priv = out->priv; g_queue_push_tail(priv->parent_q, parent); @@ -525,9 +567,12 @@ pcmk__output_xml_pop_parent(pcmk__output_t *out) { CRM_ASSERT(out != NULL && out->priv != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return); + add_root_node(out); + priv = out->priv; CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0); + /* Do not free this result - it's still part of the document */ g_queue_pop_tail(priv->parent_q); } @@ -538,8 +583,61 @@ pcmk__output_xml_peek_parent(pcmk__output_t *out) { CRM_ASSERT(out != NULL && out->priv != NULL); CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL); + add_root_node(out); + priv = out->priv; /* If queue is empty NULL will be returned */ return g_queue_peek_tail(priv->parent_q); } + +bool +pcmk__output_get_legacy_xml(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) { + return false; + } + + CRM_ASSERT(out->priv != NULL); + + priv = out->priv; + return priv->legacy_xml; +} + +void +pcmk__output_set_legacy_xml(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) { + return; + } + + CRM_ASSERT(out->priv != NULL); + + priv = out->priv; + priv->legacy_xml = true; +} + +void +pcmk__output_enable_list_element(pcmk__output_t *out) +{ + private_data_t *priv = NULL; + + CRM_ASSERT(out != NULL); + + if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) { + return; + } + + CRM_ASSERT(out->priv != NULL); + + priv = out->priv; + priv->list_element = true; +} |