root/lib/common/output_xml.c

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

DEFINITIONS

This source file includes following definitions.
  1. xml_free_priv
  2. xml_init
  3. add_error_node
  4. xml_finish
  5. xml_reset
  6. xml_subprocess_output
  7. xml_version
  8. G_GNUC_PRINTF
  9. G_GNUC_PRINTF
  10. xml_output_xml
  11. G_GNUC_PRINTF
  12. G_GNUC_PRINTF
  13. xml_increment_list
  14. xml_end_list
  15. xml_is_quiet
  16. xml_spacer
  17. xml_progress
  18. pcmk__mk_xml_output
  19. pcmk__output_xml_create_parent
  20. pcmk__output_xml_add_node_copy
  21. pcmk__output_create_xml_node
  22. pcmk__output_create_xml_text_node
  23. pcmk__output_xml_push_parent
  24. pcmk__output_xml_pop_parent
  25. pcmk__output_xml_peek_parent

   1 /*
   2  * Copyright 2019-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <ctype.h>
  13 #include <stdarg.h>
  14 #include <stdlib.h>
  15 #include <stdio.h>
  16 #include <glib.h>
  17 
  18 #include <crm/common/cmdline_internal.h>
  19 #include <crm/common/xml.h>
  20 
  21 static gboolean legacy_xml = FALSE;
  22 static gboolean simple_list = FALSE;
  23 static gboolean substitute = FALSE;
  24 
  25 GOptionEntry pcmk__xml_output_entries[] = {
  26     { "xml-legacy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &legacy_xml,
  27       NULL,
  28       NULL },
  29     { "xml-simple-list", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &simple_list,
  30       NULL,
  31       NULL },
  32     { "xml-substitute", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &substitute,
  33       NULL,
  34       NULL },
  35 
  36     { NULL }
  37 };
  38 
  39 typedef struct subst_s {
  40     const char *from;
  41     const char *to;
  42 } subst_t;
  43 
  44 static subst_t substitutions[] = {
  45     { "Active Resources",                               "resources" },
  46     { "Allocation Scores",                              "allocations" },
  47     { "Allocation Scores and Utilization Information",  "allocations_utilizations" },
  48     { "Cluster Summary",                                "summary" },
  49     { "Current cluster status",                         "cluster_status" },
  50     { "Executing Cluster Transition",                   "transition" },
  51     { "Failed Resource Actions",                        "failures" },
  52     { "Fencing History",                                "fence_history" },
  53     { "Full List of Resources",                         "resources" },
  54     { "Inactive Resources",                             "resources" },
  55     { "Migration Summary",                              "node_history" },
  56     { "Negative Location Constraints",                  "bans" },
  57     { "Node Attributes",                                "node_attributes" },
  58     { "Operations",                                     "node_history" },
  59     { "Resource Config",                                "resource_config" },
  60     { "Resource Operations",                            "operations" },
  61     { "Revised Cluster Status",                         "revised_cluster_status" },
  62     { "Transition Summary",                             "actions" },
  63     { "Utilization Information",                        "utilizations" },
  64 
  65     { NULL, NULL }
  66 };
  67 
  68 /* The first several elements of this struct must be the same as the first
  69  * several elements of private_data_s in lib/common/output_html.c.  That
  70  * struct gets passed to a bunch of the pcmk__output_xml_* functions which
  71  * assume an XML private_data_s.  Keeping them laid out the same means this
  72  * still works.
  73  */
  74 typedef struct private_data_s {
  75     /* Begin members that must match the HTML version */
  76     xmlNode *root;
  77     GQueue *parent_q;
  78     GSList *errors;
  79     /* End members that must match the HTML version */
  80     bool legacy_xml;
  81 } private_data_t;
  82 
  83 static void
  84 xml_free_priv(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  85     private_data_t *priv = NULL;
  86 
  87     if (out == NULL || out->priv == NULL) {
  88         return;
  89     }
  90 
  91     priv = out->priv;
  92 
  93     free_xml(priv->root);
  94     g_queue_free(priv->parent_q);
  95     g_slist_free(priv->errors);
  96     free(priv);
  97     out->priv = NULL;
  98 }
  99 
 100 static bool
 101 xml_init(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 102     private_data_t *priv = NULL;
 103 
 104     CRM_ASSERT(out != NULL);
 105 
 106     /* If xml_init was previously called on this output struct, just return. */
 107     if (out->priv != NULL) {
 108         return true;
 109     } else {
 110         out->priv = calloc(1, sizeof(private_data_t));
 111         if (out->priv == NULL) {
 112             return false;
 113         }
 114 
 115         priv = out->priv;
 116     }
 117 
 118     if (legacy_xml) {
 119         priv->root = create_xml_node(NULL, "crm_mon");
 120         crm_xml_add(priv->root, "version", PACEMAKER_VERSION);
 121     } else {
 122         priv->root = create_xml_node(NULL, "pacemaker-result");
 123         crm_xml_add(priv->root, "api-version", PCMK__API_VERSION);
 124 
 125         if (out->request != NULL) {
 126             crm_xml_add(priv->root, "request", out->request);
 127         }
 128     }
 129 
 130     priv->parent_q = g_queue_new();
 131     priv->errors = NULL;
 132     g_queue_push_tail(priv->parent_q, priv->root);
 133 
 134     /* Copy this from the file-level variable.  This means that it is only settable
 135      * as a command line option, and that pcmk__output_new must be called after all
 136      * command line processing is completed.
 137      */
 138     priv->legacy_xml = legacy_xml;
 139 
 140     return true;
 141 }
 142 
 143 static void
 144 add_error_node(gpointer data, gpointer user_data) {
     /* [previous][next][first][last][top][bottom][index][help] */
 145     char *str = (char *) data;
 146     xmlNodePtr node = (xmlNodePtr) user_data;
 147     pcmk_create_xml_text_node(node, "error", str);
 148 }
 149 
 150 static void
 151 xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
     /* [previous][next][first][last][top][bottom][index][help] */
 152     private_data_t *priv = NULL;
 153     xmlNodePtr node;
 154 
 155     CRM_ASSERT(out != NULL);
 156     priv = out->priv;
 157 
 158     /* If root is NULL, xml_init failed and we are being called from pcmk__output_free
 159      * in the pcmk__output_new path.
 160      */
 161     if (priv == NULL || priv->root == NULL) {
 162         return;
 163     }
 164 
 165     if (legacy_xml) {
 166         GSList *node = priv->errors;
 167 
 168         if (exit_status != CRM_EX_OK) {
 169             fprintf(stderr, "%s\n", crm_exit_str(exit_status));
 170         }
 171 
 172         while (node != NULL) {
 173             fprintf(stderr, "%s\n", (char *) node->data);
 174             node = node->next;
 175         }
 176     } else {
 177         char *rc_as_str = pcmk__itoa(exit_status);
 178 
 179         node = create_xml_node(priv->root, "status");
 180         pcmk__xe_set_props(node, "code", rc_as_str,
 181                            "message", crm_exit_str(exit_status),
 182                            NULL);
 183 
 184         if (g_slist_length(priv->errors) > 0) {
 185             xmlNodePtr errors_node = create_xml_node(node, "errors");
 186             g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
 187         }
 188 
 189         free(rc_as_str);
 190     }
 191 
 192     if (print) {
 193         char *buf = dump_xml_formatted_with_text(priv->root);
 194         fprintf(out->dest, "%s", buf);
 195         fflush(out->dest);
 196         free(buf);
 197     }
 198 
 199     if (copy_dest != NULL) {
 200         *copy_dest = copy_xml(priv->root);
 201     }
 202 }
 203 
 204 static void
 205 xml_reset(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 206     CRM_ASSERT(out != NULL);
 207 
 208     out->dest = freopen(NULL, "w", out->dest);
 209     CRM_ASSERT(out->dest != NULL);
 210 
 211     xml_free_priv(out);
 212     xml_init(out);
 213 }
 214 
 215 static void
 216 xml_subprocess_output(pcmk__output_t *out, int exit_status,
     /* [previous][next][first][last][top][bottom][index][help] */
 217                       const char *proc_stdout, const char *proc_stderr) {
 218     xmlNodePtr node, child_node;
 219     char *rc_as_str = NULL;
 220 
 221     CRM_ASSERT(out != NULL);
 222 
 223     rc_as_str = pcmk__itoa(exit_status);
 224 
 225     node = pcmk__output_xml_create_parent(out, "command",
 226                                           "code", rc_as_str,
 227                                           NULL);
 228 
 229     if (proc_stdout != NULL) {
 230         child_node = pcmk_create_xml_text_node(node, "output", proc_stdout);
 231         crm_xml_add(child_node, "source", "stdout");
 232     }
 233 
 234     if (proc_stderr != NULL) {
 235         child_node = pcmk_create_xml_text_node(node, "output", proc_stderr);
 236         crm_xml_add(child_node, "source", "stderr");
 237     }
 238 
 239     free(rc_as_str);
 240 }
 241 
 242 static void
 243 xml_version(pcmk__output_t *out, bool extended) {
     /* [previous][next][first][last][top][bottom][index][help] */
 244     CRM_ASSERT(out != NULL);
 245 
 246     pcmk__output_create_xml_node(out, "version",
 247                                  "program", "Pacemaker",
 248                                  "version", PACEMAKER_VERSION,
 249                                  "author", "Andrew Beekhof and the "
 250                                            "Pacemaker project contributors",
 251                                  "build", BUILD_VERSION,
 252                                  "features", CRM_FEATURES,
 253                                  NULL);
 254 }
 255 
 256 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 257 static void
 258 xml_err(pcmk__output_t *out, const char *format, ...) {
 259     private_data_t *priv = NULL;
 260     int len = 0;
 261     char *buf = NULL;
 262     va_list ap;
 263 
 264     CRM_ASSERT(out != NULL && out->priv != NULL);
 265     priv = out->priv;
 266 
 267     va_start(ap, format);
 268     len = vasprintf(&buf, format, ap);
 269     CRM_ASSERT(len > 0);
 270     va_end(ap);
 271 
 272     priv->errors = g_slist_append(priv->errors, buf);
 273 }
 274 
 275 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 static int
 277 xml_info(pcmk__output_t *out, const char *format, ...) {
 278     return pcmk_rc_no_output;
 279 }
 280 
 281 static void
 282 xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 283     xmlNodePtr parent = NULL;
 284     xmlNodePtr cdata_node = NULL;
 285 
 286     CRM_ASSERT(out != NULL);
 287 
 288     parent = pcmk__output_create_xml_node(out, name, NULL);
 289     cdata_node = xmlNewCDataBlock(getDocPtr(parent), (pcmkXmlStr) buf, strlen(buf));
 290     xmlAddChild(parent, cdata_node);
 291 }
 292 
 293 G_GNUC_PRINTF(4, 5)
     /* [previous][next][first][last][top][bottom][index][help] */
 294 static void
 295 xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
 296                const char *format, ...) {
 297     va_list ap;
 298     char *name = NULL;
 299     char *buf = NULL;
 300     int len;
 301 
 302     CRM_ASSERT(out != NULL);
 303 
 304     va_start(ap, format);
 305     len = vasprintf(&buf, format, ap);
 306     CRM_ASSERT(len >= 0);
 307     va_end(ap);
 308 
 309     if (substitute) {
 310         for (subst_t *s = substitutions; s->from != NULL; s++) {
 311             if (!strcmp(s->from, buf)) {
 312                 name = g_strdup(s->to);
 313                 break;
 314             }
 315         }
 316     }
 317 
 318     if (name == NULL) {
 319         name = g_ascii_strdown(buf, -1);
 320     }
 321 
 322     if (legacy_xml || simple_list) {
 323         pcmk__output_xml_create_parent(out, name, NULL);
 324     } else {
 325         pcmk__output_xml_create_parent(out, "list",
 326                                        "name", name,
 327                                        NULL);
 328     }
 329 
 330     g_free(name);
 331     free(buf);
 332 }
 333 
 334 G_GNUC_PRINTF(3, 4)
     /* [previous][next][first][last][top][bottom][index][help] */
 335 static void
 336 xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
 337     xmlNodePtr item_node = NULL;
 338     va_list ap;
 339     char *buf = NULL;
 340     int len;
 341 
 342     CRM_ASSERT(out != NULL);
 343 
 344     va_start(ap, format);
 345     len = vasprintf(&buf, format, ap);
 346     CRM_ASSERT(len >= 0);
 347     va_end(ap);
 348 
 349     item_node = pcmk__output_create_xml_text_node(out, "item", buf);
 350 
 351     if (name != NULL) {
 352         crm_xml_add(item_node, "name", name);
 353     }
 354 
 355     free(buf);
 356 }
 357 
 358 static void
 359 xml_increment_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 360     /* This function intentially left blank */
 361 }
 362 
 363 static void
 364 xml_end_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 365     private_data_t *priv = NULL;
 366 
 367     CRM_ASSERT(out != NULL && out->priv != NULL);
 368     priv = out->priv;
 369 
 370     if (priv->legacy_xml || simple_list) {
 371         g_queue_pop_tail(priv->parent_q);
 372     } else {
 373         char *buf = NULL;
 374         xmlNodePtr node;
 375 
 376         node = g_queue_pop_tail(priv->parent_q);
 377         buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
 378         crm_xml_add(node, "count", buf);
 379         free(buf);
 380     }
 381 }
 382 
 383 static bool
 384 xml_is_quiet(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 385     return false;
 386 }
 387 
 388 static void
 389 xml_spacer(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 390     /* This function intentionally left blank */
 391 }
 392 
 393 static void
 394 xml_progress(pcmk__output_t *out, bool end) {
     /* [previous][next][first][last][top][bottom][index][help] */
 395     /* This function intentionally left blank */
 396 }
 397 
 398 pcmk__output_t *
 399 pcmk__mk_xml_output(char **argv) {
     /* [previous][next][first][last][top][bottom][index][help] */
 400     pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
 401 
 402     if (retval == NULL) {
 403         return NULL;
 404     }
 405 
 406     retval->fmt_name = "xml";
 407     retval->request = pcmk__quote_cmdline(argv);
 408 
 409     retval->init = xml_init;
 410     retval->free_priv = xml_free_priv;
 411     retval->finish = xml_finish;
 412     retval->reset = xml_reset;
 413 
 414     retval->register_message = pcmk__register_message;
 415     retval->message = pcmk__call_message;
 416 
 417     retval->subprocess_output = xml_subprocess_output;
 418     retval->version = xml_version;
 419     retval->info = xml_info;
 420     retval->transient = xml_info;
 421     retval->err = xml_err;
 422     retval->output_xml = xml_output_xml;
 423 
 424     retval->begin_list = xml_begin_list;
 425     retval->list_item = xml_list_item;
 426     retval->increment_list = xml_increment_list;
 427     retval->end_list = xml_end_list;
 428 
 429     retval->is_quiet = xml_is_quiet;
 430     retval->spacer = xml_spacer;
 431     retval->progress = xml_progress;
 432     retval->prompt = pcmk__text_prompt;
 433 
 434     return retval;
 435 }
 436 
 437 xmlNodePtr
 438 pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name, ...) {
     /* [previous][next][first][last][top][bottom][index][help] */
 439     va_list args;
 440     xmlNodePtr node = NULL;
 441 
 442     CRM_ASSERT(out != NULL);
 443     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 444 
 445     node = pcmk__output_create_xml_node(out, name, NULL);
 446 
 447     va_start(args, name);
 448     pcmk__xe_set_propv(node, args);
 449     va_end(args);
 450 
 451     pcmk__output_xml_push_parent(out, node);
 452     return node;
 453 }
 454 
 455 void
 456 pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node) {
     /* [previous][next][first][last][top][bottom][index][help] */
 457     private_data_t *priv = NULL;
 458     xmlNodePtr parent = NULL;
 459 
 460     CRM_ASSERT(out != NULL && out->priv != NULL);
 461     CRM_ASSERT(node != NULL);
 462     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
 463 
 464     priv = out->priv;
 465     parent = g_queue_peek_tail(priv->parent_q);
 466 
 467     // Shouldn't happen unless the caller popped priv->root
 468     CRM_CHECK(parent != NULL, return);
 469 
 470     add_node_copy(parent, node);
 471 }
 472 
 473 xmlNodePtr
 474 pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) {
     /* [previous][next][first][last][top][bottom][index][help] */
 475     xmlNodePtr node = NULL;
 476     private_data_t *priv = NULL;
 477     va_list args;
 478 
 479     CRM_ASSERT(out != NULL && out->priv != NULL);
 480     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 481 
 482     priv = out->priv;
 483 
 484     node = create_xml_node(g_queue_peek_tail(priv->parent_q), name);
 485     va_start(args, name);
 486     pcmk__xe_set_propv(node, args);
 487     va_end(args);
 488 
 489     return node;
 490 }
 491 
 492 xmlNodePtr
 493 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
     /* [previous][next][first][last][top][bottom][index][help] */
 494     xmlNodePtr node = NULL;
 495 
 496     CRM_ASSERT(out != NULL);
 497     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 498 
 499     node = pcmk__output_create_xml_node(out, name, NULL);
 500     xmlNodeSetContent(node, (pcmkXmlStr) content);
 501     return node;
 502 }
 503 
 504 void
 505 pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) {
     /* [previous][next][first][last][top][bottom][index][help] */
 506     private_data_t *priv = NULL;
 507 
 508     CRM_ASSERT(out != NULL && out->priv != NULL);
 509     CRM_ASSERT(parent != NULL);
 510     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
 511 
 512     priv = out->priv;
 513 
 514     g_queue_push_tail(priv->parent_q, parent);
 515 }
 516 
 517 void
 518 pcmk__output_xml_pop_parent(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 519     private_data_t *priv = NULL;
 520 
 521     CRM_ASSERT(out != NULL && out->priv != NULL);
 522     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
 523 
 524     priv = out->priv;
 525 
 526     CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0);
 527     g_queue_pop_tail(priv->parent_q);
 528 }
 529 
 530 xmlNodePtr
 531 pcmk__output_xml_peek_parent(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 532     private_data_t *priv = NULL;
 533 
 534     CRM_ASSERT(out != NULL && out->priv != NULL);
 535     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 536 
 537     priv = out->priv;
 538 
 539     /* If queue is empty NULL will be returned */
 540     return g_queue_peek_tail(priv->parent_q);
 541 }

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