root/lib/common/output_xml.c

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

DEFINITIONS

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

   1 /*
   2  * Copyright 2019-2024 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 <crm/crm.h>
  17 #include <glib.h>
  18 
  19 #include <crm/common/cmdline_internal.h>
  20 #include <crm/common/output.h>
  21 #include <crm/common/xml.h>
  22 #include <crm/common/xml_internal.h>    // pcmk__xml2fd
  23 
  24 typedef struct subst_s {
  25     const char *from;
  26     const char *to;
  27 } subst_t;
  28 
  29 static const subst_t substitutions[] = {
  30     { "Active Resources",
  31       PCMK_XE_RESOURCES, },
  32     { "Assignment Scores",
  33       PCMK_XE_ALLOCATIONS, },
  34     { "Assignment Scores and Utilization Information",
  35       PCMK_XE_ALLOCATIONS_UTILIZATIONS, },
  36     { "Cluster Summary",
  37       PCMK_XE_SUMMARY, },
  38     { "Current cluster status",
  39       PCMK_XE_CLUSTER_STATUS, },
  40     { "Executing Cluster Transition",
  41       PCMK_XE_TRANSITION, },
  42     { "Failed Resource Actions",
  43       PCMK_XE_FAILURES, },
  44     { "Fencing History",
  45       PCMK_XE_FENCE_HISTORY, },
  46     { "Full List of Resources",
  47       PCMK_XE_RESOURCES, },
  48     { "Inactive Resources",
  49       PCMK_XE_RESOURCES, },
  50     { "Migration Summary",
  51       PCMK_XE_NODE_HISTORY, },
  52     { "Negative Location Constraints",
  53       PCMK_XE_BANS, },
  54     { "Node Attributes",
  55       PCMK_XE_NODE_ATTRIBUTES, },
  56     { "Operations",
  57       PCMK_XE_NODE_HISTORY, },
  58     { "Resource Config",
  59       PCMK_XE_RESOURCE_CONFIG, },
  60     { "Resource Operations",
  61       PCMK_XE_OPERATIONS, },
  62     { "Revised Cluster Status",
  63       PCMK_XE_REVISED_CLUSTER_STATUS, },
  64     { "Timings",
  65       PCMK_XE_TIMINGS, },
  66     { "Transition Summary",
  67       PCMK_XE_ACTIONS, },
  68     { "Utilization Information",
  69       PCMK_XE_UTILIZATIONS, },
  70 
  71     { NULL, NULL }
  72 };
  73 
  74 /* The first several elements of this struct must be the same as the first
  75  * several elements of private_data_s in lib/common/output_html.c.  That
  76  * struct gets passed to a bunch of the pcmk__output_xml_* functions which
  77  * assume an XML private_data_s.  Keeping them laid out the same means this
  78  * still works.
  79  */
  80 typedef struct private_data_s {
  81     /* Begin members that must match the HTML version */
  82     xmlNode *root;
  83     GQueue *parent_q;
  84     GSList *errors;
  85     /* End members that must match the HTML version */
  86     bool legacy_xml;
  87     bool list_element;
  88 } private_data_t;
  89 
  90 static bool
  91 has_root_node(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93     private_data_t *priv = NULL;
  94 
  95     pcmk__assert(out != NULL);
  96 
  97     priv = out->priv;
  98     return priv != NULL && priv->root != NULL;
  99 }
 100 
 101 static void
 102 add_root_node(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 103 {
 104     private_data_t *priv = NULL;
 105 
 106     /* has_root_node will assert if out is NULL, so no need to do it here */
 107     if (has_root_node(out)) {
 108         return;
 109     }
 110 
 111     priv = out->priv;
 112 
 113     if (priv->legacy_xml) {
 114         priv->root = pcmk__xe_create(NULL, PCMK_XE_CRM_MON);
 115         crm_xml_add(priv->root, PCMK_XA_VERSION, PACEMAKER_VERSION);
 116     } else {
 117         priv->root = pcmk__xe_create(NULL, PCMK_XE_PACEMAKER_RESULT);
 118         crm_xml_add(priv->root, PCMK_XA_API_VERSION, PCMK__API_VERSION);
 119         crm_xml_add(priv->root, PCMK_XA_REQUEST,
 120                     pcmk__s(out->request, "libpacemaker"));
 121     }
 122 
 123     priv->parent_q = g_queue_new();
 124     g_queue_push_tail(priv->parent_q, priv->root);
 125 }
 126 
 127 static void
 128 xml_free_priv(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 129     private_data_t *priv = NULL;
 130 
 131     if (out == NULL || out->priv == NULL) {
 132         return;
 133     }
 134 
 135     priv = out->priv;
 136 
 137     if (has_root_node(out)) {
 138         free_xml(priv->root);
 139         /* The elements of parent_q are xmlNodes that are a part of the
 140          * priv->root document, so the above line already frees them.  Don't
 141          * call g_queue_free_full here.
 142          */
 143         g_queue_free(priv->parent_q);
 144     }
 145 
 146     g_slist_free_full(priv->errors, free);
 147     free(priv);
 148     out->priv = NULL;
 149 }
 150 
 151 static bool
 152 xml_init(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 153     private_data_t *priv = NULL;
 154 
 155     pcmk__assert(out != NULL);
 156 
 157     /* If xml_init was previously called on this output struct, just return. */
 158     if (out->priv != NULL) {
 159         return true;
 160     } else {
 161         out->priv = calloc(1, sizeof(private_data_t));
 162         if (out->priv == NULL) {
 163             return false;
 164         }
 165 
 166         priv = out->priv;
 167     }
 168 
 169     priv->errors = NULL;
 170 
 171     return true;
 172 }
 173 
 174 static void
 175 add_error_node(gpointer data, gpointer user_data) {
     /* [previous][next][first][last][top][bottom][index][help] */
 176     const char *str = (const char *) data;
 177     xmlNodePtr node = (xmlNodePtr) user_data;
 178 
 179     node = pcmk__xe_create(node, PCMK_XE_ERROR);
 180     pcmk__xe_set_content(node, "%s", str);
 181 }
 182 
 183 static void
 184 xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
     /* [previous][next][first][last][top][bottom][index][help] */
 185     private_data_t *priv = NULL;
 186     xmlNodePtr node;
 187 
 188     pcmk__assert(out != NULL);
 189     priv = out->priv;
 190 
 191     if (priv == NULL) {
 192         return;
 193     }
 194 
 195     add_root_node(out);
 196 
 197     if (priv->legacy_xml) {
 198         GSList *node = priv->errors;
 199 
 200         if (exit_status != CRM_EX_OK) {
 201             fprintf(stderr, "%s\n", crm_exit_str(exit_status));
 202         }
 203 
 204         while (node != NULL) {
 205             fprintf(stderr, "%s\n", (char *) node->data);
 206             node = node->next;
 207         }
 208     } else {
 209         char *rc_as_str = pcmk__itoa(exit_status);
 210 
 211         node = pcmk__xe_create(priv->root, PCMK_XE_STATUS);
 212         pcmk__xe_set_props(node,
 213                            PCMK_XA_CODE, rc_as_str,
 214                            PCMK_XA_MESSAGE, crm_exit_str(exit_status),
 215                            NULL);
 216 
 217         if (g_slist_length(priv->errors) > 0) {
 218             xmlNodePtr errors_node = pcmk__xe_create(node, PCMK_XE_ERRORS);
 219             g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
 220         }
 221 
 222         free(rc_as_str);
 223     }
 224 
 225     if (print) {
 226         pcmk__xml2fd(fileno(out->dest), priv->root);
 227     }
 228 
 229     if (copy_dest != NULL) {
 230         *copy_dest = pcmk__xml_copy(NULL, priv->root);
 231     }
 232 }
 233 
 234 static void
 235 xml_reset(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 236     pcmk__assert(out != NULL);
 237 
 238     out->dest = freopen(NULL, "w", out->dest);
 239     pcmk__assert(out->dest != NULL);
 240 
 241     xml_free_priv(out);
 242     xml_init(out);
 243 }
 244 
 245 static void
 246 xml_subprocess_output(pcmk__output_t *out, int exit_status,
     /* [previous][next][first][last][top][bottom][index][help] */
 247                       const char *proc_stdout, const char *proc_stderr) {
 248     xmlNodePtr node, child_node;
 249     char *rc_as_str = NULL;
 250 
 251     pcmk__assert(out != NULL);
 252 
 253     rc_as_str = pcmk__itoa(exit_status);
 254 
 255     node = pcmk__output_xml_create_parent(out, PCMK_XE_COMMAND,
 256                                           PCMK_XA_CODE, rc_as_str,
 257                                           NULL);
 258 
 259     if (proc_stdout != NULL) {
 260         child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT);
 261         pcmk__xe_set_content(child_node, "%s", proc_stdout);
 262         crm_xml_add(child_node, PCMK_XA_SOURCE, "stdout");
 263     }
 264 
 265     if (proc_stderr != NULL) {
 266         child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT);
 267         pcmk__xe_set_content(child_node, "%s", proc_stderr);
 268         crm_xml_add(child_node, PCMK_XA_SOURCE, "stderr");
 269     }
 270 
 271     free(rc_as_str);
 272 }
 273 
 274 static void
 275 xml_version(pcmk__output_t *out, bool extended) {
     /* [previous][next][first][last][top][bottom][index][help] */
 276     const char *author = "Andrew Beekhof and the Pacemaker project "
 277                          "contributors";
 278     pcmk__assert(out != NULL);
 279 
 280     pcmk__output_create_xml_node(out, PCMK_XE_VERSION,
 281                                  PCMK_XA_PROGRAM, "Pacemaker",
 282                                  PCMK_XA_VERSION, PACEMAKER_VERSION,
 283                                  PCMK_XA_AUTHOR, author,
 284                                  PCMK_XA_BUILD, BUILD_VERSION,
 285                                  PCMK_XA_FEATURES, CRM_FEATURES,
 286                                  NULL);
 287 }
 288 
 289 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 290 static void
 291 xml_err(pcmk__output_t *out, const char *format, ...) {
 292     private_data_t *priv = NULL;
 293     int len = 0;
 294     char *buf = NULL;
 295     va_list ap;
 296 
 297     pcmk__assert((out != NULL) && (out->priv != NULL));
 298     priv = out->priv;
 299 
 300     add_root_node(out);
 301 
 302     va_start(ap, format);
 303     len = vasprintf(&buf, format, ap);
 304     pcmk__assert(len > 0);
 305     va_end(ap);
 306 
 307     priv->errors = g_slist_append(priv->errors, buf);
 308 }
 309 
 310 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 311 static int
 312 xml_info(pcmk__output_t *out, const char *format, ...) {
 313     return pcmk_rc_no_output;
 314 }
 315 
 316 static void
 317 xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 318     xmlNodePtr parent = NULL;
 319     xmlNodePtr cdata_node = NULL;
 320 
 321     pcmk__assert(out != NULL);
 322 
 323     parent = pcmk__output_create_xml_node(out, name, NULL);
 324     if (parent == NULL) {
 325         return;
 326     }
 327     cdata_node = xmlNewCDataBlock(parent->doc, (pcmkXmlStr) buf, strlen(buf));
 328     xmlAddChild(parent, cdata_node);
 329 }
 330 
 331 G_GNUC_PRINTF(4, 5)
     /* [previous][next][first][last][top][bottom][index][help] */
 332 static void
 333 xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
 334                const char *format, ...) {
 335     va_list ap;
 336     char *name = NULL;
 337     char *buf = NULL;
 338     int len;
 339     private_data_t *priv = NULL;
 340 
 341     pcmk__assert((out != NULL) && (out->priv != NULL));
 342     priv = out->priv;
 343 
 344     va_start(ap, format);
 345     len = vasprintf(&buf, format, ap);
 346     pcmk__assert(len >= 0);
 347     va_end(ap);
 348 
 349     for (const subst_t *s = substitutions; s->from != NULL; s++) {
 350         if (strcmp(s->from, buf) == 0) {
 351             name = g_strdup(s->to);
 352             break;
 353         }
 354     }
 355 
 356     if (name == NULL) {
 357         name = g_ascii_strdown(buf, -1);
 358     }
 359 
 360     if (priv->list_element) {
 361         pcmk__output_xml_create_parent(out, PCMK_XE_LIST,
 362                                        PCMK_XA_NAME, name,
 363                                        NULL);
 364     } else {
 365         pcmk__output_xml_create_parent(out, name, NULL);
 366     }
 367 
 368     g_free(name);
 369     free(buf);
 370 }
 371 
 372 G_GNUC_PRINTF(3, 4)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 static void
 374 xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
 375     xmlNodePtr item_node = NULL;
 376     va_list ap;
 377     char *buf = NULL;
 378     int len;
 379 
 380     pcmk__assert(out != NULL);
 381 
 382     va_start(ap, format);
 383     len = vasprintf(&buf, format, ap);
 384     pcmk__assert(len >= 0);
 385     va_end(ap);
 386 
 387     item_node = pcmk__output_create_xml_text_node(out, PCMK_XE_ITEM, buf);
 388 
 389     if (name != NULL) {
 390         crm_xml_add(item_node, PCMK_XA_NAME, name);
 391     }
 392 
 393     free(buf);
 394 }
 395 
 396 static void
 397 xml_increment_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 398     /* This function intentially left blank */
 399 }
 400 
 401 static void
 402 xml_end_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 403     private_data_t *priv = NULL;
 404 
 405     pcmk__assert((out != NULL) && (out->priv != NULL));
 406     priv = out->priv;
 407 
 408     if (priv->list_element) {
 409         char *buf = NULL;
 410         xmlNodePtr node;
 411 
 412         /* Do not free node here - it's still part of the document */
 413         node = g_queue_pop_tail(priv->parent_q);
 414         buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
 415         crm_xml_add(node, PCMK_XA_COUNT, buf);
 416         free(buf);
 417     } else {
 418         /* Do not free this result - it's still part of the document */
 419         g_queue_pop_tail(priv->parent_q);
 420     }
 421 }
 422 
 423 static bool
 424 xml_is_quiet(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 425     return false;
 426 }
 427 
 428 static void
 429 xml_spacer(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 430     /* This function intentionally left blank */
 431 }
 432 
 433 static void
 434 xml_progress(pcmk__output_t *out, bool end) {
     /* [previous][next][first][last][top][bottom][index][help] */
 435     /* This function intentionally left blank */
 436 }
 437 
 438 pcmk__output_t *
 439 pcmk__mk_xml_output(char **argv) {
     /* [previous][next][first][last][top][bottom][index][help] */
 440     pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
 441 
 442     if (retval == NULL) {
 443         return NULL;
 444     }
 445 
 446     retval->fmt_name = "xml";
 447     retval->request = pcmk__quote_cmdline(argv);
 448 
 449     retval->init = xml_init;
 450     retval->free_priv = xml_free_priv;
 451     retval->finish = xml_finish;
 452     retval->reset = xml_reset;
 453 
 454     retval->register_message = pcmk__register_message;
 455     retval->message = pcmk__call_message;
 456 
 457     retval->subprocess_output = xml_subprocess_output;
 458     retval->version = xml_version;
 459     retval->info = xml_info;
 460     retval->transient = xml_info;
 461     retval->err = xml_err;
 462     retval->output_xml = xml_output_xml;
 463 
 464     retval->begin_list = xml_begin_list;
 465     retval->list_item = xml_list_item;
 466     retval->increment_list = xml_increment_list;
 467     retval->end_list = xml_end_list;
 468 
 469     retval->is_quiet = xml_is_quiet;
 470     retval->spacer = xml_spacer;
 471     retval->progress = xml_progress;
 472     retval->prompt = pcmk__text_prompt;
 473 
 474     return retval;
 475 }
 476 
 477 xmlNodePtr
 478 pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name, ...) {
     /* [previous][next][first][last][top][bottom][index][help] */
 479     va_list args;
 480     xmlNodePtr node = NULL;
 481 
 482     pcmk__assert(out != NULL);
 483     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 484 
 485     node = pcmk__output_create_xml_node(out, name, NULL);
 486 
 487     va_start(args, name);
 488     pcmk__xe_set_propv(node, args);
 489     va_end(args);
 490 
 491     pcmk__output_xml_push_parent(out, node);
 492     return node;
 493 }
 494 
 495 void
 496 pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node) {
     /* [previous][next][first][last][top][bottom][index][help] */
 497     private_data_t *priv = NULL;
 498     xmlNodePtr parent = NULL;
 499 
 500     pcmk__assert((out != NULL) && (out->priv != NULL) && (node != NULL));
 501     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
 502 
 503     add_root_node(out);
 504 
 505     priv = out->priv;
 506     parent = g_queue_peek_tail(priv->parent_q);
 507 
 508     // Shouldn't happen unless the caller popped priv->root
 509     CRM_CHECK(parent != NULL, return);
 510 
 511     pcmk__xml_copy(parent, node);
 512 }
 513 
 514 xmlNodePtr
 515 pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) {
     /* [previous][next][first][last][top][bottom][index][help] */
 516     xmlNodePtr node = NULL;
 517     private_data_t *priv = NULL;
 518     va_list args;
 519 
 520     pcmk__assert((out != NULL) && (out->priv != NULL));
 521     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 522 
 523     add_root_node(out);
 524 
 525     priv = out->priv;
 526 
 527     node = pcmk__xe_create(g_queue_peek_tail(priv->parent_q), name);
 528     va_start(args, name);
 529     pcmk__xe_set_propv(node, args);
 530     va_end(args);
 531 
 532     return node;
 533 }
 534 
 535 xmlNodePtr
 536 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
     /* [previous][next][first][last][top][bottom][index][help] */
 537     xmlNodePtr node = NULL;
 538 
 539     pcmk__assert(out != NULL);
 540     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 541 
 542     node = pcmk__output_create_xml_node(out, name, NULL);
 543     pcmk__xe_set_content(node, "%s", content);
 544     return node;
 545 }
 546 
 547 void
 548 pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) {
     /* [previous][next][first][last][top][bottom][index][help] */
 549     private_data_t *priv = NULL;
 550 
 551     pcmk__assert((out != NULL) && (out->priv != NULL) && (parent != NULL));
 552     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
 553 
 554     add_root_node(out);
 555 
 556     priv = out->priv;
 557 
 558     g_queue_push_tail(priv->parent_q, parent);
 559 }
 560 
 561 void
 562 pcmk__output_xml_pop_parent(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 563     private_data_t *priv = NULL;
 564 
 565     pcmk__assert((out != NULL) && (out->priv != NULL));
 566     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
 567 
 568     add_root_node(out);
 569 
 570     priv = out->priv;
 571 
 572     pcmk__assert(g_queue_get_length(priv->parent_q) > 0);
 573     /* Do not free this result - it's still part of the document */
 574     g_queue_pop_tail(priv->parent_q);
 575 }
 576 
 577 xmlNodePtr
 578 pcmk__output_xml_peek_parent(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 579     private_data_t *priv = NULL;
 580 
 581     pcmk__assert((out != NULL) && (out->priv != NULL));
 582     CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
 583 
 584     add_root_node(out);
 585 
 586     priv = out->priv;
 587 
 588     /* If queue is empty NULL will be returned */
 589     return g_queue_peek_tail(priv->parent_q);
 590 }
 591 
 592 bool
 593 pcmk__output_get_legacy_xml(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 594 {
 595     private_data_t *priv = NULL;
 596 
 597     pcmk__assert(out != NULL);
 598 
 599     if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
 600         return false;
 601     }
 602 
 603     pcmk__assert(out->priv != NULL);
 604 
 605     priv = out->priv;
 606     return priv->legacy_xml;
 607 }
 608 
 609 void
 610 pcmk__output_set_legacy_xml(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 611 {
 612     private_data_t *priv = NULL;
 613 
 614     pcmk__assert(out != NULL);
 615 
 616     if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
 617         return;
 618     }
 619 
 620     pcmk__assert(out->priv != NULL);
 621 
 622     priv = out->priv;
 623     priv->legacy_xml = true;
 624 }
 625 
 626 void
 627 pcmk__output_enable_list_element(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 628 {
 629     private_data_t *priv = NULL;
 630 
 631     pcmk__assert(out != NULL);
 632 
 633     if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
 634         return;
 635     }
 636 
 637     pcmk__assert(out->priv != NULL);
 638 
 639     priv = out->priv;
 640     priv->list_element = true;
 641 }

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