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. finish_reset_common
  5. xml_finish
  6. xml_reset
  7. xml_subprocess_output
  8. xml_version
  9. G_GNUC_PRINTF
  10. G_GNUC_PRINTF
  11. xml_output_xml
  12. G_GNUC_PRINTF
  13. G_GNUC_PRINTF
  14. xml_increment_list
  15. xml_end_list
  16. xml_is_quiet
  17. pcmk__mk_xml_output
  18. pcmk__output_xml_create_parent
  19. pcmk__output_xml_add_node
  20. pcmk__output_create_xml_node
  21. pcmk__output_create_xml_text_node
  22. pcmk__output_xml_push_parent
  23. pcmk__output_xml_pop_parent
  24. pcmk__output_xml_peek_parent

   1 /*
   2  * Copyright 2019-2020 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 #ifndef _GNU_SOURCE
  11 #  define _GNU_SOURCE
  12 #endif
  13 
  14 #ifndef PCMK__CONFIG_H
  15 #  define PCMK__CONFIG_H
  16 #  include <config.h>
  17 #endif
  18 
  19 #include <ctype.h>
  20 #include <stdarg.h>
  21 #include <stdlib.h>
  22 #include <stdio.h>
  23 #include <glib.h>
  24 
  25 #include <crm/crm.h>
  26 #include <crm/common/xml.h>
  27 #include <crm/common/output_internal.h>
  28 
  29 static gboolean legacy_xml = FALSE;
  30 static gboolean simple_list = FALSE;
  31 static gboolean substitute = FALSE;
  32 
  33 GOptionEntry pcmk__xml_output_entries[] = {
  34     { "xml-legacy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &legacy_xml,
  35       NULL,
  36       NULL },
  37     { "xml-simple-list", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &simple_list,
  38       NULL,
  39       NULL },
  40     { "xml-substitute", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &substitute,
  41       NULL,
  42       NULL },
  43 
  44     { NULL }
  45 };
  46 
  47 typedef struct subst_s {
  48     const char *from;
  49     const char *to;
  50 } subst_t;
  51 
  52 static subst_t substitutions[] = {
  53     { "Attributes",                     "attributes" },
  54     { "Active Resources",               "resources" },
  55     { "Full List of Resources",         "resources" },
  56     { "Inactive Resources",             "resources" },
  57     { "Cluster Summary",                "summary" },
  58     { "Failed Resource Actions",        "failures" },
  59     { "Fencing History",                "fence_history" },
  60     { "Migration Summary",              "node_history" },
  61     { "Operations",                     "node_history" },
  62     { "Negative Location Constraints",  "bans" },
  63     { "Node Attributes",                "node_attributes" },
  64     { "Resources",                      "resources" },
  65     { "Tickets",                        "tickets" },
  66 
  67     { NULL, NULL }
  68 };
  69 
  70 typedef struct private_data_s {
  71     xmlNode *root;
  72     GQueue *parent_q;
  73     GSList *errors;
  74     bool legacy_xml;
  75 } private_data_t;
  76 
  77 static void
  78 xml_free_priv(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  79     private_data_t *priv = out->priv;
  80 
  81     if (priv == NULL) {
  82         return;
  83     }
  84 
  85     xmlFreeNode(priv->root);
  86     g_queue_free(priv->parent_q);
  87     g_slist_free(priv->errors);
  88     free(priv);
  89     out->priv = NULL;
  90 }
  91 
  92 static bool
  93 xml_init(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  94     private_data_t *priv = NULL;
  95 
  96     /* If xml_init was previously called on this output struct, just return. */
  97     if (out->priv != NULL) {
  98         return true;
  99     } else {
 100         out->priv = calloc(1, sizeof(private_data_t));
 101         if (out->priv == NULL) {
 102             return false;
 103         }
 104 
 105         priv = out->priv;
 106     }
 107 
 108     if (legacy_xml) {
 109         priv->root = create_xml_node(NULL, "crm_mon");
 110         xmlSetProp(priv->root, (pcmkXmlStr) "version", (pcmkXmlStr) VERSION);
 111     } else {
 112         priv->root = create_xml_node(NULL, "pacemaker-result");
 113         xmlSetProp(priv->root, (pcmkXmlStr) "api-version", (pcmkXmlStr) PCMK__API_VERSION);
 114 
 115         if (out->request != NULL) {
 116             xmlSetProp(priv->root, (pcmkXmlStr) "request", (pcmkXmlStr) out->request);
 117         }
 118     }
 119 
 120     priv->parent_q = g_queue_new();
 121     priv->errors = NULL;
 122     g_queue_push_tail(priv->parent_q, priv->root);
 123 
 124     /* Copy this from the file-level variable.  This means that it is only settable
 125      * as a command line option, and that pcmk__output_new must be called after all
 126      * command line processing is completed.
 127      */
 128     priv->legacy_xml = legacy_xml;
 129 
 130     return true;
 131 }
 132 
 133 static void
 134 add_error_node(gpointer data, gpointer user_data) {
     /* [previous][next][first][last][top][bottom][index][help] */
 135     char *str = (char *) data;
 136     xmlNodePtr node = (xmlNodePtr) user_data;
 137     pcmk_create_xml_text_node(node, "error", str);
 138 }
 139 
 140 static void
 141 finish_reset_common(pcmk__output_t *out, crm_exit_t exit_status, bool print) {
     /* [previous][next][first][last][top][bottom][index][help] */
 142     xmlNodePtr node;
 143     private_data_t *priv = out->priv;
 144 
 145     if (legacy_xml) {
 146         GSList *node = priv->errors;
 147 
 148         if (exit_status != CRM_EX_OK) {
 149             fprintf(stderr, "%s\n", crm_exit_str(exit_status));
 150         }
 151 
 152         while (node != NULL) {
 153             fprintf(stderr, "%s\n", (char *) node->data);
 154             node = node->next;
 155         }
 156     } else {
 157         char *rc_as_str = crm_itoa(exit_status);
 158 
 159         node = create_xml_node(priv->root, "status");
 160         xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
 161         xmlSetProp(node, (pcmkXmlStr) "message", (pcmkXmlStr) crm_exit_str(exit_status));
 162 
 163         if (g_slist_length(priv->errors) > 0) {
 164             xmlNodePtr errors_node = create_xml_node(node, "errors");
 165             g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
 166         }
 167 
 168         free(rc_as_str);
 169     }
 170 
 171     if (print) {
 172         char *buf = dump_xml_formatted_with_text(priv->root);
 173         fprintf(out->dest, "%s", buf);
 174         free(buf);
 175     }
 176 }
 177 
 178 static void
 179 xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
     /* [previous][next][first][last][top][bottom][index][help] */
 180     private_data_t *priv = out->priv;
 181 
 182     /* If root is NULL, xml_init failed and we are being called from pcmk__output_free
 183      * in the pcmk__output_new path.
 184      */
 185     if (priv == NULL || priv->root == NULL) {
 186         return;
 187     }
 188 
 189     finish_reset_common(out, exit_status, print);
 190 
 191     if (copy_dest != NULL) {
 192         *copy_dest = copy_xml(priv->root);
 193     }
 194 }
 195 
 196 static void
 197 xml_reset(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 198     CRM_ASSERT(out != NULL);
 199 
 200     out->dest = freopen(NULL, "w", out->dest);
 201     CRM_ASSERT(out->dest != NULL);
 202 
 203     if (out->priv != NULL) {
 204         finish_reset_common(out, CRM_EX_OK, true);
 205     }
 206 
 207     xml_free_priv(out);
 208     xml_init(out);
 209 }
 210 
 211 static void
 212 xml_subprocess_output(pcmk__output_t *out, int exit_status,
     /* [previous][next][first][last][top][bottom][index][help] */
 213                       const char *proc_stdout, const char *proc_stderr) {
 214     xmlNodePtr node, child_node;
 215     char *rc_as_str = NULL;
 216 
 217     rc_as_str = crm_itoa(exit_status);
 218 
 219     node = pcmk__output_xml_create_parent(out, "command");
 220     xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
 221 
 222     if (proc_stdout != NULL) {
 223         child_node = pcmk_create_xml_text_node(node, "output", proc_stdout);
 224         xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stdout");
 225     }
 226 
 227     if (proc_stderr != NULL) {
 228         child_node = pcmk_create_xml_text_node(node, "output", proc_stderr);
 229         xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stderr");
 230     }
 231 
 232     pcmk__output_xml_add_node(out, node);
 233     free(rc_as_str);
 234 }
 235 
 236 static void
 237 xml_version(pcmk__output_t *out, bool extended) {
     /* [previous][next][first][last][top][bottom][index][help] */
 238     xmlNodePtr node;
 239     private_data_t *priv = out->priv;
 240     CRM_ASSERT(priv != NULL);
 241 
 242     node = pcmk__output_create_xml_node(out, "version");
 243     xmlSetProp(node, (pcmkXmlStr) "program", (pcmkXmlStr) "Pacemaker");
 244     xmlSetProp(node, (pcmkXmlStr) "version", (pcmkXmlStr) PACEMAKER_VERSION);
 245     xmlSetProp(node, (pcmkXmlStr) "author", (pcmkXmlStr) "Andrew Beekhof");
 246     xmlSetProp(node, (pcmkXmlStr) "build", (pcmkXmlStr) BUILD_VERSION);
 247     xmlSetProp(node, (pcmkXmlStr) "features", (pcmkXmlStr) CRM_FEATURES);
 248 }
 249 
 250 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 static void
 252 xml_err(pcmk__output_t *out, const char *format, ...) {
 253     private_data_t *priv = out->priv;
 254     int len = 0;
 255     char *buf = NULL;
 256     va_list ap;
 257 
 258     CRM_ASSERT(priv != NULL);
 259     va_start(ap, format);
 260     len = vasprintf(&buf, format, ap);
 261     CRM_ASSERT(len > 0);
 262     va_end(ap);
 263 
 264     priv->errors = g_slist_append(priv->errors, buf);
 265 }
 266 
 267 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 268 static void
 269 xml_info(pcmk__output_t *out, const char *format, ...) {
 270     /* This function intentially left blank */
 271 }
 272 
 273 static void
 274 xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 275     xmlNodePtr parent = NULL;
 276     xmlNodePtr cdata_node = NULL;
 277     private_data_t *priv = out->priv;
 278 
 279     CRM_ASSERT(priv != NULL);
 280 
 281     parent = pcmk__output_create_xml_node(out, name);
 282     cdata_node = xmlNewCDataBlock(getDocPtr(parent), (pcmkXmlStr) buf, strlen(buf));
 283     xmlAddChild(parent, cdata_node);
 284 }
 285 
 286 G_GNUC_PRINTF(4, 5)
     /* [previous][next][first][last][top][bottom][index][help] */
 287 static void
 288 xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
 289                const char *format, ...) {
 290     va_list ap;
 291     const char *name = NULL;
 292     char *buf = NULL;
 293     int len;
 294 
 295     va_start(ap, format);
 296     len = vasprintf(&buf, format, ap);
 297     CRM_ASSERT(len >= 0);
 298     va_end(ap);
 299 
 300     if (substitute) {
 301         for (subst_t *s = substitutions; s->from != NULL; s++) {
 302             if (!strcmp(s->from, buf)) {
 303                 name = s->to;
 304                 break;
 305             }
 306         }
 307     }
 308 
 309     if (name == NULL) {
 310         name = buf;
 311     }
 312 
 313     if (legacy_xml || simple_list) {
 314         pcmk__output_xml_create_parent(out, name);
 315     } else {
 316         xmlNodePtr list_node = NULL;
 317 
 318         list_node = pcmk__output_xml_create_parent(out, "list");
 319         xmlSetProp(list_node, (pcmkXmlStr) "name", (pcmkXmlStr) name);
 320     }
 321 
 322     free(buf);
 323 }
 324 
 325 G_GNUC_PRINTF(3, 4)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 static void
 327 xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
 328     private_data_t *priv = out->priv;
 329     xmlNodePtr item_node = NULL;
 330     va_list ap;
 331     char *buf = NULL;
 332     int len;
 333 
 334     CRM_ASSERT(priv != NULL);
 335 
 336     va_start(ap, format);
 337     len = vasprintf(&buf, format, ap);
 338     CRM_ASSERT(len >= 0);
 339     va_end(ap);
 340 
 341     item_node = pcmk__output_create_xml_text_node(out, "item", buf);
 342 
 343     if (name != NULL) {
 344         xmlSetProp(item_node, (pcmkXmlStr) "name", (pcmkXmlStr) name);
 345     }
 346 
 347     free(buf);
 348 }
 349 
 350 static void
 351 xml_increment_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 352     /* This function intentially left blank */
 353 }
 354 
 355 static void
 356 xml_end_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 357     private_data_t *priv = out->priv;
 358 
 359     CRM_ASSERT(priv != NULL);
 360 
 361     if (priv->legacy_xml || simple_list) {
 362         g_queue_pop_tail(priv->parent_q);
 363     } else {
 364         char *buf = NULL;
 365         xmlNodePtr node;
 366 
 367         node = g_queue_pop_tail(priv->parent_q);
 368         buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
 369         xmlSetProp(node, (pcmkXmlStr) "count", (pcmkXmlStr) buf);
 370         free(buf);
 371     }
 372 }
 373 
 374 static bool
 375 xml_is_quiet(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 376     return false;
 377 }
 378 
 379 pcmk__output_t *
 380 pcmk__mk_xml_output(char **argv) {
     /* [previous][next][first][last][top][bottom][index][help] */
 381     pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
 382 
 383     if (retval == NULL) {
 384         return NULL;
 385     }
 386 
 387     retval->fmt_name = "xml";
 388     retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
 389 
 390     retval->init = xml_init;
 391     retval->free_priv = xml_free_priv;
 392     retval->finish = xml_finish;
 393     retval->reset = xml_reset;
 394 
 395     retval->register_message = pcmk__register_message;
 396     retval->message = pcmk__call_message;
 397 
 398     retval->subprocess_output = xml_subprocess_output;
 399     retval->version = xml_version;
 400     retval->info = xml_info;
 401     retval->err = xml_err;
 402     retval->output_xml = xml_output_xml;
 403 
 404     retval->begin_list = xml_begin_list;
 405     retval->list_item = xml_list_item;
 406     retval->increment_list = xml_increment_list;
 407     retval->end_list = xml_end_list;
 408 
 409     retval->is_quiet = xml_is_quiet;
 410 
 411     return retval;
 412 }
 413 
 414 xmlNodePtr
 415 pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name) {
     /* [previous][next][first][last][top][bottom][index][help] */
 416     xmlNodePtr node = pcmk__output_create_xml_node(out, name);
 417     pcmk__output_xml_push_parent(out, node);
 418     return node;
 419 }
 420 
 421 void
 422 pcmk__output_xml_add_node(pcmk__output_t *out, xmlNodePtr node) {
     /* [previous][next][first][last][top][bottom][index][help] */
 423     private_data_t *priv = out->priv;
 424 
 425     CRM_ASSERT(priv != NULL);
 426     CRM_ASSERT(node != NULL);
 427 
 428     xmlAddChild(g_queue_peek_tail(priv->parent_q), node);
 429 }
 430 
 431 xmlNodePtr
 432 pcmk__output_create_xml_node(pcmk__output_t *out, const char *name) {
     /* [previous][next][first][last][top][bottom][index][help] */
 433     private_data_t *priv = out->priv;
 434 
 435     CRM_ASSERT(priv != NULL);
 436 
 437     return create_xml_node(g_queue_peek_tail(priv->parent_q), name);
 438 }
 439 
 440 xmlNodePtr
 441 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
     /* [previous][next][first][last][top][bottom][index][help] */
 442     xmlNodePtr node = pcmk__output_create_xml_node(out, name);
 443     xmlNodeSetContent(node, (pcmkXmlStr) content);
 444     return node;
 445 }
 446 
 447 void
 448 pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) {
     /* [previous][next][first][last][top][bottom][index][help] */
 449     private_data_t *priv = out->priv;
 450 
 451     CRM_ASSERT(priv != NULL);
 452     CRM_ASSERT(parent != NULL);
 453 
 454     g_queue_push_tail(priv->parent_q, parent);
 455 }
 456 
 457 void
 458 pcmk__output_xml_pop_parent(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 459     private_data_t *priv = out->priv;
 460 
 461     CRM_ASSERT(priv != NULL);
 462     CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0);
 463 
 464     g_queue_pop_tail(priv->parent_q);
 465 }
 466 
 467 xmlNodePtr
 468 pcmk__output_xml_peek_parent(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 469     private_data_t *priv = out->priv;
 470 
 471     CRM_ASSERT(priv != NULL);
 472 
 473     /* If queue is empty NULL will be returned */
 474     return g_queue_peek_tail(priv->parent_q);
 475 }

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