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

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