root/lib/pacemaker/pcmk_output.c

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

DEFINITIONS

This source file includes following definitions.
  1. colocations_header
  2. colocations_xml_node
  3. do_locations_list_xml
  4. PCMK__OUTPUT_ARGS
  5. PCMK__OUTPUT_ARGS
  6. PCMK__OUTPUT_ARGS
  7. PCMK__OUTPUT_ARGS
  8. PCMK__OUTPUT_ARGS
  9. PCMK__OUTPUT_ARGS
  10. PCMK__OUTPUT_ARGS
  11. PCMK__OUTPUT_ARGS
  12. PCMK__OUTPUT_ARGS
  13. PCMK__OUTPUT_ARGS
  14. PCMK__OUTPUT_ARGS
  15. PCMK__OUTPUT_ARGS
  16. PCMK__OUTPUT_ARGS
  17. PCMK__OUTPUT_ARGS
  18. PCMK__OUTPUT_ARGS
  19. PCMK__OUTPUT_ARGS
  20. PCMK__OUTPUT_ARGS
  21. PCMK__OUTPUT_ARGS
  22. PCMK__OUTPUT_ARGS
  23. PCMK__OUTPUT_ARGS
  24. PCMK__OUTPUT_ARGS
  25. PCMK__OUTPUT_ARGS
  26. PCMK__OUTPUT_ARGS
  27. PCMK__OUTPUT_ARGS
  28. PCMK__OUTPUT_ARGS
  29. PCMK__OUTPUT_ARGS
  30. add_digest_xml
  31. PCMK__OUTPUT_ARGS
  32. PCMK__OUTPUT_ARGS
  33. PCMK__OUTPUT_ARGS
  34. PCMK__OUTPUT_ARGS
  35. PCMK__OUTPUT_ARGS
  36. PCMK__OUTPUT_ARGS
  37. PCMK__OUTPUT_ARGS
  38. PCMK__OUTPUT_ARGS
  39. PCMK__OUTPUT_ARGS
  40. PCMK__OUTPUT_ARGS
  41. PCMK__OUTPUT_ARGS
  42. PCMK__OUTPUT_ARGS
  43. PCMK__OUTPUT_ARGS
  44. PCMK__OUTPUT_ARGS
  45. PCMK__OUTPUT_ARGS
  46. PCMK__OUTPUT_ARGS
  47. PCMK__OUTPUT_ARGS
  48. PCMK__OUTPUT_ARGS
  49. PCMK__OUTPUT_ARGS
  50. PCMK__OUTPUT_ARGS
  51. PCMK__OUTPUT_ARGS
  52. PCMK__OUTPUT_ARGS
  53. PCMK__OUTPUT_ARGS
  54. PCMK__OUTPUT_ARGS
  55. PCMK__OUTPUT_ARGS
  56. PCMK__OUTPUT_ARGS
  57. PCMK__OUTPUT_ARGS
  58. PCMK__OUTPUT_ARGS
  59. PCMK__OUTPUT_ARGS
  60. PCMK__OUTPUT_ARGS
  61. PCMK__OUTPUT_ARGS
  62. PCMK__OUTPUT_ARGS
  63. PCMK__OUTPUT_ARGS
  64. PCMK__OUTPUT_ARGS
  65. PCMK__OUTPUT_ARGS
  66. PCMK__OUTPUT_ARGS
  67. PCMK__OUTPUT_ARGS
  68. add_ticket_element_with_constraints
  69. add_resource_element
  70. PCMK__OUTPUT_ARGS
  71. PCMK__OUTPUT_ARGS
  72. add_ticket_element
  73. PCMK__OUTPUT_ARGS
  74. pcmk__register_lib_messages

   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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <crm/common/output.h>
  12 #include <crm/common/results.h>
  13 #include <crm/common/xml.h>
  14 #include <crm/stonith-ng.h>
  15 #include <crm/fencing/internal.h>   // stonith__*
  16 #include <crm/pengine/internal.h>
  17 #include <libxml/tree.h>
  18 #include <pacemaker-internal.h>
  19 
  20 #include <inttypes.h>
  21 #include <stdint.h>
  22 
  23 static char *
  24 colocations_header(pcmk_resource_t *rsc, pcmk__colocation_t *cons,
     /* [previous][next][first][last][top][bottom][index][help] */
  25                    bool dependents) {
  26     char *retval = NULL;
  27 
  28     if (cons->primary_role > pcmk_role_started) {
  29         retval = crm_strdup_printf("%s (score=%s, %s role=%s, id=%s)",
  30                                    rsc->id, pcmk_readable_score(cons->score),
  31                                    (dependents? "needs" : "with"),
  32                                    pcmk_role_text(cons->primary_role),
  33                                    cons->id);
  34     } else {
  35         retval = crm_strdup_printf("%s (score=%s, id=%s)",
  36                                    rsc->id, pcmk_readable_score(cons->score),
  37                                    cons->id);
  38     }
  39     return retval;
  40 }
  41 
  42 static void
  43 colocations_xml_node(pcmk__output_t *out, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
  44                      pcmk__colocation_t *cons) {
  45     xmlNodePtr node = NULL;
  46 
  47     node = pcmk__output_create_xml_node(out, PCMK_XE_RSC_COLOCATION,
  48                                         PCMK_XA_ID, cons->id,
  49                                         PCMK_XA_RSC, cons->dependent->id,
  50                                         PCMK_XA_WITH_RSC, cons->primary->id,
  51                                         PCMK_XA_SCORE,
  52                                         pcmk_readable_score(cons->score),
  53                                         NULL);
  54 
  55     if (cons->node_attribute) {
  56         xmlSetProp(node, (pcmkXmlStr) PCMK_XA_NODE_ATTRIBUTE,
  57                    (pcmkXmlStr) cons->node_attribute);
  58     }
  59 
  60     if (cons->dependent_role != pcmk_role_unknown) {
  61         xmlSetProp(node, (pcmkXmlStr) PCMK_XA_RSC_ROLE,
  62                    (pcmkXmlStr) pcmk_role_text(cons->dependent_role));
  63     }
  64 
  65     if (cons->primary_role != pcmk_role_unknown) {
  66         xmlSetProp(node, (pcmkXmlStr) PCMK_XA_WITH_RSC_ROLE,
  67                    (pcmkXmlStr) pcmk_role_text(cons->primary_role));
  68     }
  69 }
  70 
  71 static int
  72 do_locations_list_xml(pcmk__output_t *out, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
  73                       bool add_header)
  74 {
  75     GList *lpc = NULL;
  76     GList *list = rsc->rsc_location;
  77     int rc = pcmk_rc_no_output;
  78 
  79     for (lpc = list; lpc != NULL; lpc = lpc->next) {
  80         pcmk__location_t *cons = lpc->data;
  81 
  82         GList *lpc2 = NULL;
  83 
  84         for (lpc2 = cons->nodes; lpc2 != NULL; lpc2 = lpc2->next) {
  85             pcmk_node_t *node = (pcmk_node_t *) lpc2->data;
  86 
  87             if (add_header) {
  88                 PCMK__OUTPUT_LIST_HEADER(out, false, rc, "locations");
  89             }
  90 
  91             pcmk__output_create_xml_node(out, PCMK_XE_RSC_LOCATION,
  92                                          PCMK_XA_NODE, node->details->uname,
  93                                          PCMK_XA_RSC, rsc->id,
  94                                          PCMK_XA_ID, cons->id,
  95                                          PCMK_XA_SCORE,
  96                                          pcmk_readable_score(node->weight),
  97                                          NULL);
  98         }
  99     }
 100 
 101     if (add_header) {
 102         PCMK__OUTPUT_LIST_FOOTER(out, rc);
 103     }
 104 
 105     return rc;
 106 }
 107 
 108 PCMK__OUTPUT_ARGS("rsc-action-item", "const char *", "pcmk_resource_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
 109                   "pcmk_node_t *", "pcmk_node_t *", "pcmk_action_t *",
 110                   "pcmk_action_t *")
 111 static int
 112 rsc_action_item(pcmk__output_t *out, va_list args)
 113 {
 114     const char *change = va_arg(args, const char *);
 115     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 116     pcmk_node_t *origin = va_arg(args, pcmk_node_t *);
 117     pcmk_node_t *destination = va_arg(args, pcmk_node_t *);
 118     pcmk_action_t *action = va_arg(args, pcmk_action_t *);
 119     pcmk_action_t *source = va_arg(args, pcmk_action_t *);
 120 
 121     int len = 0;
 122     char *reason = NULL;
 123     char *details = NULL;
 124     bool same_host = false;
 125     bool same_role = false;
 126     bool need_role = false;
 127 
 128     static int rsc_width = 5;
 129     static int detail_width = 5;
 130 
 131     CRM_ASSERT(action);
 132     CRM_ASSERT(destination != NULL || origin != NULL);
 133 
 134     if (source == NULL) {
 135         source = action;
 136     }
 137 
 138     len = strlen(rsc->id);
 139     if (len > rsc_width) {
 140         rsc_width = len + 2;
 141     }
 142 
 143     if ((rsc->role > pcmk_role_started)
 144         || (rsc->next_role > pcmk_role_unpromoted)) {
 145         need_role = true;
 146     }
 147 
 148     if (pcmk__same_node(origin, destination)) {
 149         same_host = true;
 150     }
 151 
 152     if (rsc->role == rsc->next_role) {
 153         same_role = true;
 154     }
 155 
 156     if (need_role && (origin == NULL)) {
 157         /* Starting and promoting a promotable clone instance */
 158         details = crm_strdup_printf("%s -> %s %s", pcmk_role_text(rsc->role),
 159                                     pcmk_role_text(rsc->next_role),
 160                                     pcmk__node_name(destination));
 161 
 162     } else if (origin == NULL) {
 163         /* Starting a resource */
 164         details = crm_strdup_printf("%s", pcmk__node_name(destination));
 165 
 166     } else if (need_role && (destination == NULL)) {
 167         /* Stopping a promotable clone instance */
 168         details = crm_strdup_printf("%s %s", pcmk_role_text(rsc->role),
 169                                     pcmk__node_name(origin));
 170 
 171     } else if (destination == NULL) {
 172         /* Stopping a resource */
 173         details = crm_strdup_printf("%s", pcmk__node_name(origin));
 174 
 175     } else if (need_role && same_role && same_host) {
 176         /* Recovering, restarting or re-promoting a promotable clone instance */
 177         details = crm_strdup_printf("%s %s", pcmk_role_text(rsc->role),
 178                                     pcmk__node_name(origin));
 179 
 180     } else if (same_role && same_host) {
 181         /* Recovering or Restarting a normal resource */
 182         details = crm_strdup_printf("%s", pcmk__node_name(origin));
 183 
 184     } else if (need_role && same_role) {
 185         /* Moving a promotable clone instance */
 186         details = crm_strdup_printf("%s -> %s %s", pcmk__node_name(origin),
 187                                     pcmk__node_name(destination),
 188                                     pcmk_role_text(rsc->role));
 189 
 190     } else if (same_role) {
 191         /* Moving a normal resource */
 192         details = crm_strdup_printf("%s -> %s", pcmk__node_name(origin),
 193                                     pcmk__node_name(destination));
 194 
 195     } else if (same_host) {
 196         /* Promoting or demoting a promotable clone instance */
 197         details = crm_strdup_printf("%s -> %s %s", pcmk_role_text(rsc->role),
 198                                     pcmk_role_text(rsc->next_role),
 199                                     pcmk__node_name(origin));
 200 
 201     } else {
 202         /* Moving and promoting/demoting */
 203         details = crm_strdup_printf("%s %s -> %s %s",
 204                                     pcmk_role_text(rsc->role),
 205                                     pcmk__node_name(origin),
 206                                     pcmk_role_text(rsc->next_role),
 207                                     pcmk__node_name(destination));
 208     }
 209 
 210     len = strlen(details);
 211     if (len > detail_width) {
 212         detail_width = len;
 213     }
 214 
 215     if ((source->reason != NULL)
 216         && !pcmk_is_set(action->flags, pcmk_action_runnable)) {
 217         reason = crm_strdup_printf("due to %s (blocked)", source->reason);
 218 
 219     } else if (source->reason) {
 220         reason = crm_strdup_printf("due to %s", source->reason);
 221 
 222     } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
 223         reason = strdup("blocked");
 224 
 225     }
 226 
 227     out->list_item(out, NULL, "%-8s   %-*s   ( %*s )%s%s",
 228                    change, rsc_width, rsc->id, detail_width, details,
 229                    ((reason == NULL)? "" : "  "), pcmk__s(reason, ""));
 230 
 231     free(details);
 232     free(reason);
 233     return pcmk_rc_ok;
 234 }
 235 
 236 PCMK__OUTPUT_ARGS("rsc-action-item", "const char *", "pcmk_resource_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
 237                   "pcmk_node_t *", "pcmk_node_t *", "pcmk_action_t *",
 238                   "pcmk_action_t *")
 239 static int
 240 rsc_action_item_xml(pcmk__output_t *out, va_list args)
 241 {
 242     const char *change = va_arg(args, const char *);
 243     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 244     pcmk_node_t *origin = va_arg(args, pcmk_node_t *);
 245     pcmk_node_t *destination = va_arg(args, pcmk_node_t *);
 246     pcmk_action_t *action = va_arg(args, pcmk_action_t *);
 247     pcmk_action_t *source = va_arg(args, pcmk_action_t *);
 248 
 249     char *change_str = NULL;
 250 
 251     bool same_host = false;
 252     bool same_role = false;
 253     bool need_role = false;
 254     xmlNode *xml = NULL;
 255 
 256     CRM_ASSERT(action);
 257     CRM_ASSERT(destination != NULL || origin != NULL);
 258 
 259     if (source == NULL) {
 260         source = action;
 261     }
 262 
 263     if ((rsc->role > pcmk_role_started)
 264         || (rsc->next_role > pcmk_role_unpromoted)) {
 265         need_role = true;
 266     }
 267 
 268     if (pcmk__same_node(origin, destination)) {
 269         same_host = true;
 270     }
 271 
 272     if (rsc->role == rsc->next_role) {
 273         same_role = true;
 274     }
 275 
 276     change_str = g_ascii_strdown(change, -1);
 277     xml = pcmk__output_create_xml_node(out, PCMK_XE_RSC_ACTION,
 278                                        PCMK_XA_ACTION, change_str,
 279                                        PCMK_XA_RESOURCE, rsc->id,
 280                                        NULL);
 281     g_free(change_str);
 282 
 283     if (need_role && (origin == NULL)) {
 284         /* Starting and promoting a promotable clone instance */
 285         pcmk__xe_set_props(xml,
 286                            PCMK_XA_ROLE, pcmk_role_text(rsc->role),
 287                            PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role),
 288                            PCMK_XA_DEST, destination->details->uname,
 289                            NULL);
 290 
 291     } else if (origin == NULL) {
 292         /* Starting a resource */
 293         crm_xml_add(xml, PCMK_XA_NODE, destination->details->uname);
 294 
 295     } else if (need_role && (destination == NULL)) {
 296         /* Stopping a promotable clone instance */
 297         pcmk__xe_set_props(xml,
 298                            PCMK_XA_ROLE, pcmk_role_text(rsc->role),
 299                            PCMK_XA_NODE, origin->details->uname,
 300                            NULL);
 301 
 302     } else if (destination == NULL) {
 303         /* Stopping a resource */
 304         crm_xml_add(xml, PCMK_XA_NODE, origin->details->uname);
 305 
 306     } else if (need_role && same_role && same_host) {
 307         /* Recovering, restarting or re-promoting a promotable clone instance */
 308         pcmk__xe_set_props(xml,
 309                            PCMK_XA_ROLE, pcmk_role_text(rsc->role),
 310                            PCMK_XA_SOURCE, origin->details->uname,
 311                            NULL);
 312 
 313     } else if (same_role && same_host) {
 314         /* Recovering or Restarting a normal resource */
 315         crm_xml_add(xml, PCMK_XA_SOURCE, origin->details->uname);
 316 
 317     } else if (need_role && same_role) {
 318         /* Moving a promotable clone instance */
 319         pcmk__xe_set_props(xml,
 320                            PCMK_XA_SOURCE, origin->details->uname,
 321                            PCMK_XA_DEST, destination->details->uname,
 322                            PCMK_XA_ROLE, pcmk_role_text(rsc->role),
 323                            NULL);
 324 
 325     } else if (same_role) {
 326         /* Moving a normal resource */
 327         pcmk__xe_set_props(xml,
 328                            PCMK_XA_SOURCE, origin->details->uname,
 329                            PCMK_XA_DEST, destination->details->uname,
 330                            NULL);
 331 
 332     } else if (same_host) {
 333         /* Promoting or demoting a promotable clone instance */
 334         pcmk__xe_set_props(xml,
 335                            PCMK_XA_ROLE, pcmk_role_text(rsc->role),
 336                            PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role),
 337                            PCMK_XA_SOURCE, origin->details->uname,
 338                            NULL);
 339 
 340     } else {
 341         /* Moving and promoting/demoting */
 342         pcmk__xe_set_props(xml,
 343                            PCMK_XA_ROLE, pcmk_role_text(rsc->role),
 344                            PCMK_XA_SOURCE, origin->details->uname,
 345                            PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role),
 346                            PCMK_XA_DEST, destination->details->uname,
 347                            NULL);
 348     }
 349 
 350     if ((source->reason != NULL)
 351         && !pcmk_is_set(action->flags, pcmk_action_runnable)) {
 352         pcmk__xe_set_props(xml,
 353                            PCMK_XA_REASON, source->reason,
 354                            PCMK_XA_BLOCKED, PCMK_VALUE_TRUE,
 355                            NULL);
 356 
 357     } else if (source->reason != NULL) {
 358         crm_xml_add(xml, PCMK_XA_REASON, source->reason);
 359 
 360     } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
 361         pcmk__xe_set_bool_attr(xml, PCMK_XA_BLOCKED, true);
 362 
 363     }
 364 
 365     return pcmk_rc_ok;
 366 }
 367 
 368 PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pcmk_resource_t *", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 369 static int
 370 rsc_is_colocated_with_list(pcmk__output_t *out, va_list args) {
 371     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 372     bool recursive = va_arg(args, int);
 373 
 374     int rc = pcmk_rc_no_output;
 375 
 376     if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
 377         return rc;
 378     }
 379 
 380     /* We're listing constraints explicitly involving rsc, so use rsc->rsc_cons
 381      * directly rather than rsc->cmds->this_with_colocations().
 382      */
 383     pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
 384     for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
 385         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
 386         char *hdr = NULL;
 387 
 388         PCMK__OUTPUT_LIST_HEADER(out, false, rc,
 389                                  "Resources %s is colocated with", rsc->id);
 390 
 391         if (pcmk_is_set(cons->primary->flags, pcmk_rsc_detect_loop)) {
 392             out->list_item(out, NULL, "%s (id=%s - loop)",
 393                            cons->primary->id, cons->id);
 394             continue;
 395         }
 396 
 397         hdr = colocations_header(cons->primary, cons, false);
 398         out->list_item(out, NULL, "%s", hdr);
 399         free(hdr);
 400 
 401         // Empty list header for indentation of information about this resource
 402         out->begin_list(out, NULL, NULL, NULL);
 403 
 404         out->message(out, "locations-list", cons->primary);
 405         if (recursive) {
 406             out->message(out, "rsc-is-colocated-with-list",
 407                          cons->primary, recursive);
 408         }
 409 
 410         out->end_list(out);
 411     }
 412 
 413     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 414     return rc;
 415 }
 416 
 417 PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pcmk_resource_t *", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 418 static int
 419 rsc_is_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
 420     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 421     bool recursive = va_arg(args, int);
 422 
 423     int rc = pcmk_rc_no_output;
 424 
 425     if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
 426         return rc;
 427     }
 428 
 429     /* We're listing constraints explicitly involving rsc, so use rsc->rsc_cons
 430      * directly rather than rsc->cmds->this_with_colocations().
 431      */
 432     pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
 433     for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
 434         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
 435 
 436         if (pcmk_is_set(cons->primary->flags, pcmk_rsc_detect_loop)) {
 437             colocations_xml_node(out, cons->primary, cons);
 438             continue;
 439         }
 440 
 441         colocations_xml_node(out, cons->primary, cons);
 442         do_locations_list_xml(out, cons->primary, false);
 443 
 444         if (recursive) {
 445             out->message(out, "rsc-is-colocated-with-list",
 446                          cons->primary, recursive);
 447         }
 448     }
 449 
 450     return rc;
 451 }
 452 
 453 PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pcmk_resource_t *", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 454 static int
 455 rscs_colocated_with_list(pcmk__output_t *out, va_list args) {
 456     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 457     bool recursive = va_arg(args, int);
 458 
 459     int rc = pcmk_rc_no_output;
 460 
 461     if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
 462         return rc;
 463     }
 464 
 465     /* We're listing constraints explicitly involving rsc, so use
 466      * rsc->rsc_cons_lhs directly rather than
 467      * rsc->cmds->with_this_colocations().
 468      */
 469     pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
 470     for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
 471         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
 472         char *hdr = NULL;
 473 
 474         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Resources colocated with %s",
 475                                  rsc->id);
 476 
 477         if (pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop)) {
 478             out->list_item(out, NULL, "%s (id=%s - loop)",
 479                            cons->dependent->id, cons->id);
 480             continue;
 481         }
 482 
 483         hdr = colocations_header(cons->dependent, cons, true);
 484         out->list_item(out, NULL, "%s", hdr);
 485         free(hdr);
 486 
 487         // Empty list header for indentation of information about this resource
 488         out->begin_list(out, NULL, NULL, NULL);
 489 
 490         out->message(out, "locations-list", cons->dependent);
 491         if (recursive) {
 492             out->message(out, "rscs-colocated-with-list",
 493                          cons->dependent, recursive);
 494         }
 495 
 496         out->end_list(out);
 497     }
 498 
 499     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 500     return rc;
 501 }
 502 
 503 PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pcmk_resource_t *", "bool")
     /* [previous][next][first][last][top][bottom][index][help] */
 504 static int
 505 rscs_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
 506     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 507     bool recursive = va_arg(args, int);
 508 
 509     int rc = pcmk_rc_no_output;
 510 
 511     if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
 512         return rc;
 513     }
 514 
 515     /* We're listing constraints explicitly involving rsc, so use
 516      * rsc->rsc_cons_lhs directly rather than
 517      * rsc->cmds->with_this_colocations().
 518      */
 519     pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
 520     for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
 521         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
 522 
 523         if (pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop)) {
 524             colocations_xml_node(out, cons->dependent, cons);
 525             continue;
 526         }
 527 
 528         colocations_xml_node(out, cons->dependent, cons);
 529         do_locations_list_xml(out, cons->dependent, false);
 530 
 531         if (recursive) {
 532             out->message(out, "rscs-colocated-with-list",
 533                          cons->dependent, recursive);
 534         }
 535     }
 536 
 537     return rc;
 538 }
 539 
 540 PCMK__OUTPUT_ARGS("locations-list", "pcmk_resource_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 541 static int
 542 locations_list(pcmk__output_t *out, va_list args) {
 543     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 544 
 545     GList *lpc = NULL;
 546     GList *list = rsc->rsc_location;
 547     int rc = pcmk_rc_no_output;
 548 
 549     for (lpc = list; lpc != NULL; lpc = lpc->next) {
 550         pcmk__location_t *cons = lpc->data;
 551 
 552         GList *lpc2 = NULL;
 553 
 554         for (lpc2 = cons->nodes; lpc2 != NULL; lpc2 = lpc2->next) {
 555             pcmk_node_t *node = (pcmk_node_t *) lpc2->data;
 556 
 557             PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Locations");
 558             out->list_item(out, NULL, "Node %s (score=%s, id=%s, rsc=%s)",
 559                            pcmk__node_name(node),
 560                            pcmk_readable_score(node->weight), cons->id,
 561                            rsc->id);
 562         }
 563     }
 564 
 565     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 566     return rc;
 567 }
 568 
 569 PCMK__OUTPUT_ARGS("locations-list", "pcmk_resource_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 570 static int
 571 locations_list_xml(pcmk__output_t *out, va_list args) {
 572     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 573     return do_locations_list_xml(out, rsc, true);
 574 }
 575 
 576 PCMK__OUTPUT_ARGS("locations-and-colocations", "pcmk_resource_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
 577                   "bool", "bool")
 578 static int
 579 locations_and_colocations(pcmk__output_t *out, va_list args)
 580 {
 581     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 582     bool recursive = va_arg(args, int);
 583     bool force = va_arg(args, int);
 584 
 585     pcmk__unpack_constraints(rsc->cluster);
 586 
 587     // Constraints apply to group/clone, not member/instance
 588     if (!force) {
 589         rsc = uber_parent(rsc);
 590     }
 591 
 592     out->message(out, "locations-list", rsc);
 593 
 594     pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
 595     out->message(out, "rscs-colocated-with-list", rsc, recursive);
 596 
 597     pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
 598     out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
 599     return pcmk_rc_ok;
 600 }
 601 
 602 PCMK__OUTPUT_ARGS("locations-and-colocations", "pcmk_resource_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
 603                   "bool", "bool")
 604 static int
 605 locations_and_colocations_xml(pcmk__output_t *out, va_list args)
 606 {
 607     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 608     bool recursive = va_arg(args, int);
 609     bool force = va_arg(args, int);
 610 
 611     pcmk__unpack_constraints(rsc->cluster);
 612 
 613     // Constraints apply to group/clone, not member/instance
 614     if (!force) {
 615         rsc = uber_parent(rsc);
 616     }
 617 
 618     pcmk__output_xml_create_parent(out, PCMK_XE_CONSTRAINTS, NULL);
 619     do_locations_list_xml(out, rsc, false);
 620 
 621     pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
 622     out->message(out, "rscs-colocated-with-list", rsc, recursive);
 623 
 624     pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
 625     out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
 626 
 627     pcmk__output_xml_pop_parent(out);
 628     return pcmk_rc_ok;
 629 }
 630 
 631 PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 632                   "const char *")
 633 static int
 634 health(pcmk__output_t *out, va_list args)
 635 {
 636     const char *sys_from G_GNUC_UNUSED = va_arg(args, const char *);
 637     const char *host_from = va_arg(args, const char *);
 638     const char *fsa_state = va_arg(args, const char *);
 639     const char *result = va_arg(args, const char *);
 640 
 641     return out->info(out, "Controller on %s in state %s: %s",
 642                      pcmk__s(host_from, "unknown node"),
 643                      pcmk__s(fsa_state, "unknown"),
 644                      pcmk__s(result, "unknown result"));
 645 }
 646 
 647 PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 648                   "const char *")
 649 static int
 650 health_text(pcmk__output_t *out, va_list args)
 651 {
 652     if (!out->is_quiet(out)) {
 653         return health(out, args);
 654     } else {
 655         const char *sys_from G_GNUC_UNUSED = va_arg(args, const char *);
 656         const char *host_from G_GNUC_UNUSED = va_arg(args, const char *);
 657         const char *fsa_state = va_arg(args, const char *);
 658         const char *result G_GNUC_UNUSED = va_arg(args, const char *);
 659 
 660         if (fsa_state != NULL) {
 661             pcmk__formatted_printf(out, "%s\n", fsa_state);
 662             return pcmk_rc_ok;
 663         }
 664     }
 665 
 666     return pcmk_rc_no_output;
 667 }
 668 
 669 PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 670                   "const char *")
 671 static int
 672 health_xml(pcmk__output_t *out, va_list args)
 673 {
 674     const char *sys_from = va_arg(args, const char *);
 675     const char *host_from = va_arg(args, const char *);
 676     const char *fsa_state = va_arg(args, const char *);
 677     const char *result = va_arg(args, const char *);
 678 
 679     pcmk__output_create_xml_node(out, pcmk__s(sys_from, ""),
 680                                  PCMK_XA_NODE_NAME, pcmk__s(host_from, ""),
 681                                  PCMK_XA_STATE, pcmk__s(fsa_state, ""),
 682                                  PCMK_XA_RESULT, pcmk__s(result, ""),
 683                                  NULL);
 684     return pcmk_rc_ok;
 685 }
 686 
 687 PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 688                   "enum pcmk_pacemakerd_state", "const char *", "time_t")
 689 static int
 690 pacemakerd_health(pcmk__output_t *out, va_list args)
 691 {
 692     const char *sys_from = va_arg(args, const char *);
 693     enum pcmk_pacemakerd_state state =
 694         (enum pcmk_pacemakerd_state) va_arg(args, int);
 695     const char *state_s = va_arg(args, const char *);
 696     time_t last_updated = va_arg(args, time_t);
 697 
 698     char *last_updated_s = NULL;
 699     int rc = pcmk_rc_ok;
 700 
 701     if (sys_from == NULL) {
 702         if (state == pcmk_pacemakerd_state_remote) {
 703             sys_from = "pacemaker-remoted";
 704         } else {
 705             sys_from = CRM_SYSTEM_MCP;
 706         }
 707     }
 708 
 709     if (state_s == NULL) {
 710         state_s = pcmk__pcmkd_state_enum2friendly(state);
 711     }
 712 
 713     if (last_updated != 0) {
 714         last_updated_s = pcmk__epoch2str(&last_updated,
 715                                          crm_time_log_date
 716                                          |crm_time_log_timeofday
 717                                          |crm_time_log_with_timezone);
 718     }
 719 
 720     rc = out->info(out, "Status of %s: '%s' (last updated %s)",
 721                    sys_from, state_s,
 722                    pcmk__s(last_updated_s, "at unknown time"));
 723 
 724     free(last_updated_s);
 725     return rc;
 726 }
 727 
 728 PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 729                   "enum pcmk_pacemakerd_state", "const char *", "time_t")
 730 static int
 731 pacemakerd_health_html(pcmk__output_t *out, va_list args)
 732 {
 733     const char *sys_from = va_arg(args, const char *);
 734     enum pcmk_pacemakerd_state state =
 735         (enum pcmk_pacemakerd_state) va_arg(args, int);
 736     const char *state_s = va_arg(args, const char *);
 737     time_t last_updated = va_arg(args, time_t);
 738 
 739     char *last_updated_s = NULL;
 740     char *msg = NULL;
 741 
 742     if (sys_from == NULL) {
 743         if (state == pcmk_pacemakerd_state_remote) {
 744             sys_from = "pacemaker-remoted";
 745         } else {
 746             sys_from = CRM_SYSTEM_MCP;
 747         }
 748     }
 749 
 750     if (state_s == NULL) {
 751         state_s = pcmk__pcmkd_state_enum2friendly(state);
 752     }
 753 
 754     if (last_updated != 0) {
 755         last_updated_s = pcmk__epoch2str(&last_updated,
 756                                          crm_time_log_date
 757                                          |crm_time_log_timeofday
 758                                          |crm_time_log_with_timezone);
 759     }
 760 
 761     msg = crm_strdup_printf("Status of %s: '%s' (last updated %s)",
 762                             sys_from, state_s,
 763                             pcmk__s(last_updated_s, "at unknown time"));
 764     pcmk__output_create_html_node(out, "li", NULL, NULL, msg);
 765 
 766     free(msg);
 767     free(last_updated_s);
 768     return pcmk_rc_ok;
 769 }
 770 
 771 PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 772                   "enum pcmk_pacemakerd_state", "const char *", "time_t")
 773 static int
 774 pacemakerd_health_text(pcmk__output_t *out, va_list args)
 775 {
 776     if (!out->is_quiet(out)) {
 777         return pacemakerd_health(out, args);
 778     } else {
 779         const char *sys_from G_GNUC_UNUSED = va_arg(args, const char *);
 780         enum pcmk_pacemakerd_state state =
 781             (enum pcmk_pacemakerd_state) va_arg(args, int);
 782         const char *state_s = va_arg(args, const char *);
 783         time_t last_updated G_GNUC_UNUSED = va_arg(args, time_t);
 784 
 785         if (state_s == NULL) {
 786             state_s = pcmk_pacemakerd_api_daemon_state_enum2text(state);
 787         }
 788         pcmk__formatted_printf(out, "%s\n", state_s);
 789         return pcmk_rc_ok;
 790     }
 791 }
 792 
 793 PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 794                   "enum pcmk_pacemakerd_state", "const char *", "time_t")
 795 static int
 796 pacemakerd_health_xml(pcmk__output_t *out, va_list args)
 797 {
 798     const char *sys_from = va_arg(args, const char *);
 799     enum pcmk_pacemakerd_state state =
 800         (enum pcmk_pacemakerd_state) va_arg(args, int);
 801     const char *state_s = va_arg(args, const char *);
 802     time_t last_updated = va_arg(args, time_t);
 803 
 804     char *last_updated_s = NULL;
 805 
 806     if (sys_from == NULL) {
 807         if (state == pcmk_pacemakerd_state_remote) {
 808             sys_from = "pacemaker-remoted";
 809         } else {
 810             sys_from = CRM_SYSTEM_MCP;
 811         }
 812     }
 813 
 814     if (state_s == NULL) {
 815         state_s = pcmk_pacemakerd_api_daemon_state_enum2text(state);
 816     }
 817 
 818     if (last_updated != 0) {
 819         last_updated_s = pcmk__epoch2str(&last_updated,
 820                                          crm_time_log_date
 821                                          |crm_time_log_timeofday
 822                                          |crm_time_log_with_timezone);
 823     }
 824 
 825     pcmk__output_create_xml_node(out, PCMK_XE_PACEMAKERD,
 826                                  PCMK_XA_SYS_FROM, sys_from,
 827                                  PCMK_XA_STATE, state_s,
 828                                  PCMK_XA_LAST_UPDATED, last_updated_s,
 829                                  NULL);
 830     free(last_updated_s);
 831     return pcmk_rc_ok;
 832 }
 833 
 834 PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 835 static int
 836 profile_default(pcmk__output_t *out, va_list args) {
 837     const char *xml_file = va_arg(args, const char *);
 838     clock_t start = va_arg(args, clock_t);
 839     clock_t end = va_arg(args, clock_t);
 840 
 841     out->list_item(out, NULL, "Testing %s ... %.2f secs", xml_file,
 842                    (end - start) / (float) CLOCKS_PER_SEC);
 843 
 844     return pcmk_rc_ok;
 845 }
 846 
 847 PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 848 static int
 849 profile_xml(pcmk__output_t *out, va_list args) {
 850     const char *xml_file = va_arg(args, const char *);
 851     clock_t start = va_arg(args, clock_t);
 852     clock_t end = va_arg(args, clock_t);
 853 
 854     char *duration = pcmk__ftoa((end - start) / (float) CLOCKS_PER_SEC);
 855 
 856     pcmk__output_create_xml_node(out, PCMK_XE_TIMING,
 857                                  PCMK_XA_FILE, xml_file,
 858                                  PCMK_XA_DURATION, duration,
 859                                  NULL);
 860 
 861     free(duration);
 862     return pcmk_rc_ok;
 863 }
 864 
 865 PCMK__OUTPUT_ARGS("dc", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 866 static int
 867 dc(pcmk__output_t *out, va_list args)
 868 {
 869     const char *dc = va_arg(args, const char *);
 870 
 871     return out->info(out, "Designated Controller is: %s",
 872                      pcmk__s(dc, "not yet elected"));
 873 }
 874 
 875 PCMK__OUTPUT_ARGS("dc", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 876 static int
 877 dc_text(pcmk__output_t *out, va_list args)
 878 {
 879     if (!out->is_quiet(out)) {
 880         return dc(out, args);
 881     } else {
 882         const char *dc = va_arg(args, const char *);
 883 
 884         if (dc != NULL) {
 885             pcmk__formatted_printf(out, "%s\n", pcmk__s(dc, ""));
 886             return pcmk_rc_ok;
 887         }
 888     }
 889 
 890     return pcmk_rc_no_output;
 891 }
 892 
 893 PCMK__OUTPUT_ARGS("dc", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 894 static int
 895 dc_xml(pcmk__output_t *out, va_list args)
 896 {
 897     const char *dc = va_arg(args, const char *);
 898 
 899     pcmk__output_create_xml_node(out, PCMK_XE_DC,
 900                                  PCMK_XA_NODE_NAME, pcmk__s(dc, ""),
 901                                  NULL);
 902     return pcmk_rc_ok;
 903 }
 904 
 905 PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 906                   "const char *", "bool")
 907 static int
 908 crmadmin_node(pcmk__output_t *out, va_list args)
 909 {
 910     const char *type = va_arg(args, const char *);
 911     const char *name = va_arg(args, const char *);
 912     const char *id = va_arg(args, const char *);
 913     bool bash_export = va_arg(args, int);
 914 
 915     if (bash_export) {
 916         return out->info(out, "export %s=%s",
 917                          pcmk__s(name, "<null>"), pcmk__s(id, ""));
 918     } else {
 919         return out->info(out, "%s node: %s (%s)", type ? type : "cluster",
 920                          pcmk__s(name, "<null>"), pcmk__s(id, "<null>"));
 921     }
 922 }
 923 
 924 PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 925                   "const char *", "bool")
 926 static int
 927 crmadmin_node_text(pcmk__output_t *out, va_list args)
 928 {
 929     if (!out->is_quiet(out)) {
 930         return crmadmin_node(out, args);
 931     } else {
 932         const char *type G_GNUC_UNUSED = va_arg(args, const char *);
 933         const char *name = va_arg(args, const char *);
 934         const char *id G_GNUC_UNUSED = va_arg(args, const char *);
 935         bool bash_export G_GNUC_UNUSED = va_arg(args, int);
 936 
 937         pcmk__formatted_printf(out, "%s\n", pcmk__s(name, "<null>"));
 938         return pcmk_rc_ok;
 939     }
 940 }
 941 
 942 PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
 943                   "const char *", "bool")
 944 static int
 945 crmadmin_node_xml(pcmk__output_t *out, va_list args)
 946 {
 947     const char *type = va_arg(args, const char *);
 948     const char *name = va_arg(args, const char *);
 949     const char *id = va_arg(args, const char *);
 950     bool bash_export G_GNUC_UNUSED = va_arg(args, int);
 951 
 952     pcmk__output_create_xml_node(out, PCMK_XE_NODE,
 953                                  PCMK_XA_TYPE, pcmk__s(type, "cluster"),
 954                                  PCMK_XA_NAME, pcmk__s(name, ""),
 955                                  PCMK_XA_ID, pcmk__s(id, ""),
 956                                  NULL);
 957     return pcmk_rc_ok;
 958 }
 959 
 960 PCMK__OUTPUT_ARGS("digests", "const pcmk_resource_t *", "const pcmk_node_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
 961                   "const char *", "guint", "const pcmk__op_digest_t *")
 962 static int
 963 digests_text(pcmk__output_t *out, va_list args)
 964 {
 965     const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
 966     const pcmk_node_t *node = va_arg(args, const pcmk_node_t *);
 967     const char *task = va_arg(args, const char *);
 968     guint interval_ms = va_arg(args, guint);
 969     const pcmk__op_digest_t *digests = va_arg(args, const pcmk__op_digest_t *);
 970 
 971     char *action_desc = NULL;
 972     const char *rsc_desc = "unknown resource";
 973     const char *node_desc = "unknown node";
 974 
 975     if (interval_ms != 0) {
 976         action_desc = crm_strdup_printf("%ums-interval %s action", interval_ms,
 977                                         ((task == NULL)? "unknown" : task));
 978     } else if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
 979         action_desc = strdup("probe action");
 980     } else {
 981         action_desc = crm_strdup_printf("%s action",
 982                                         ((task == NULL)? "unknown" : task));
 983     }
 984     if ((rsc != NULL) && (rsc->id != NULL)) {
 985         rsc_desc = rsc->id;
 986     }
 987     if ((node != NULL) && (node->details->uname != NULL)) {
 988         node_desc = node->details->uname;
 989     }
 990     out->begin_list(out, NULL, NULL, "Digests for %s %s on %s",
 991                     rsc_desc, action_desc, node_desc);
 992     free(action_desc);
 993 
 994     if (digests == NULL) {
 995         out->list_item(out, NULL, "none");
 996         out->end_list(out);
 997         return pcmk_rc_ok;
 998     }
 999     if (digests->digest_all_calc != NULL) {
1000         out->list_item(out, NULL, "%s (all parameters)",
1001                        digests->digest_all_calc);
1002     }
1003     if (digests->digest_secure_calc != NULL) {
1004         out->list_item(out, NULL, "%s (non-private parameters)",
1005                        digests->digest_secure_calc);
1006     }
1007     if (digests->digest_restart_calc != NULL) {
1008         out->list_item(out, NULL, "%s (non-reloadable parameters)",
1009                        digests->digest_restart_calc);
1010     }
1011     out->end_list(out);
1012     return pcmk_rc_ok;
1013 }
1014 
1015 static void
1016 add_digest_xml(xmlNode *parent, const char *type, const char *digest,
     /* [previous][next][first][last][top][bottom][index][help] */
1017                xmlNode *digest_source)
1018 {
1019     if (digest != NULL) {
1020         xmlNodePtr digest_xml = pcmk__xe_create(parent, PCMK_XE_DIGEST);
1021 
1022         crm_xml_add(digest_xml, PCMK_XA_TYPE, pcmk__s(type, "unspecified"));
1023         crm_xml_add(digest_xml, PCMK_XA_HASH, digest);
1024         pcmk__xml_copy(digest_xml, digest_source);
1025     }
1026 }
1027 
1028 PCMK__OUTPUT_ARGS("digests", "const pcmk_resource_t *", "const pcmk_node_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
1029                   "const char *", "guint", "const pcmk__op_digest_t *")
1030 static int
1031 digests_xml(pcmk__output_t *out, va_list args)
1032 {
1033     const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
1034     const pcmk_node_t *node = va_arg(args, const pcmk_node_t *);
1035     const char *task = va_arg(args, const char *);
1036     guint interval_ms = va_arg(args, guint);
1037     const pcmk__op_digest_t *digests = va_arg(args, const pcmk__op_digest_t *);
1038 
1039     char *interval_s = crm_strdup_printf("%ums", interval_ms);
1040     xmlNode *xml = NULL;
1041 
1042     xml = pcmk__output_create_xml_node(out, PCMK_XE_DIGESTS,
1043                                        PCMK_XA_RESOURCE, pcmk__s(rsc->id, ""),
1044                                        PCMK_XA_NODE,
1045                                        pcmk__s(node->details->uname, ""),
1046                                        PCMK_XA_TASK, pcmk__s(task, ""),
1047                                        PCMK_XA_INTERVAL, interval_s,
1048                                        NULL);
1049     free(interval_s);
1050     if (digests != NULL) {
1051         add_digest_xml(xml, "all", digests->digest_all_calc,
1052                        digests->params_all);
1053         add_digest_xml(xml, "nonprivate", digests->digest_secure_calc,
1054                        digests->params_secure);
1055         add_digest_xml(xml, "nonreloadable", digests->digest_restart_calc,
1056                        digests->params_restart);
1057     }
1058     return pcmk_rc_ok;
1059 }
1060 
1061 #define STOP_SANITY_ASSERT(lineno) do {                                 \
1062         if ((current != NULL) && current->details->unclean) {           \
1063             /* It will be a pseudo op */                                \
1064         } else if (stop == NULL) {                                      \
1065             crm_err("%s:%d: No stop action exists for %s",              \
1066                     __func__, lineno, rsc->id);                         \
1067             CRM_ASSERT(stop != NULL);                                   \
1068         } else if (pcmk_is_set(stop->flags, pcmk_action_optional)) {    \
1069             crm_err("%s:%d: Action %s is still optional",               \
1070                     __func__, lineno, stop->uuid);                      \
1071             CRM_ASSERT(!pcmk_is_set(stop->flags, pcmk_action_optional));\
1072         }                                                               \
1073     } while (0)
1074 
1075 PCMK__OUTPUT_ARGS("rsc-action", "pcmk_resource_t *", "pcmk_node_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
1076                   "pcmk_node_t *")
1077 static int
1078 rsc_action_default(pcmk__output_t *out, va_list args)
1079 {
1080     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1081     pcmk_node_t *current = va_arg(args, pcmk_node_t *);
1082     pcmk_node_t *next = va_arg(args, pcmk_node_t *);
1083 
1084     GList *possible_matches = NULL;
1085     char *key = NULL;
1086     int rc = pcmk_rc_no_output;
1087     bool moving = false;
1088 
1089     pcmk_node_t *start_node = NULL;
1090     pcmk_action_t *start = NULL;
1091     pcmk_action_t *stop = NULL;
1092     pcmk_action_t *promote = NULL;
1093     pcmk_action_t *demote = NULL;
1094     pcmk_action_t *reason_op = NULL;
1095 
1096     if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)
1097         || (current == NULL && next == NULL)) {
1098         const bool managed = pcmk_is_set(rsc->flags, pcmk_rsc_managed);
1099 
1100         pcmk__rsc_info(rsc, "Leave   %s\t(%s%s)",
1101                        rsc->id, pcmk_role_text(rsc->role),
1102                        (managed? "" : " unmanaged"));
1103         return rc;
1104     }
1105 
1106     moving = (current != NULL) && (next != NULL)
1107              && !pcmk__same_node(current, next);
1108 
1109     possible_matches = pe__resource_actions(rsc, next, PCMK_ACTION_START,
1110                                             false);
1111     if (possible_matches) {
1112         start = possible_matches->data;
1113         g_list_free(possible_matches);
1114     }
1115 
1116     if ((start == NULL)
1117         || !pcmk_is_set(start->flags, pcmk_action_runnable)) {
1118         start_node = NULL;
1119     } else {
1120         start_node = current;
1121     }
1122     possible_matches = pe__resource_actions(rsc, start_node, PCMK_ACTION_STOP,
1123                                             false);
1124     if (possible_matches) {
1125         stop = possible_matches->data;
1126         g_list_free(possible_matches);
1127     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_stop_unexpected)) {
1128         /* The resource is multiply active with PCMK_META_MULTIPLE_ACTIVE set to
1129          * PCMK_VALUE_STOP_UNEXPECTED, and not stopping on its current node, but
1130          * it should be stopping elsewhere.
1131          */
1132         possible_matches = pe__resource_actions(rsc, NULL, PCMK_ACTION_STOP,
1133                                                 false);
1134         if (possible_matches != NULL) {
1135             stop = possible_matches->data;
1136             g_list_free(possible_matches);
1137         }
1138     }
1139 
1140     possible_matches = pe__resource_actions(rsc, next, PCMK_ACTION_PROMOTE,
1141                                             false);
1142     if (possible_matches) {
1143         promote = possible_matches->data;
1144         g_list_free(possible_matches);
1145     }
1146 
1147     possible_matches = pe__resource_actions(rsc, next, PCMK_ACTION_DEMOTE,
1148                                             false);
1149     if (possible_matches) {
1150         demote = possible_matches->data;
1151         g_list_free(possible_matches);
1152     }
1153 
1154     if (rsc->role == rsc->next_role) {
1155         pcmk_action_t *migrate_op = NULL;
1156 
1157         CRM_CHECK(next != NULL, return rc);
1158 
1159         possible_matches = pe__resource_actions(rsc, next,
1160                                                 PCMK_ACTION_MIGRATE_FROM,
1161                                                 false);
1162         if (possible_matches) {
1163             migrate_op = possible_matches->data;
1164         }
1165 
1166         if ((migrate_op != NULL) && (current != NULL)
1167             && pcmk_is_set(migrate_op->flags, pcmk_action_runnable)) {
1168             rc = out->message(out, "rsc-action-item", "Migrate", rsc, current,
1169                               next, start, NULL);
1170 
1171         } else if (pcmk_is_set(rsc->flags, pcmk_rsc_reload)) {
1172             rc = out->message(out, "rsc-action-item", "Reload", rsc, current,
1173                               next, start, NULL);
1174 
1175         } else if ((start == NULL)
1176                    || pcmk_is_set(start->flags, pcmk_action_optional)) {
1177             if ((demote != NULL) && (promote != NULL)
1178                 && !pcmk_is_set(demote->flags, pcmk_action_optional)
1179                 && !pcmk_is_set(promote->flags, pcmk_action_optional)) {
1180                 rc = out->message(out, "rsc-action-item", "Re-promote", rsc,
1181                                   current, next, promote, demote);
1182             } else {
1183                 pcmk__rsc_info(rsc, "Leave   %s\t(%s %s)", rsc->id,
1184                                pcmk_role_text(rsc->role),
1185                                pcmk__node_name(next));
1186             }
1187 
1188         } else if (!pcmk_is_set(start->flags, pcmk_action_runnable)) {
1189             if ((stop == NULL) || (stop->reason == NULL)) {
1190                 reason_op = start;
1191             } else {
1192                 reason_op = stop;
1193             }
1194             rc = out->message(out, "rsc-action-item", "Stop", rsc, current,
1195                               NULL, stop, reason_op);
1196             STOP_SANITY_ASSERT(__LINE__);
1197 
1198         } else if (moving && current) {
1199             const bool failed = pcmk_is_set(rsc->flags, pcmk_rsc_failed);
1200 
1201             rc = out->message(out, "rsc-action-item",
1202                               (failed? "Recover" : "Move"), rsc, current, next,
1203                               stop, NULL);
1204 
1205         } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
1206             rc = out->message(out, "rsc-action-item", "Recover", rsc, current,
1207                               NULL, stop, NULL);
1208             STOP_SANITY_ASSERT(__LINE__);
1209 
1210         } else {
1211             rc = out->message(out, "rsc-action-item", "Restart", rsc, current,
1212                               next, start, NULL);
1213 #if 0
1214             /* @TODO This can be reached in situations that should really be
1215              * "Start" (see for example the migrate-fail-7 regression test)
1216              */
1217             STOP_SANITY_ASSERT(__LINE__);
1218 #endif
1219         }
1220 
1221         g_list_free(possible_matches);
1222         return rc;
1223     }
1224 
1225     if ((stop != NULL)
1226         && ((rsc->next_role == pcmk_role_stopped)
1227             || ((start != NULL)
1228                 && !pcmk_is_set(start->flags, pcmk_action_runnable)))) {
1229 
1230         key = stop_key(rsc);
1231         for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) {
1232             pcmk_node_t *node = iter->data;
1233             pcmk_action_t *stop_op = NULL;
1234 
1235             reason_op = start;
1236             possible_matches = find_actions(rsc->actions, key, node);
1237             if (possible_matches) {
1238                 stop_op = possible_matches->data;
1239                 g_list_free(possible_matches);
1240             }
1241 
1242             if (stop_op != NULL) {
1243                 if (pcmk_is_set(stop_op->flags, pcmk_action_runnable)) {
1244                     STOP_SANITY_ASSERT(__LINE__);
1245                 }
1246                 if (stop_op->reason != NULL) {
1247                     reason_op = stop_op;
1248                 }
1249             }
1250 
1251             if (out->message(out, "rsc-action-item", "Stop", rsc, node, NULL,
1252                              stop_op, reason_op) == pcmk_rc_ok) {
1253                 rc = pcmk_rc_ok;
1254             }
1255         }
1256 
1257         free(key);
1258 
1259     } else if ((stop != NULL)
1260                && pcmk_all_flags_set(rsc->flags,
1261                                      pcmk_rsc_failed|pcmk_rsc_stop_if_failed)) {
1262         /* 'stop' may be NULL if the failure was ignored */
1263         rc = out->message(out, "rsc-action-item", "Recover", rsc, current,
1264                           next, stop, start);
1265         STOP_SANITY_ASSERT(__LINE__);
1266 
1267     } else if (moving) {
1268         rc = out->message(out, "rsc-action-item", "Move", rsc, current, next,
1269                           stop, NULL);
1270         STOP_SANITY_ASSERT(__LINE__);
1271 
1272     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_reload)) {
1273         rc = out->message(out, "rsc-action-item", "Reload", rsc, current, next,
1274                           start, NULL);
1275 
1276     } else if ((stop != NULL)
1277                && !pcmk_is_set(stop->flags, pcmk_action_optional)) {
1278         rc = out->message(out, "rsc-action-item", "Restart", rsc, current,
1279                           next, start, NULL);
1280         STOP_SANITY_ASSERT(__LINE__);
1281 
1282     } else if (rsc->role == pcmk_role_promoted) {
1283         CRM_LOG_ASSERT(current != NULL);
1284         rc = out->message(out, "rsc-action-item", "Demote", rsc, current,
1285                           next, demote, NULL);
1286 
1287     } else if (rsc->next_role == pcmk_role_promoted) {
1288         CRM_LOG_ASSERT(next);
1289         rc = out->message(out, "rsc-action-item", "Promote", rsc, current,
1290                           next, promote, NULL);
1291 
1292     } else if ((rsc->role == pcmk_role_stopped)
1293                && (rsc->next_role > pcmk_role_stopped)) {
1294         rc = out->message(out, "rsc-action-item", "Start", rsc, current, next,
1295                           start, NULL);
1296     }
1297 
1298     return rc;
1299 }
1300 
1301 PCMK__OUTPUT_ARGS("node-action", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1302 static int
1303 node_action(pcmk__output_t *out, va_list args)
1304 {
1305     const char *task = va_arg(args, const char *);
1306     const char *node_name = va_arg(args, const char *);
1307     const char *reason = va_arg(args, const char *);
1308 
1309     if (task == NULL) {
1310         return pcmk_rc_no_output;
1311     } else if (reason) {
1312         out->list_item(out, NULL, "%s %s '%s'", task, node_name, reason);
1313     } else {
1314         crm_notice(" * %s %s", task, node_name);
1315     }
1316 
1317     return pcmk_rc_ok;
1318 }
1319 
1320 PCMK__OUTPUT_ARGS("node-action", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1321 static int
1322 node_action_xml(pcmk__output_t *out, va_list args)
1323 {
1324     const char *task = va_arg(args, const char *);
1325     const char *node_name = va_arg(args, const char *);
1326     const char *reason = va_arg(args, const char *);
1327 
1328     if (task == NULL) {
1329         return pcmk_rc_no_output;
1330     } else if (reason) {
1331         pcmk__output_create_xml_node(out, PCMK_XE_NODE_ACTION,
1332                                      PCMK_XA_TASK, task,
1333                                      PCMK_XA_NODE, node_name,
1334                                      PCMK_XA_REASON, reason,
1335                                      NULL);
1336     } else {
1337         crm_notice(" * %s %s", task, node_name);
1338     }
1339 
1340     return pcmk_rc_ok;
1341 }
1342 
1343 PCMK__OUTPUT_ARGS("node-info", "uint32_t", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1344                   "const char *", "bool", "bool")
1345 static int
1346 node_info_default(pcmk__output_t *out, va_list args)
1347 {
1348     uint32_t node_id = va_arg(args, uint32_t);
1349     const char *node_name = va_arg(args, const char *);
1350     const char *uuid = va_arg(args, const char *);
1351     const char *state = va_arg(args, const char *);
1352     bool have_quorum = (bool) va_arg(args, int);
1353     bool is_remote = (bool) va_arg(args, int);
1354 
1355     return out->info(out,
1356                      "Node %" PRIu32 ": %s "
1357                      "(uuid=%s, state=%s, have_quorum=%s, is_remote=%s)",
1358                      node_id, pcmk__s(node_name, "unknown"),
1359                      pcmk__s(uuid, "unknown"), pcmk__s(state, "unknown"),
1360                      pcmk__btoa(have_quorum), pcmk__btoa(is_remote));
1361 }
1362 
1363 PCMK__OUTPUT_ARGS("node-info", "uint32_t", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1364                   "const char *", "bool", "bool")
1365 static int
1366 node_info_xml(pcmk__output_t *out, va_list args)
1367 {
1368     uint32_t node_id = va_arg(args, uint32_t);
1369     const char *node_name = va_arg(args, const char *);
1370     const char *uuid = va_arg(args, const char *);
1371     const char *state = va_arg(args, const char *);
1372     bool have_quorum = (bool) va_arg(args, int);
1373     bool is_remote = (bool) va_arg(args, int);
1374 
1375     char *id_s = crm_strdup_printf("%" PRIu32, node_id);
1376 
1377     pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO,
1378                                  PCMK_XA_NODEID, id_s,
1379                                  PCMK_XA_UNAME, node_name,
1380                                  PCMK_XA_ID, uuid,
1381                                  PCMK_XA_CRMD, state,
1382                                  PCMK_XA_HAVE_QUORUM, pcmk__btoa(have_quorum),
1383                                  PCMK_XA_REMOTE_NODE, pcmk__btoa(is_remote),
1384                                  NULL);
1385     free(id_s);
1386     return pcmk_rc_ok;
1387 }
1388 
1389 PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1390                   "xmlNode *")
1391 static int
1392 inject_cluster_action(pcmk__output_t *out, va_list args)
1393 {
1394     const char *node = va_arg(args, const char *);
1395     const char *task = va_arg(args, const char *);
1396     xmlNodePtr rsc = va_arg(args, xmlNodePtr);
1397 
1398     if (out->is_quiet(out)) {
1399         return pcmk_rc_no_output;
1400     }
1401 
1402     if (rsc != NULL) {
1403         out->list_item(out, NULL, "Cluster action:  %s for %s on %s",
1404                        task, pcmk__xe_id(rsc), node);
1405     } else {
1406         out->list_item(out, NULL, "Cluster action:  %s on %s", task, node);
1407     }
1408 
1409     return pcmk_rc_ok;
1410 }
1411 
1412 PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1413                   "xmlNode *")
1414 static int
1415 inject_cluster_action_xml(pcmk__output_t *out, va_list args)
1416 {
1417     const char *node = va_arg(args, const char *);
1418     const char *task = va_arg(args, const char *);
1419     xmlNodePtr rsc = va_arg(args, xmlNodePtr);
1420 
1421     xmlNodePtr xml_node = NULL;
1422 
1423     if (out->is_quiet(out)) {
1424         return pcmk_rc_no_output;
1425     }
1426 
1427     xml_node = pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_ACTION,
1428                                             PCMK_XA_TASK, task,
1429                                             PCMK_XA_NODE, node,
1430                                             NULL);
1431 
1432     if (rsc) {
1433         crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(rsc));
1434     }
1435 
1436     return pcmk_rc_ok;
1437 }
1438 
1439 PCMK__OUTPUT_ARGS("inject-fencing-action", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1440 static int
1441 inject_fencing_action(pcmk__output_t *out, va_list args)
1442 {
1443     const char *target = va_arg(args, const char *);
1444     const char *op = va_arg(args, const char *);
1445 
1446     if (out->is_quiet(out)) {
1447         return pcmk_rc_no_output;
1448     }
1449 
1450     out->list_item(out, NULL, "Fencing %s (%s)", target, op);
1451     return pcmk_rc_ok;
1452 }
1453 
1454 PCMK__OUTPUT_ARGS("inject-fencing-action", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1455 static int
1456 inject_fencing_action_xml(pcmk__output_t *out, va_list args)
1457 {
1458     const char *target = va_arg(args, const char *);
1459     const char *op = va_arg(args, const char *);
1460 
1461     if (out->is_quiet(out)) {
1462         return pcmk_rc_no_output;
1463     }
1464 
1465     pcmk__output_create_xml_node(out, PCMK_XE_FENCING_ACTION,
1466                                  PCMK_XA_TARGET, target,
1467                                  PCMK_XA_OP, op,
1468                                  NULL);
1469     return pcmk_rc_ok;
1470 }
1471 
1472 PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
1473 static int
1474 inject_attr(pcmk__output_t *out, va_list args)
1475 {
1476     const char *name = va_arg(args, const char *);
1477     const char *value = va_arg(args, const char *);
1478     xmlNodePtr cib_node = va_arg(args, xmlNodePtr);
1479 
1480     xmlChar *node_path = NULL;
1481 
1482     if (out->is_quiet(out)) {
1483         return pcmk_rc_no_output;
1484     }
1485 
1486     node_path = xmlGetNodePath(cib_node);
1487 
1488     out->list_item(out, NULL, "Injecting attribute %s=%s into %s '%s'",
1489                    name, value, node_path, pcmk__xe_id(cib_node));
1490 
1491     free(node_path);
1492     return pcmk_rc_ok;
1493 }
1494 
1495 PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
1496 static int
1497 inject_attr_xml(pcmk__output_t *out, va_list args)
1498 {
1499     const char *name = va_arg(args, const char *);
1500     const char *value = va_arg(args, const char *);
1501     xmlNodePtr cib_node = va_arg(args, xmlNodePtr);
1502 
1503     xmlChar *node_path = NULL;
1504 
1505     if (out->is_quiet(out)) {
1506         return pcmk_rc_no_output;
1507     }
1508 
1509     node_path = xmlGetNodePath(cib_node);
1510 
1511     pcmk__output_create_xml_node(out, PCMK_XE_INJECT_ATTR,
1512                                  PCMK_XA_NAME, name,
1513                                  PCMK_XA_VALUE, value,
1514                                  PCMK_XA_NODE_PATH, node_path,
1515                                  PCMK_XA_CIB_NODE, pcmk__xe_id(cib_node),
1516                                  NULL);
1517     free(node_path);
1518     return pcmk_rc_ok;
1519 }
1520 
1521 PCMK__OUTPUT_ARGS("inject-spec", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1522 static int
1523 inject_spec(pcmk__output_t *out, va_list args)
1524 {
1525     const char *spec = va_arg(args, const char *);
1526 
1527     if (out->is_quiet(out)) {
1528         return pcmk_rc_no_output;
1529     }
1530 
1531     out->list_item(out, NULL, "Injecting %s into the configuration", spec);
1532     return pcmk_rc_ok;
1533 }
1534 
1535 PCMK__OUTPUT_ARGS("inject-spec", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1536 static int
1537 inject_spec_xml(pcmk__output_t *out, va_list args)
1538 {
1539     const char *spec = va_arg(args, const char *);
1540 
1541     if (out->is_quiet(out)) {
1542         return pcmk_rc_no_output;
1543     }
1544 
1545     pcmk__output_create_xml_node(out, PCMK_XE_INJECT_SPEC,
1546                                  PCMK_XA_SPEC, spec,
1547                                  NULL);
1548     return pcmk_rc_ok;
1549 }
1550 
1551 PCMK__OUTPUT_ARGS("inject-modify-config", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1552 static int
1553 inject_modify_config(pcmk__output_t *out, va_list args)
1554 {
1555     const char *quorum = va_arg(args, const char *);
1556     const char *watchdog = va_arg(args, const char *);
1557 
1558     if (out->is_quiet(out)) {
1559         return pcmk_rc_no_output;
1560     }
1561 
1562     out->begin_list(out, NULL, NULL, "Performing Requested Modifications");
1563 
1564     if (quorum) {
1565         out->list_item(out, NULL, "Setting quorum: %s", quorum);
1566     }
1567 
1568     if (watchdog) {
1569         out->list_item(out, NULL, "Setting watchdog: %s", watchdog);
1570     }
1571 
1572     return pcmk_rc_ok;
1573 }
1574 
1575 PCMK__OUTPUT_ARGS("inject-modify-config", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1576 static int
1577 inject_modify_config_xml(pcmk__output_t *out, va_list args)
1578 {
1579     const char *quorum = va_arg(args, const char *);
1580     const char *watchdog = va_arg(args, const char *);
1581 
1582     xmlNodePtr node = NULL;
1583 
1584     if (out->is_quiet(out)) {
1585         return pcmk_rc_no_output;
1586     }
1587 
1588     node = pcmk__output_xml_create_parent(out, PCMK_XE_MODIFICATIONS, NULL);
1589 
1590     if (quorum) {
1591         crm_xml_add(node, PCMK_XA_QUORUM, quorum);
1592     }
1593 
1594     if (watchdog) {
1595         crm_xml_add(node, PCMK_XA_WATCHDOG, watchdog);
1596     }
1597 
1598     pcmk__output_xml_pop_parent(out);
1599     return pcmk_rc_ok;
1600 }
1601 
1602 PCMK__OUTPUT_ARGS("inject-modify-node", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1603 static int
1604 inject_modify_node(pcmk__output_t *out, va_list args)
1605 {
1606     const char *action = va_arg(args, const char *);
1607     const char *node = va_arg(args, const char *);
1608 
1609     if (out->is_quiet(out)) {
1610         return pcmk_rc_no_output;
1611     }
1612 
1613     if (pcmk__str_eq(action, "Online", pcmk__str_none)) {
1614         out->list_item(out, NULL, "Bringing node %s online", node);
1615         return pcmk_rc_ok;
1616     } else if (pcmk__str_eq(action, "Offline", pcmk__str_none)) {
1617         out->list_item(out, NULL, "Taking node %s offline", node);
1618         return pcmk_rc_ok;
1619     } else if (pcmk__str_eq(action, "Failing", pcmk__str_none)) {
1620         out->list_item(out, NULL, "Failing node %s", node);
1621         return pcmk_rc_ok;
1622     }
1623 
1624     return pcmk_rc_no_output;
1625 }
1626 
1627 PCMK__OUTPUT_ARGS("inject-modify-node", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1628 static int
1629 inject_modify_node_xml(pcmk__output_t *out, va_list args)
1630 {
1631     const char *action = va_arg(args, const char *);
1632     const char *node = va_arg(args, const char *);
1633 
1634     if (out->is_quiet(out)) {
1635         return pcmk_rc_no_output;
1636     }
1637 
1638     pcmk__output_create_xml_node(out, PCMK_XE_MODIFY_NODE,
1639                                  PCMK_XA_ACTION, action,
1640                                  PCMK_XA_NODE, node,
1641                                  NULL);
1642     return pcmk_rc_ok;
1643 }
1644 
1645 PCMK__OUTPUT_ARGS("inject-modify-ticket", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1646 static int
1647 inject_modify_ticket(pcmk__output_t *out, va_list args)
1648 {
1649     const char *action = va_arg(args, const char *);
1650     const char *ticket = va_arg(args, const char *);
1651 
1652     if (out->is_quiet(out)) {
1653         return pcmk_rc_no_output;
1654     }
1655 
1656     if (pcmk__str_eq(action, "Standby", pcmk__str_none)) {
1657         out->list_item(out, NULL, "Making ticket %s standby", ticket);
1658     } else {
1659         out->list_item(out, NULL, "%s ticket %s", action, ticket);
1660     }
1661 
1662     return pcmk_rc_ok;
1663 }
1664 
1665 PCMK__OUTPUT_ARGS("inject-modify-ticket", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1666 static int
1667 inject_modify_ticket_xml(pcmk__output_t *out, va_list args)
1668 {
1669     const char *action = va_arg(args, const char *);
1670     const char *ticket = va_arg(args, const char *);
1671 
1672     if (out->is_quiet(out)) {
1673         return pcmk_rc_no_output;
1674     }
1675 
1676     pcmk__output_create_xml_node(out, PCMK_XE_MODIFY_TICKET,
1677                                  PCMK_XA_ACTION, action,
1678                                  PCMK_XA_TICKET, ticket,
1679                                  NULL);
1680     return pcmk_rc_ok;
1681 }
1682 
1683 PCMK__OUTPUT_ARGS("inject-pseudo-action", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1684 static int
1685 inject_pseudo_action(pcmk__output_t *out, va_list args)
1686 {
1687     const char *node = va_arg(args, const char *);
1688     const char *task = va_arg(args, const char *);
1689 
1690     if (out->is_quiet(out)) {
1691         return pcmk_rc_no_output;
1692     }
1693 
1694     out->list_item(out, NULL, "Pseudo action:   %s%s%s",
1695                    task, ((node == NULL)? "" : " on "), pcmk__s(node, ""));
1696     return pcmk_rc_ok;
1697 }
1698 
1699 PCMK__OUTPUT_ARGS("inject-pseudo-action", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1700 static int
1701 inject_pseudo_action_xml(pcmk__output_t *out, va_list args)
1702 {
1703     const char *node = va_arg(args, const char *);
1704     const char *task = va_arg(args, const char *);
1705 
1706     xmlNodePtr xml_node = NULL;
1707 
1708     if (out->is_quiet(out)) {
1709         return pcmk_rc_no_output;
1710     }
1711 
1712     xml_node = pcmk__output_create_xml_node(out, PCMK_XE_PSEUDO_ACTION,
1713                                             PCMK_XA_TASK, task,
1714                                             NULL);
1715     if (node) {
1716         crm_xml_add(xml_node, PCMK_XA_NODE, node);
1717     }
1718 
1719     return pcmk_rc_ok;
1720 }
1721 
1722 PCMK__OUTPUT_ARGS("inject-rsc-action", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1723                   "const char *", "guint")
1724 static int
1725 inject_rsc_action(pcmk__output_t *out, va_list args)
1726 {
1727     const char *rsc = va_arg(args, const char *);
1728     const char *operation = va_arg(args, const char *);
1729     const char *node = va_arg(args, const char *);
1730     guint interval_ms = va_arg(args, guint);
1731 
1732     if (out->is_quiet(out)) {
1733         return pcmk_rc_no_output;
1734     }
1735 
1736     if (interval_ms) {
1737         out->list_item(out, NULL, "Resource action: %-15s %s=%u on %s",
1738                        rsc, operation, interval_ms, node);
1739     } else {
1740         out->list_item(out, NULL, "Resource action: %-15s %s on %s",
1741                        rsc, operation, node);
1742     }
1743 
1744     return pcmk_rc_ok;
1745 }
1746 
1747 PCMK__OUTPUT_ARGS("inject-rsc-action", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1748                   "const char *", "guint")
1749 static int
1750 inject_rsc_action_xml(pcmk__output_t *out, va_list args)
1751 {
1752     const char *rsc = va_arg(args, const char *);
1753     const char *operation = va_arg(args, const char *);
1754     const char *node = va_arg(args, const char *);
1755     guint interval_ms = va_arg(args, guint);
1756 
1757     xmlNodePtr xml_node = NULL;
1758 
1759     if (out->is_quiet(out)) {
1760         return pcmk_rc_no_output;
1761     }
1762 
1763     xml_node = pcmk__output_create_xml_node(out, PCMK_XE_RSC_ACTION,
1764                                             PCMK_XA_RESOURCE, rsc,
1765                                             PCMK_XA_OP, operation,
1766                                             PCMK_XA_NODE, node,
1767                                             NULL);
1768 
1769     if (interval_ms) {
1770         char *interval_s = pcmk__itoa(interval_ms);
1771 
1772         crm_xml_add(xml_node, PCMK_XA_INTERVAL, interval_s);
1773         free(interval_s);
1774     }
1775 
1776     return pcmk_rc_ok;
1777 }
1778 
1779 #define CHECK_RC(retcode, retval)   \
1780     if (retval == pcmk_rc_ok) {     \
1781         retcode = pcmk_rc_ok;       \
1782     }
1783 
1784 PCMK__OUTPUT_ARGS("cluster-status", "pcmk_scheduler_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
1785                   "enum pcmk_pacemakerd_state", "crm_exit_t",
1786                   "stonith_history_t *", "enum pcmk__fence_history", "uint32_t",
1787                   "uint32_t", "const char *", "GList *", "GList *")
1788 int
1789 pcmk__cluster_status_text(pcmk__output_t *out, va_list args)
1790 {
1791     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1792     enum pcmk_pacemakerd_state pcmkd_state =
1793         (enum pcmk_pacemakerd_state) va_arg(args, int);
1794     crm_exit_t history_rc = va_arg(args, crm_exit_t);
1795     stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
1796     enum pcmk__fence_history fence_history = va_arg(args, int);
1797     uint32_t section_opts = va_arg(args, uint32_t);
1798     uint32_t show_opts = va_arg(args, uint32_t);
1799     const char *prefix = va_arg(args, const char *);
1800     GList *unames = va_arg(args, GList *);
1801     GList *resources = va_arg(args, GList *);
1802 
1803     int rc = pcmk_rc_no_output;
1804     bool already_printed_failure = false;
1805 
1806     CHECK_RC(rc, out->message(out, "cluster-summary", scheduler, pcmkd_state,
1807                               section_opts, show_opts));
1808 
1809     if (pcmk_is_set(section_opts, pcmk_section_nodes) && unames) {
1810         CHECK_RC(rc, out->message(out, "node-list", scheduler->nodes, unames,
1811                                   resources, show_opts, rc == pcmk_rc_ok));
1812     }
1813 
1814     /* Print resources section, if needed */
1815     if (pcmk_is_set(section_opts, pcmk_section_resources)) {
1816         CHECK_RC(rc, out->message(out, "resource-list", scheduler, show_opts,
1817                                   true, unames, resources, rc == pcmk_rc_ok));
1818     }
1819 
1820     /* print Node Attributes section if requested */
1821     if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
1822         CHECK_RC(rc, out->message(out, "node-attribute-list", scheduler,
1823                                   show_opts, (rc == pcmk_rc_ok), unames,
1824                                   resources));
1825     }
1826 
1827     /* If requested, print resource operations (which includes failcounts)
1828      * or just failcounts
1829      */
1830     if (pcmk_any_flags_set(section_opts,
1831                            pcmk_section_operations|pcmk_section_failcounts)) {
1832         CHECK_RC(rc, out->message(out, "node-summary", scheduler, unames,
1833                                   resources, section_opts, show_opts,
1834                                   (rc == pcmk_rc_ok)));
1835     }
1836 
1837     /* If there were any failed actions, print them */
1838     if (pcmk_is_set(section_opts, pcmk_section_failures)
1839         && (scheduler->failed != NULL)
1840         && (scheduler->failed->children != NULL)) {
1841 
1842         CHECK_RC(rc, out->message(out, "failed-action-list", scheduler, unames,
1843                                   resources, show_opts, rc == pcmk_rc_ok));
1844     }
1845 
1846     /* Print failed stonith actions */
1847     if (pcmk_is_set(section_opts, pcmk_section_fence_failed) &&
1848         fence_history != pcmk__fence_history_none) {
1849         if (history_rc == 0) {
1850             stonith_history_t *hp = NULL;
1851 
1852             hp = stonith__first_matching_event(stonith_history,
1853                                                stonith__event_state_eq,
1854                                                GINT_TO_POINTER(st_failed));
1855             if (hp) {
1856                 CHECK_RC(rc, out->message(out, "failed-fencing-list",
1857                                           stonith_history, unames, section_opts,
1858                                           show_opts, rc == pcmk_rc_ok));
1859             }
1860         } else {
1861             PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
1862             out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
1863             out->list_item(out, NULL, "Failed to get fencing history: %s",
1864                            crm_exit_str(history_rc));
1865             out->end_list(out);
1866 
1867             already_printed_failure = true;
1868         }
1869     }
1870 
1871     /* Print tickets if requested */
1872     if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
1873         CHECK_RC(rc, out->message(out, "ticket-list", scheduler->tickets,
1874                                   (rc == pcmk_rc_ok), false, false));
1875     }
1876 
1877     /* Print negative location constraints if requested */
1878     if (pcmk_is_set(section_opts, pcmk_section_bans)) {
1879         CHECK_RC(rc, out->message(out, "ban-list", scheduler, prefix, resources,
1880                                   show_opts, rc == pcmk_rc_ok));
1881     }
1882 
1883     /* Print stonith history */
1884     if (pcmk_any_flags_set(section_opts, pcmk_section_fencing_all) &&
1885         fence_history != pcmk__fence_history_none) {
1886         if (history_rc != 0) {
1887             if (!already_printed_failure) {
1888                 PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
1889                 out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
1890                 out->list_item(out, NULL, "Failed to get fencing history: %s",
1891                                crm_exit_str(history_rc));
1892                 out->end_list(out);
1893             }
1894         } else if (pcmk_is_set(section_opts, pcmk_section_fence_worked)) {
1895             stonith_history_t *hp = NULL;
1896 
1897             hp = stonith__first_matching_event(stonith_history,
1898                                                stonith__event_state_neq,
1899                                                GINT_TO_POINTER(st_failed));
1900             if (hp) {
1901                 CHECK_RC(rc, out->message(out, "fencing-list", hp, unames,
1902                                           section_opts, show_opts,
1903                                           rc == pcmk_rc_ok));
1904             }
1905         } else if (pcmk_is_set(section_opts, pcmk_section_fence_pending)) {
1906             stonith_history_t *hp = NULL;
1907 
1908             hp = stonith__first_matching_event(stonith_history,
1909                                                stonith__event_state_pending,
1910                                                NULL);
1911             if (hp) {
1912                 CHECK_RC(rc, out->message(out, "pending-fencing-list", hp,
1913                                           unames, section_opts, show_opts,
1914                                           rc == pcmk_rc_ok));
1915             }
1916         }
1917     }
1918 
1919     return rc;
1920 }
1921 
1922 PCMK__OUTPUT_ARGS("cluster-status", "pcmk_scheduler_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
1923                   "enum pcmk_pacemakerd_state", "crm_exit_t",
1924                   "stonith_history_t *", "enum pcmk__fence_history", "uint32_t",
1925                   "uint32_t", "const char *", "GList *", "GList *")
1926 static int
1927 cluster_status_xml(pcmk__output_t *out, va_list args)
1928 {
1929     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1930     enum pcmk_pacemakerd_state pcmkd_state =
1931         (enum pcmk_pacemakerd_state) va_arg(args, int);
1932     crm_exit_t history_rc = va_arg(args, crm_exit_t);
1933     stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
1934     enum pcmk__fence_history fence_history = va_arg(args, int);
1935     uint32_t section_opts = va_arg(args, uint32_t);
1936     uint32_t show_opts = va_arg(args, uint32_t);
1937     const char *prefix = va_arg(args, const char *);
1938     GList *unames = va_arg(args, GList *);
1939     GList *resources = va_arg(args, GList *);
1940 
1941     out->message(out, "cluster-summary", scheduler, pcmkd_state, section_opts,
1942                  show_opts);
1943 
1944     /*** NODES ***/
1945     if (pcmk_is_set(section_opts, pcmk_section_nodes)) {
1946         out->message(out, "node-list", scheduler->nodes, unames, resources,
1947                      show_opts, false);
1948     }
1949 
1950     /* Print resources section, if needed */
1951     if (pcmk_is_set(section_opts, pcmk_section_resources)) {
1952         /* XML output always displays full details. */
1953         uint32_t full_show_opts = show_opts & ~pcmk_show_brief;
1954 
1955         out->message(out, "resource-list", scheduler, full_show_opts,
1956                      false, unames, resources, false);
1957     }
1958 
1959     /* print Node Attributes section if requested */
1960     if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
1961         out->message(out, "node-attribute-list", scheduler, show_opts, false,
1962                      unames, resources);
1963     }
1964 
1965     /* If requested, print resource operations (which includes failcounts)
1966      * or just failcounts
1967      */
1968     if (pcmk_any_flags_set(section_opts,
1969                            pcmk_section_operations|pcmk_section_failcounts)) {
1970         out->message(out, "node-summary", scheduler, unames,
1971                      resources, section_opts, show_opts, false);
1972     }
1973 
1974     /* If there were any failed actions, print them */
1975     if (pcmk_is_set(section_opts, pcmk_section_failures)
1976         && (scheduler->failed != NULL)
1977         && (scheduler->failed->children != NULL)) {
1978 
1979         out->message(out, "failed-action-list", scheduler, unames, resources,
1980                      show_opts, false);
1981     }
1982 
1983     /* Print stonith history */
1984     if (pcmk_is_set(section_opts, pcmk_section_fencing_all) &&
1985         fence_history != pcmk__fence_history_none) {
1986         out->message(out, "full-fencing-list", history_rc, stonith_history,
1987                      unames, section_opts, show_opts, false);
1988     }
1989 
1990     /* Print tickets if requested */
1991     if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
1992         out->message(out, "ticket-list", scheduler->tickets, false, false, false);
1993     }
1994 
1995     /* Print negative location constraints if requested */
1996     if (pcmk_is_set(section_opts, pcmk_section_bans)) {
1997         out->message(out, "ban-list", scheduler, prefix, resources, show_opts,
1998                      false);
1999     }
2000 
2001     return pcmk_rc_ok;
2002 }
2003 
2004 PCMK__OUTPUT_ARGS("cluster-status", "pcmk_scheduler_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
2005                   "enum pcmk_pacemakerd_state", "crm_exit_t",
2006                   "stonith_history_t *", "enum pcmk__fence_history", "uint32_t",
2007                   "uint32_t", "const char *", "GList *", "GList *")
2008 static int
2009 cluster_status_html(pcmk__output_t *out, va_list args)
2010 {
2011     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2012     enum pcmk_pacemakerd_state pcmkd_state =
2013         (enum pcmk_pacemakerd_state) va_arg(args, int);
2014     crm_exit_t history_rc = va_arg(args, crm_exit_t);
2015     stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
2016     enum pcmk__fence_history fence_history = va_arg(args, int);
2017     uint32_t section_opts = va_arg(args, uint32_t);
2018     uint32_t show_opts = va_arg(args, uint32_t);
2019     const char *prefix = va_arg(args, const char *);
2020     GList *unames = va_arg(args, GList *);
2021     GList *resources = va_arg(args, GList *);
2022     bool already_printed_failure = false;
2023 
2024     out->message(out, "cluster-summary", scheduler, pcmkd_state, section_opts,
2025                  show_opts);
2026 
2027     /*** NODE LIST ***/
2028     if (pcmk_is_set(section_opts, pcmk_section_nodes) && unames) {
2029         out->message(out, "node-list", scheduler->nodes, unames, resources,
2030                      show_opts, false);
2031     }
2032 
2033     /* Print resources section, if needed */
2034     if (pcmk_is_set(section_opts, pcmk_section_resources)) {
2035         out->message(out, "resource-list", scheduler, show_opts, true, unames,
2036                      resources, false);
2037     }
2038 
2039     /* print Node Attributes section if requested */
2040     if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
2041         out->message(out, "node-attribute-list", scheduler, show_opts, false,
2042                      unames, resources);
2043     }
2044 
2045     /* If requested, print resource operations (which includes failcounts)
2046      * or just failcounts
2047      */
2048     if (pcmk_any_flags_set(section_opts,
2049                            pcmk_section_operations|pcmk_section_failcounts)) {
2050         out->message(out, "node-summary", scheduler, unames,
2051                      resources, section_opts, show_opts, false);
2052     }
2053 
2054     /* If there were any failed actions, print them */
2055     if (pcmk_is_set(section_opts, pcmk_section_failures)
2056         && (scheduler->failed != NULL)
2057         && (scheduler->failed->children != NULL)) {
2058 
2059         out->message(out, "failed-action-list", scheduler, unames, resources,
2060                      show_opts, false);
2061     }
2062 
2063     /* Print failed stonith actions */
2064     if (pcmk_is_set(section_opts, pcmk_section_fence_failed) &&
2065         fence_history != pcmk__fence_history_none) {
2066         if (history_rc == 0) {
2067             stonith_history_t *hp = NULL;
2068 
2069             hp = stonith__first_matching_event(stonith_history,
2070                                                stonith__event_state_eq,
2071                                                GINT_TO_POINTER(st_failed));
2072             if (hp) {
2073                 out->message(out, "failed-fencing-list", stonith_history,
2074                              unames, section_opts, show_opts, false);
2075             }
2076         } else {
2077             out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
2078             out->list_item(out, NULL, "Failed to get fencing history: %s",
2079                            crm_exit_str(history_rc));
2080             out->end_list(out);
2081         }
2082     }
2083 
2084     /* Print stonith history */
2085     if (pcmk_any_flags_set(section_opts, pcmk_section_fencing_all) &&
2086         fence_history != pcmk__fence_history_none) {
2087         if (history_rc != 0) {
2088             if (!already_printed_failure) {
2089                 out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
2090                 out->list_item(out, NULL, "Failed to get fencing history: %s",
2091                                crm_exit_str(history_rc));
2092                 out->end_list(out);
2093             }
2094         } else if (pcmk_is_set(section_opts, pcmk_section_fence_worked)) {
2095             stonith_history_t *hp = NULL;
2096 
2097             hp = stonith__first_matching_event(stonith_history,
2098                                                stonith__event_state_neq,
2099                                                GINT_TO_POINTER(st_failed));
2100             if (hp) {
2101                 out->message(out, "fencing-list", hp, unames, section_opts,
2102                              show_opts, false);
2103             }
2104         } else if (pcmk_is_set(section_opts, pcmk_section_fence_pending)) {
2105             stonith_history_t *hp = NULL;
2106 
2107             hp = stonith__first_matching_event(stonith_history,
2108                                                stonith__event_state_pending,
2109                                                NULL);
2110             if (hp) {
2111                 out->message(out, "pending-fencing-list", hp, unames,
2112                              section_opts, show_opts, false);
2113             }
2114         }
2115     }
2116 
2117     /* Print tickets if requested */
2118     if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
2119         out->message(out, "ticket-list", scheduler->tickets, false, false, false);
2120     }
2121 
2122     /* Print negative location constraints if requested */
2123     if (pcmk_is_set(section_opts, pcmk_section_bans)) {
2124         out->message(out, "ban-list", scheduler, prefix, resources, show_opts,
2125                      false);
2126     }
2127 
2128     return pcmk_rc_ok;
2129 }
2130 
2131 #define KV_PAIR(k, v) do { \
2132     if (legacy) { \
2133         pcmk__g_strcat(s, k "=", pcmk__s(v, ""), " ", NULL); \
2134     } else { \
2135         pcmk__g_strcat(s, k "=\"", pcmk__s(v, ""), "\" ", NULL); \
2136     } \
2137 } while (0)
2138 
2139 PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
2140                   "const char *", "const char *", "bool", "bool")
2141 static int
2142 attribute_default(pcmk__output_t *out, va_list args)
2143 {
2144     const char *scope = va_arg(args, const char *);
2145     const char *instance = va_arg(args, const char *);
2146     const char *name = va_arg(args, const char *);
2147     const char *value = va_arg(args, const char *);
2148     const char *host = va_arg(args, const char *);
2149     bool quiet = va_arg(args, int);
2150     bool legacy = va_arg(args, int);
2151 
2152     gchar *value_esc = NULL;
2153     GString *s = NULL;
2154 
2155     if (quiet) {
2156         if (value != NULL) {
2157             /* Quiet needs to be turned off for ->info() to do anything */
2158             bool was_quiet = out->is_quiet(out);
2159 
2160             if (was_quiet) {
2161                 out->quiet = false;
2162             }
2163 
2164             out->info(out, "%s", value);
2165 
2166             out->quiet = was_quiet;
2167         }
2168 
2169         return pcmk_rc_ok;
2170     }
2171 
2172     s = g_string_sized_new(50);
2173 
2174     if (pcmk__xml_needs_escape(value, pcmk__xml_escape_attr_pretty)) {
2175         value_esc = pcmk__xml_escape(value, pcmk__xml_escape_attr_pretty);
2176         value = value_esc;
2177     }
2178 
2179     if (!pcmk__str_empty(scope)) {
2180         KV_PAIR(PCMK_XA_SCOPE, scope);
2181     }
2182 
2183     if (!pcmk__str_empty(instance)) {
2184         KV_PAIR(PCMK_XA_ID, instance);
2185     }
2186 
2187     KV_PAIR(PCMK_XA_NAME, name);
2188 
2189     if (!pcmk__str_empty(host)) {
2190         KV_PAIR(PCMK_XA_HOST, host);
2191     }
2192 
2193     if (legacy) {
2194         pcmk__g_strcat(s, PCMK_XA_VALUE "=", pcmk__s(value, "(null)"), NULL);
2195     } else {
2196         pcmk__g_strcat(s, PCMK_XA_VALUE "=\"", pcmk__s(value, ""), "\"", NULL);
2197     }
2198 
2199     out->info(out, "%s", s->str);
2200 
2201     g_free(value_esc);
2202     g_string_free(s, TRUE);
2203     return pcmk_rc_ok;
2204 }
2205 
2206 PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
2207                   "const char *", "const char *", "bool", "bool")
2208 static int
2209 attribute_xml(pcmk__output_t *out, va_list args)
2210 {
2211     const char *scope = va_arg(args, const char *);
2212     const char *instance = va_arg(args, const char *);
2213     const char *name = va_arg(args, const char *);
2214     const char *value = va_arg(args, const char *);
2215     const char *host = va_arg(args, const char *);
2216     bool quiet G_GNUC_UNUSED = va_arg(args, int);
2217     bool legacy G_GNUC_UNUSED = va_arg(args, int);
2218 
2219     xmlNodePtr node = NULL;
2220 
2221     node = pcmk__output_create_xml_node(out, PCMK_XE_ATTRIBUTE,
2222                                         PCMK_XA_NAME, name,
2223                                         PCMK_XA_VALUE, pcmk__s(value, ""),
2224                                         NULL);
2225 
2226     if (!pcmk__str_empty(scope)) {
2227         crm_xml_add(node, PCMK_XA_SCOPE, scope);
2228     }
2229 
2230     if (!pcmk__str_empty(instance)) {
2231         crm_xml_add(node, PCMK_XA_ID, instance);
2232     }
2233 
2234     if (!pcmk__str_empty(host)) {
2235         crm_xml_add(node, PCMK_XA_HOST, host);
2236     }
2237 
2238     return pcmk_rc_ok;
2239 }
2240 
2241 PCMK__OUTPUT_ARGS("rule-check", "const char *", "int", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2242 static int
2243 rule_check_default(pcmk__output_t *out, va_list args)
2244 {
2245     const char *rule_id = va_arg(args, const char *);
2246     int result = va_arg(args, int);
2247     const char *error = va_arg(args, const char *);
2248 
2249     switch (result) {
2250         case pcmk_rc_within_range:
2251             return out->info(out, "Rule %s is still in effect", rule_id);
2252         case pcmk_rc_ok:
2253             return out->info(out, "Rule %s satisfies conditions", rule_id);
2254         case pcmk_rc_after_range:
2255             return out->info(out, "Rule %s is expired", rule_id);
2256         case pcmk_rc_before_range:
2257             return out->info(out, "Rule %s has not yet taken effect", rule_id);
2258         case pcmk_rc_op_unsatisfied:
2259             return out->info(out, "Rule %s does not satisfy conditions",
2260                              rule_id);
2261         default:
2262             out->err(out,
2263                      "Could not determine whether rule %s is in effect: %s",
2264                      rule_id, ((error != NULL)? error : "unexpected error"));
2265             return pcmk_rc_ok;
2266     }
2267 }
2268 
2269 PCMK__OUTPUT_ARGS("rule-check", "const char *", "int", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2270 static int
2271 rule_check_xml(pcmk__output_t *out, va_list args)
2272 {
2273     const char *rule_id = va_arg(args, const char *);
2274     int result = va_arg(args, int);
2275     const char *error = va_arg(args, const char *);
2276 
2277     char *rc_str = pcmk__itoa(pcmk_rc2exitc(result));
2278 
2279     pcmk__output_create_xml_node(out, PCMK_XE_RULE_CHECK,
2280                                  PCMK_XA_RULE_ID, rule_id,
2281                                  PCMK_XA_RC, rc_str,
2282                                  NULL);
2283     free(rc_str);
2284 
2285     switch (result) {
2286         case pcmk_rc_within_range:
2287         case pcmk_rc_ok:
2288         case pcmk_rc_after_range:
2289         case pcmk_rc_before_range:
2290         case pcmk_rc_op_unsatisfied:
2291             return pcmk_rc_ok;
2292         default:
2293             out->err(out,
2294                     "Could not determine whether rule %s is in effect: %s",
2295                     rule_id, ((error != NULL)? error : "unexpected error"));
2296             return pcmk_rc_ok;
2297     }
2298 }
2299 
2300 PCMK__OUTPUT_ARGS("result-code", "int", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2301 static int
2302 result_code_none(pcmk__output_t *out, va_list args)
2303 {
2304     return pcmk_rc_no_output;
2305 }
2306 
2307 PCMK__OUTPUT_ARGS("result-code", "int", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2308 static int
2309 result_code_text(pcmk__output_t *out, va_list args)
2310 {
2311     int code = va_arg(args, int);
2312     const char *name = va_arg(args, const char *);
2313     const char *desc = va_arg(args, const char *);
2314 
2315     static int code_width = 0;
2316 
2317     if (out->is_quiet(out)) {
2318         /* If out->is_quiet(), don't print the code. Print name and/or desc in a
2319          * compact format for text output, or print nothing at all for none-type
2320          * output.
2321          */
2322         if ((name != NULL) && (desc != NULL)) {
2323             pcmk__formatted_printf(out, "%s - %s\n", name, desc);
2324 
2325         } else if ((name != NULL) || (desc != NULL)) {
2326             pcmk__formatted_printf(out, "%s\n", ((name != NULL)? name : desc));
2327         }
2328         return pcmk_rc_ok;
2329     }
2330 
2331     /* Get length of longest (most negative) standard Pacemaker return code
2332      * This should be longer than all the values of any other type of return
2333      * code.
2334      */
2335     if (code_width == 0) {
2336         long long most_negative = pcmk_rc_error - (long long) pcmk__n_rc + 1;
2337         code_width = (int) snprintf(NULL, 0, "%lld", most_negative);
2338     }
2339 
2340     if ((name != NULL) && (desc != NULL)) {
2341         static int name_width = 0;
2342 
2343         if (name_width == 0) {
2344             // Get length of longest standard Pacemaker return code name
2345             for (int lpc = 0; lpc < pcmk__n_rc; lpc++) {
2346                 int len = (int) strlen(pcmk_rc_name(pcmk_rc_error - lpc));
2347                 name_width = QB_MAX(name_width, len);
2348             }
2349         }
2350         return out->info(out, "% *d: %-*s  %s", code_width, code, name_width,
2351                          name, desc);
2352     }
2353 
2354     if ((name != NULL) || (desc != NULL)) {
2355         return out->info(out, "% *d: %s", code_width, code,
2356                          ((name != NULL)? name : desc));
2357     }
2358 
2359     return out->info(out, "% *d", code_width, code);
2360 }
2361 
2362 PCMK__OUTPUT_ARGS("result-code", "int", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2363 static int
2364 result_code_xml(pcmk__output_t *out, va_list args)
2365 {
2366     int code = va_arg(args, int);
2367     const char *name = va_arg(args, const char *);
2368     const char *desc = va_arg(args, const char *);
2369 
2370     char *code_str = pcmk__itoa(code);
2371 
2372     pcmk__output_create_xml_node(out, PCMK_XE_RESULT_CODE,
2373                                  PCMK_XA_CODE, code_str,
2374                                  PCMK_XA_NAME, name,
2375                                  PCMK_XA_DESCRIPTION, desc,
2376                                  NULL);
2377     free(code_str);
2378     return pcmk_rc_ok;
2379 }
2380 
2381 PCMK__OUTPUT_ARGS("ticket-attribute", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2382 static int
2383 ticket_attribute_default(pcmk__output_t *out, va_list args)
2384 {
2385     const char *ticket_id G_GNUC_UNUSED = va_arg(args, const char *);
2386     const char *name G_GNUC_UNUSED = va_arg(args, const char *);
2387     const char *value = va_arg(args, const char *);
2388 
2389     out->info(out, "%s", value);
2390     return pcmk_rc_ok;
2391 }
2392 
2393 PCMK__OUTPUT_ARGS("ticket-attribute", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2394 static int
2395 ticket_attribute_xml(pcmk__output_t *out, va_list args)
2396 {
2397     const char *ticket_id = va_arg(args, const char *);
2398     const char *name = va_arg(args, const char *);
2399     const char *value = va_arg(args, const char *);
2400 
2401     /* Create:
2402      * <tickets>
2403      *   <ticket id="">
2404      *     <attribute name="" value="" />
2405      *   </ticket>
2406      * </tickets>
2407      */
2408     pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL);
2409     pcmk__output_xml_create_parent(out, PCMK_XE_TICKET,
2410                                    PCMK_XA_ID, ticket_id, NULL);
2411     pcmk__output_create_xml_node(out, PCMK_XA_ATTRIBUTE,
2412                                  PCMK_XA_NAME, name,
2413                                  PCMK_XA_VALUE, value,
2414                                  NULL);
2415     pcmk__output_xml_pop_parent(out);
2416     pcmk__output_xml_pop_parent(out);
2417 
2418     return pcmk_rc_ok;
2419 }
2420 
2421 PCMK__OUTPUT_ARGS("ticket-constraints", "xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
2422 static int
2423 ticket_constraints_default(pcmk__output_t *out, va_list args)
2424 {
2425     xmlNode *constraint_xml = va_arg(args, xmlNode *);
2426 
2427     /* constraint_xml can take two forms:
2428      *
2429      * <rsc_ticket id="rsc1-req-ticketA" rsc="rsc1" ticket="ticketA" ... />
2430      *
2431      * for when there's only one ticket in the CIB, or when the user asked
2432      * for a specific ticket (crm_ticket -c -t for instance)
2433      *
2434      * <xpath-query>
2435      *   <rsc_ticket id="rsc1-req-ticketA" rsc="rsc1" ticket="ticketA" ... />
2436      *   <rsc_ticket id="rsc1-req-ticketB" rsc="rsc2" ticket="ticketB" ... />
2437      * </xpath-query>
2438      *
2439      * for when there's multiple tickets in the and the user did not ask for
2440      * a specific one.
2441      *
2442      * In both cases, we simply output a <rsc_ticket> element for each ticket
2443      * in the results.
2444      */
2445     out->info(out, "Constraints XML:\n");
2446 
2447     if (pcmk__xe_is(constraint_xml, PCMK__XE_XPATH_QUERY)) {
2448         xmlNode *child = pcmk__xe_first_child(constraint_xml, NULL, NULL, NULL);
2449 
2450         do {
2451             GString *buf = g_string_sized_new(1024);
2452 
2453             pcmk__xml_string(child, pcmk__xml_fmt_pretty, buf, 0);
2454             out->output_xml(out, PCMK_XE_CONSTRAINT, buf->str);
2455             g_string_free(buf, TRUE);
2456 
2457             child = pcmk__xe_next(child);
2458         } while (child != NULL);
2459     } else {
2460         GString *buf = g_string_sized_new(1024);
2461 
2462         pcmk__xml_string(constraint_xml, pcmk__xml_fmt_pretty, buf, 0);
2463         out->output_xml(out, PCMK_XE_CONSTRAINT, buf->str);
2464         g_string_free(buf, TRUE);
2465     }
2466 
2467     return pcmk_rc_ok;
2468 }
2469 
2470 static int
2471 add_ticket_element_with_constraints(xmlNode *node, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
2472 {
2473     pcmk__output_t *out = (pcmk__output_t *) userdata;
2474     const char *ticket_id = crm_element_value(node, PCMK_XA_TICKET);
2475 
2476     pcmk__output_xml_create_parent(out, PCMK_XE_TICKET,
2477                                    PCMK_XA_ID, ticket_id, NULL);
2478     pcmk__output_xml_create_parent(out, PCMK_XE_CONSTRAINTS, NULL);
2479     pcmk__output_xml_add_node_copy(out, node);
2480 
2481     /* Pop two parents so now we are back under the <tickets> element */
2482     pcmk__output_xml_pop_parent(out);
2483     pcmk__output_xml_pop_parent(out);
2484 
2485     return pcmk_rc_ok;
2486 }
2487 
2488 static int
2489 add_resource_element(xmlNode *node, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
2490 {
2491     pcmk__output_t *out = (pcmk__output_t *) userdata;
2492     const char *rsc = crm_element_value(node, PCMK_XA_RSC);
2493 
2494     pcmk__output_create_xml_node(out, PCMK_XE_RESOURCE,
2495                                  PCMK_XA_ID, rsc, NULL);
2496     return pcmk_rc_ok;
2497 }
2498 
2499 PCMK__OUTPUT_ARGS("ticket-constraints", "xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
2500 static int
2501 ticket_constraints_xml(pcmk__output_t *out, va_list args)
2502 {
2503     xmlNode *constraint_xml = va_arg(args, xmlNode *);
2504 
2505     /* Create:
2506      * <tickets>
2507      *   <ticket id="">
2508      *     <constraints>
2509      *       <rsc_ticket />
2510      *     </constraints>
2511      *   </ticket>
2512      *   ...
2513      * </tickets>
2514      */
2515     pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL);
2516 
2517     if (pcmk__xe_is(constraint_xml, PCMK__XE_XPATH_QUERY)) {
2518         /* Iterate through the list of children once to create all the
2519          * ticket/constraint elements.
2520          */
2521         pcmk__xe_foreach_child(constraint_xml, NULL, add_ticket_element_with_constraints, out);
2522 
2523         /* Put us back at the same level as where <tickets> was created. */
2524         pcmk__output_xml_pop_parent(out);
2525 
2526         /* Constraints can reference a resource ID that is defined in the XML
2527          * schema as an IDREF.  This requires some other element to be present
2528          * with an id= attribute that matches.
2529          *
2530          * Iterate through the list of children a second time to create the
2531          * following:
2532          *
2533          * <resources>
2534          *   <resource id="" />
2535          *   ...
2536          * </resources>
2537          */
2538         pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
2539         pcmk__xe_foreach_child(constraint_xml, NULL, add_resource_element, out);
2540         pcmk__output_xml_pop_parent(out);
2541 
2542     } else {
2543         /* Creating the output for a single constraint is much easier.  All the
2544          * comments in the above block apply here.
2545          */
2546         add_ticket_element_with_constraints(constraint_xml, out);
2547         pcmk__output_xml_pop_parent(out);
2548 
2549         pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
2550         add_resource_element(constraint_xml, out);
2551         pcmk__output_xml_pop_parent(out);
2552     }
2553 
2554     return pcmk_rc_ok;
2555 }
2556 
2557 PCMK__OUTPUT_ARGS("ticket-state", "xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
2558 static int
2559 ticket_state_default(pcmk__output_t *out, va_list args)
2560 {
2561     xmlNode *state_xml = va_arg(args, xmlNode *);
2562 
2563     GString *buf = g_string_sized_new(1024);
2564 
2565     out->info(out, "State XML:\n");
2566     pcmk__xml_string(state_xml, pcmk__xml_fmt_pretty, buf, 0);
2567     out->output_xml(out, PCMK__XE_TICKET_STATE, buf->str);
2568 
2569     g_string_free(buf, TRUE);
2570     return pcmk_rc_ok;
2571 }
2572 
2573 static int
2574 add_ticket_element(xmlNode *node, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
2575 {
2576     pcmk__output_t *out = (pcmk__output_t *) userdata;
2577     xmlNode *ticket_node = NULL;
2578 
2579     ticket_node = pcmk__output_create_xml_node(out, PCMK_XE_TICKET, NULL);
2580     pcmk__xe_copy_attrs(ticket_node, node, pcmk__xaf_none);
2581     return pcmk_rc_ok;
2582 }
2583 
2584 PCMK__OUTPUT_ARGS("ticket-state", "xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
2585 static int
2586 ticket_state_xml(pcmk__output_t *out, va_list args)
2587 {
2588     xmlNode *state_xml = va_arg(args, xmlNode *);
2589 
2590     /* Create:
2591      * <tickets>
2592      *   <ticket />
2593      *   ...
2594      * </tickets>
2595      */
2596     pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL);
2597 
2598     if (state_xml->children != NULL) {
2599         /* Iterate through the list of children once to create all the
2600          * ticket elements.
2601          */
2602         pcmk__xe_foreach_child(state_xml, PCMK__XE_TICKET_STATE, add_ticket_element, out);
2603 
2604     } else {
2605         add_ticket_element(state_xml, out);
2606     }
2607 
2608     pcmk__output_xml_pop_parent(out);
2609     return pcmk_rc_ok;
2610 }
2611 
2612 static pcmk__message_entry_t fmt_functions[] = {
2613     { "attribute", "default", attribute_default },
2614     { "attribute", "xml", attribute_xml },
2615     { "cluster-status", "default", pcmk__cluster_status_text },
2616     { "cluster-status", "html", cluster_status_html },
2617     { "cluster-status", "xml", cluster_status_xml },
2618     { "crmadmin-node", "default", crmadmin_node },
2619     { "crmadmin-node", "text", crmadmin_node_text },
2620     { "crmadmin-node", "xml", crmadmin_node_xml },
2621     { "dc", "default", dc },
2622     { "dc", "text", dc_text },
2623     { "dc", "xml", dc_xml },
2624     { "digests", "default", digests_text },
2625     { "digests", "xml", digests_xml },
2626     { "health", "default", health },
2627     { "health", "text", health_text },
2628     { "health", "xml", health_xml },
2629     { "inject-attr", "default", inject_attr },
2630     { "inject-attr", "xml", inject_attr_xml },
2631     { "inject-cluster-action", "default", inject_cluster_action },
2632     { "inject-cluster-action", "xml", inject_cluster_action_xml },
2633     { "inject-fencing-action", "default", inject_fencing_action },
2634     { "inject-fencing-action", "xml", inject_fencing_action_xml },
2635     { "inject-modify-config", "default", inject_modify_config },
2636     { "inject-modify-config", "xml", inject_modify_config_xml },
2637     { "inject-modify-node", "default", inject_modify_node },
2638     { "inject-modify-node", "xml", inject_modify_node_xml },
2639     { "inject-modify-ticket", "default", inject_modify_ticket },
2640     { "inject-modify-ticket", "xml", inject_modify_ticket_xml },
2641     { "inject-pseudo-action", "default", inject_pseudo_action },
2642     { "inject-pseudo-action", "xml", inject_pseudo_action_xml },
2643     { "inject-rsc-action", "default", inject_rsc_action },
2644     { "inject-rsc-action", "xml", inject_rsc_action_xml },
2645     { "inject-spec", "default", inject_spec },
2646     { "inject-spec", "xml", inject_spec_xml },
2647     { "locations-and-colocations", "default", locations_and_colocations },
2648     { "locations-and-colocations", "xml", locations_and_colocations_xml },
2649     { "locations-list", "default", locations_list },
2650     { "locations-list", "xml", locations_list_xml },
2651     { "node-action", "default", node_action },
2652     { "node-action", "xml", node_action_xml },
2653     { "node-info", "default", node_info_default },
2654     { "node-info", "xml", node_info_xml },
2655     { "pacemakerd-health", "default", pacemakerd_health },
2656     { "pacemakerd-health", "html", pacemakerd_health_html },
2657     { "pacemakerd-health", "text", pacemakerd_health_text },
2658     { "pacemakerd-health", "xml", pacemakerd_health_xml },
2659     { "profile", "default", profile_default, },
2660     { "profile", "xml", profile_xml },
2661     { "result-code", PCMK_VALUE_NONE, result_code_none },
2662     { "result-code", "text", result_code_text },
2663     { "result-code", "xml", result_code_xml },
2664     { "rsc-action", "default", rsc_action_default },
2665     { "rsc-action-item", "default", rsc_action_item },
2666     { "rsc-action-item", "xml", rsc_action_item_xml },
2667     { "rsc-is-colocated-with-list", "default", rsc_is_colocated_with_list },
2668     { "rsc-is-colocated-with-list", "xml", rsc_is_colocated_with_list_xml },
2669     { "rscs-colocated-with-list", "default", rscs_colocated_with_list },
2670     { "rscs-colocated-with-list", "xml", rscs_colocated_with_list_xml },
2671     { "rule-check", "default", rule_check_default },
2672     { "rule-check", "xml", rule_check_xml },
2673     { "ticket-attribute", "default", ticket_attribute_default },
2674     { "ticket-attribute", "xml", ticket_attribute_xml },
2675     { "ticket-constraints", "default", ticket_constraints_default },
2676     { "ticket-constraints", "xml", ticket_constraints_xml },
2677     { "ticket-state", "default", ticket_state_default },
2678     { "ticket-state", "xml", ticket_state_xml },
2679 
2680     { NULL, NULL, NULL }
2681 };
2682 
2683 void
2684 pcmk__register_lib_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
2685     pcmk__register_messages(out, fmt_functions);
2686 }

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