root/tools/crm_simulate.c

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

DEFINITIONS

This source file includes following definitions.
  1. in_place_cb
  2. live_check_cb
  3. node_down_cb
  4. node_fail_cb
  5. node_up_cb
  6. op_fail_cb
  7. op_inject_cb
  8. quorum_cb
  9. save_dotfile_cb
  10. save_graph_cb
  11. show_scores_cb
  12. simulate_cb
  13. ticket_activate_cb
  14. ticket_grant_cb
  15. ticket_revoke_cb
  16. ticket_standby_cb
  17. utilization_cb
  18. watchdog_cb
  19. xml_file_cb
  20. xml_pipe_cb
  21. get_date
  22. print_cluster_status
  23. create_action_name
  24. create_dotfile
  25. setup_input
  26. profile_one
  27. profile_all
  28. PCMK__OUTPUT_ARGS
  29. PCMK__OUTPUT_ARGS
  30. crm_simulate_register_messages
  31. build_arg_context
  32. main

   1 /*
   2  * Copyright 2009-2021 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 
  12 #include <stdio.h>
  13 #include <unistd.h>
  14 #include <stdlib.h>
  15 #include <time.h>
  16 
  17 #include <sys/stat.h>
  18 #include <sys/param.h>
  19 #include <sys/types.h>
  20 #include <dirent.h>
  21 
  22 #include <crm/crm.h>
  23 #include <crm/cib.h>
  24 #include <crm/common/cmdline_internal.h>
  25 #include <crm/common/output_internal.h>
  26 #include <crm/common/output.h>
  27 #include <crm/common/util.h>
  28 #include <crm/common/iso8601.h>
  29 #include <crm/pengine/status.h>
  30 #include <pacemaker-internal.h>
  31 
  32 #define SUMMARY "crm_simulate - simulate a Pacemaker cluster's response to events"
  33 
  34 struct {
  35     gboolean all_actions;
  36     char *dot_file;
  37     char *graph_file;
  38     gchar *input_file;
  39     guint modified;
  40     GList *node_up;
  41     GList *node_down;
  42     GList *node_fail;
  43     GList *op_fail;
  44     GList *op_inject;
  45     gchar *output_file;
  46     gboolean print_pending;
  47     gboolean process;
  48     char *quorum;
  49     long long repeat;
  50     gboolean show_attrs;
  51     gboolean show_failcounts;
  52     gboolean show_scores;
  53     gboolean show_utilization;
  54     gboolean simulate;
  55     gboolean store;
  56     gchar *test_dir;
  57     GList *ticket_grant;
  58     GList *ticket_revoke;
  59     GList *ticket_standby;
  60     GList *ticket_activate;
  61     char *use_date;
  62     char *watchdog;
  63     char *xml_file;
  64 } options = {
  65     .print_pending = TRUE,
  66     .repeat = 1
  67 };
  68 
  69 cib_t *global_cib = NULL;
  70 bool action_numbers = FALSE;
  71 char *temp_shadow = NULL;
  72 extern gboolean bringing_nodes_online;
  73 crm_exit_t exit_code = CRM_EX_OK;
  74 
  75 #define INDENT "                                   "
  76 
  77 static pcmk__supported_format_t formats[] = {
  78     PCMK__SUPPORTED_FORMAT_NONE,
  79     PCMK__SUPPORTED_FORMAT_TEXT,
  80     PCMK__SUPPORTED_FORMAT_XML,
  81     { NULL, NULL, NULL }
  82 };
  83 
  84 static gboolean
  85 in_place_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  86     options.store = TRUE;
  87     options.process = TRUE;
  88     options.simulate = TRUE;
  89     return TRUE;
  90 }
  91 
  92 static gboolean
  93 live_check_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  94     if (options.xml_file) {
  95         free(options.xml_file);
  96     }
  97 
  98     options.xml_file = NULL;
  99     return TRUE;
 100 }
 101 
 102 static gboolean
 103 node_down_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 104     options.modified++;
 105     options.node_down = g_list_append(options.node_down, (gchar *) g_strdup(optarg));
 106     return TRUE;
 107 }
 108 
 109 static gboolean
 110 node_fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 111     options.modified++;
 112     options.node_fail = g_list_append(options.node_fail, (gchar *) g_strdup(optarg));
 113     return TRUE;
 114 }
 115 
 116 static gboolean
 117 node_up_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 118     options.modified++;
 119     bringing_nodes_online = TRUE;
 120     options.node_up = g_list_append(options.node_up, (gchar *) g_strdup(optarg));
 121     return TRUE;
 122 }
 123 
 124 static gboolean
 125 op_fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 126     options.process = TRUE;
 127     options.simulate = TRUE;
 128     options.op_fail = g_list_append(options.op_fail, (gchar *) g_strdup(optarg));
 129     return TRUE;
 130 }
 131 
 132 static gboolean
 133 op_inject_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 134     options.modified++;
 135     options.op_inject = g_list_append(options.op_inject, (gchar *) g_strdup(optarg));
 136     return TRUE;
 137 }
 138 
 139 static gboolean
 140 quorum_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 141     if (options.quorum) {
 142         free(options.quorum);
 143     }
 144 
 145     options.modified++;
 146     options.quorum = strdup(optarg);
 147     return TRUE;
 148 }
 149 
 150 static gboolean
 151 save_dotfile_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 152     if (options.dot_file) {
 153         free(options.dot_file);
 154     }
 155 
 156     options.process = TRUE;
 157     options.dot_file = strdup(optarg);
 158     return TRUE;
 159 }
 160 
 161 static gboolean
 162 save_graph_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 163     if (options.graph_file) {
 164         free(options.graph_file);
 165     }
 166 
 167     options.process = TRUE;
 168     options.graph_file = strdup(optarg);
 169     return TRUE;
 170 }
 171 
 172 static gboolean
 173 show_scores_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 174     options.process = TRUE;
 175     options.show_scores = TRUE;
 176     return TRUE;
 177 }
 178 
 179 static gboolean
 180 simulate_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 181     options.process = TRUE;
 182     options.simulate = TRUE;
 183     return TRUE;
 184 }
 185 
 186 static gboolean
 187 ticket_activate_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 188     options.modified++;
 189     options.ticket_activate = g_list_append(options.ticket_activate, (gchar *) g_strdup(optarg));
 190     return TRUE;
 191 }
 192 
 193 static gboolean
 194 ticket_grant_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 195     options.modified++;
 196     options.ticket_grant = g_list_append(options.ticket_grant, (gchar *) g_strdup(optarg));
 197     return TRUE;
 198 }
 199 
 200 static gboolean
 201 ticket_revoke_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 202     options.modified++;
 203     options.ticket_revoke = g_list_append(options.ticket_revoke, (gchar *) g_strdup(optarg));
 204     return TRUE;
 205 }
 206 
 207 static gboolean
 208 ticket_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 209     options.modified++;
 210     options.ticket_standby = g_list_append(options.ticket_standby, (gchar *) g_strdup(optarg));
 211     return TRUE;
 212 }
 213 
 214 static gboolean
 215 utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 216     options.process = TRUE;
 217     options.show_utilization = TRUE;
 218     return TRUE;
 219 }
 220 
 221 static gboolean
 222 watchdog_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 223     if (options.watchdog) {
 224         free(options.watchdog);
 225     }
 226 
 227     options.modified++;
 228     options.watchdog = strdup(optarg);
 229     return TRUE;
 230 }
 231 
 232 static gboolean
 233 xml_file_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 234     if (options.xml_file) {
 235         free(options.xml_file);
 236     }
 237 
 238     options.xml_file = strdup(optarg);
 239     return TRUE;
 240 }
 241 
 242 static gboolean
 243 xml_pipe_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 244     if (options.xml_file) {
 245         free(options.xml_file);
 246     }
 247 
 248     options.xml_file = strdup("-");
 249     return TRUE;
 250 }
 251 
 252 static GOptionEntry operation_entries[] = {
 253     { "run", 'R', 0, G_OPTION_ARG_NONE, &options.process,
 254       "Process the supplied input and show what actions the cluster will take in response",
 255       NULL },
 256     { "simulate", 'S', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, simulate_cb,
 257       "Like --run, but also simulate taking those actions and show the resulting new status",
 258       NULL },
 259     { "in-place", 'X', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, in_place_cb,
 260       "Like --simulate, but also store the results back to the input file",
 261       NULL },
 262     { "show-attrs", 'A', 0, G_OPTION_ARG_NONE, &options.show_attrs,
 263       "Show node attributes",
 264       NULL },
 265     { "show-failcounts", 'c', 0, G_OPTION_ARG_NONE, &options.show_failcounts,
 266       "Show resource fail counts",
 267       NULL },
 268     { "show-scores", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_scores_cb,
 269       "Show allocation scores",
 270       NULL },
 271     { "show-utilization", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
 272       "Show utilization information",
 273       NULL },
 274     { "profile", 'P', 0, G_OPTION_ARG_FILENAME, &options.test_dir,
 275       "Process all the XML files in the named directory to create profiling data",
 276       "DIR" },
 277     { "repeat", 'N', 0, G_OPTION_ARG_INT, &options.repeat,
 278       "With --profile, repeat each test N times and print timings",
 279       "N" },
 280     /* Deprecated */
 281     { "pending", 'j', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.print_pending,
 282       "Display pending state if 'record-pending' is enabled",
 283       NULL },
 284 
 285     { NULL }
 286 };
 287 
 288 static GOptionEntry synthetic_entries[] = {
 289     { "node-up", 'u', 0, G_OPTION_ARG_CALLBACK, node_up_cb,
 290       "Simulate bringing a node online",
 291       "NODE" },
 292     { "node-down", 'd', 0, G_OPTION_ARG_CALLBACK, node_down_cb,
 293       "Simulate taking a node offline",
 294       "NODE" },
 295     { "node-fail", 'f', 0, G_OPTION_ARG_CALLBACK, node_fail_cb,
 296       "Simulate a node failing",
 297       "NODE" },
 298     { "op-inject", 'i', 0, G_OPTION_ARG_CALLBACK, op_inject_cb,
 299       "Generate a failure for the cluster to react to in the simulation.\n"
 300       INDENT "See `Operation Specification` help for more information.",
 301       "OPSPEC" },
 302     { "op-fail", 'F', 0, G_OPTION_ARG_CALLBACK, op_fail_cb,
 303       "If the specified task occurs during the simulation, have it fail with return code ${rc}.\n"
 304       INDENT "The transition will normally stop at the failed action.\n"
 305       INDENT "Save the result with --save-output and re-run with --xml-file.\n"
 306       INDENT "See `Operation Specification` help for more information.",
 307       "OPSPEC" },
 308     { "set-datetime", 't', 0, G_OPTION_ARG_STRING, &options.use_date,
 309       "Set date/time (ISO 8601 format, see https://en.wikipedia.org/wiki/ISO_8601)",
 310       "DATETIME" },
 311     { "quorum", 'q', 0, G_OPTION_ARG_CALLBACK, quorum_cb,
 312       "Set to '1' (or 'true') to indicate cluster has quorum",
 313       "QUORUM" },
 314     { "watchdog", 'w', 0, G_OPTION_ARG_CALLBACK, watchdog_cb,
 315       "Set to '1' (or 'true') to indicate cluster has an active watchdog device",
 316       "DEVICE" },
 317     { "ticket-grant", 'g', 0, G_OPTION_ARG_CALLBACK, ticket_grant_cb,
 318       "Simulate granting a ticket",
 319       "TICKET" },
 320     { "ticket-revoke", 'r', 0, G_OPTION_ARG_CALLBACK, ticket_revoke_cb,
 321       "Simulate revoking a ticket",
 322       "TICKET" },
 323     { "ticket-standby", 'b', 0, G_OPTION_ARG_CALLBACK, ticket_standby_cb,
 324       "Simulate making a ticket standby",
 325       "TICKET" },
 326     { "ticket-activate", 'e', 0, G_OPTION_ARG_CALLBACK, ticket_activate_cb,
 327       "Simulate activating a ticket",
 328       "TICKET" },
 329 
 330     { NULL }
 331 };
 332 
 333 static GOptionEntry artifact_entries[] = {
 334     { "save-input", 'I', 0, G_OPTION_ARG_FILENAME, &options.input_file,
 335       "Save the input configuration to the named file",
 336       "FILE" },
 337     { "save-output", 'O', 0, G_OPTION_ARG_FILENAME, &options.output_file,
 338       "Save the output configuration to the named file",
 339       "FILE" },
 340     { "save-graph", 'G', 0, G_OPTION_ARG_CALLBACK, save_graph_cb,
 341       "Save the transition graph (XML format) to the named file",
 342       "FILE" },
 343     { "save-dotfile", 'D', 0, G_OPTION_ARG_CALLBACK, save_dotfile_cb,
 344       "Save the transition graph (DOT format) to the named file",
 345       "FILE" },
 346     { "all-actions", 'a', 0, G_OPTION_ARG_NONE, &options.all_actions,
 347       "Display all possible actions in DOT graph (even if not part of transition)",
 348       NULL },
 349 
 350     { NULL }
 351 };
 352 
 353 static GOptionEntry source_entries[] = {
 354     { "live-check", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, live_check_cb,
 355       "Connect to CIB manager and use the current CIB contents as input",
 356       NULL },
 357     { "xml-file", 'x', 0, G_OPTION_ARG_CALLBACK, xml_file_cb,
 358       "Retrieve XML from the named file",
 359       "FILE" },
 360     { "xml-pipe", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, xml_pipe_cb,
 361       "Retrieve XML from stdin",
 362       NULL },
 363 
 364     { NULL }
 365 };
 366 
 367 static void
 368 get_date(pe_working_set_t *data_set, bool print_original, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 369 {
 370     pcmk__output_t *out = data_set->priv;
 371     time_t original_date = 0;
 372 
 373     crm_element_value_epoch(data_set->input, "execution-date", &original_date);
 374 
 375     if (use_date) {
 376         data_set->now = crm_time_new(use_date);
 377         out->info(out, "Setting effective cluster time: %s", use_date);
 378         crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
 379                      crm_time_log_date | crm_time_log_timeofday);
 380 
 381 
 382     } else if (original_date) {
 383 
 384         data_set->now = crm_time_new(NULL);
 385         crm_time_set_timet(data_set->now, &original_date);
 386 
 387         if (print_original) {
 388             char *when = crm_time_as_string(data_set->now,
 389                             crm_time_log_date|crm_time_log_timeofday);
 390 
 391             out->info(out, "Using the original execution date of: %s", when);
 392             free(when);
 393         }
 394     }
 395 }
 396 
 397 static void
 398 print_cluster_status(pe_working_set_t * data_set, unsigned int show_opts)
     /* [previous][next][first][last][top][bottom][index][help] */
 399 {
 400     pcmk__output_t *out = data_set->priv;
 401     int rc = pcmk_rc_no_output;
 402     GList *all = NULL;
 403 
 404     all = g_list_prepend(all, (gpointer) "*");
 405 
 406     rc = out->message(out, "node-list", data_set->nodes, all, all, show_opts);
 407     PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
 408     rc = out->message(out, "resource-list", data_set, show_opts | pcmk_show_inactive_rscs,
 409                       FALSE, all, all, FALSE);
 410 
 411     if (options.show_attrs) {
 412         rc = out->message(out, "node-attribute-list", data_set,
 413                           0, rc == pcmk_rc_ok, all, all);
 414     }
 415 
 416     if (options.show_failcounts) {
 417         rc = out->message(out, "node-summary", data_set, all, all,
 418                           0, show_opts, rc == pcmk_rc_ok);
 419 
 420         out->message(out, "failed-action-list", data_set, all, all,
 421                      rc == pcmk_rc_ok);
 422     }
 423 
 424     g_list_free(all);
 425 }
 426 
 427 static char *
 428 create_action_name(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 429 {
 430     char *action_name = NULL;
 431     const char *prefix = "";
 432     const char *action_host = NULL;
 433     const char *clone_name = NULL;
 434     const char *task = action->task;
 435 
 436     if (action->node) {
 437         action_host = action->node->details->uname;
 438     } else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
 439         action_host = "<none>";
 440     }
 441 
 442     if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_casei)) {
 443         prefix = "Cancel ";
 444         task = action->cancel_task;
 445     }
 446 
 447     if (action->rsc && action->rsc->clone_name) {
 448         clone_name = action->rsc->clone_name;
 449     }
 450 
 451     if (clone_name) {
 452         char *key = NULL;
 453         guint interval_ms = 0;
 454 
 455         if (pcmk__guint_from_hash(action->meta,
 456                                   XML_LRM_ATTR_INTERVAL_MS, 0,
 457                                   &interval_ms) != pcmk_rc_ok) {
 458             interval_ms = 0;
 459         }
 460 
 461         if (pcmk__strcase_any_of(action->task, RSC_NOTIFY, RSC_NOTIFIED, NULL)) {
 462             const char *n_type = g_hash_table_lookup(action->meta, "notify_key_type");
 463             const char *n_task = g_hash_table_lookup(action->meta, "notify_key_operation");
 464 
 465             CRM_ASSERT(n_type != NULL);
 466             CRM_ASSERT(n_task != NULL);
 467             key = pcmk__notify_key(clone_name, n_type, n_task);
 468 
 469         } else {
 470             key = pcmk__op_key(clone_name, task, interval_ms);
 471         }
 472 
 473         if (action_host) {
 474             action_name = crm_strdup_printf("%s%s %s", prefix, key, action_host);
 475         } else {
 476             action_name = crm_strdup_printf("%s%s", prefix, key);
 477         }
 478         free(key);
 479 
 480     } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
 481         const char *op = g_hash_table_lookup(action->meta, "stonith_action");
 482 
 483         action_name = crm_strdup_printf("%s%s '%s' %s", prefix, action->task, op, action_host);
 484 
 485     } else if (action->rsc && action_host) {
 486         action_name = crm_strdup_printf("%s%s %s", prefix, action->uuid, action_host);
 487 
 488     } else if (action_host) {
 489         action_name = crm_strdup_printf("%s%s %s", prefix, action->task, action_host);
 490 
 491     } else {
 492         action_name = crm_strdup_printf("%s", action->uuid);
 493     }
 494 
 495     if (action_numbers) { // i.e. verbose
 496         char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
 497 
 498         free(action_name);
 499         action_name = with_id;
 500     }
 501     return action_name;
 502 }
 503 
 504 static bool
 505 create_dotfile(pe_working_set_t * data_set, const char *dot_file, gboolean all_actions,
     /* [previous][next][first][last][top][bottom][index][help] */
 506                GError **error)
 507 {
 508     GList *gIter = NULL;
 509     FILE *dot_strm = fopen(dot_file, "w");
 510 
 511     if (dot_strm == NULL) {
 512         g_set_error(error, PCMK__RC_ERROR, errno,
 513                     "Could not open %s for writing: %s", dot_file,
 514                     pcmk_rc_str(errno));
 515         return false;
 516     }
 517 
 518     fprintf(dot_strm, " digraph \"g\" {\n");
 519     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 520         pe_action_t *action = (pe_action_t *) gIter->data;
 521         const char *style = "dashed";
 522         const char *font = "black";
 523         const char *color = "black";
 524         char *action_name = create_action_name(action);
 525 
 526         crm_trace("Action %d: %s %s %p", action->id, action_name, action->uuid, action);
 527 
 528         if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 529             font = "orange";
 530         }
 531 
 532         if (pcmk_is_set(action->flags, pe_action_dumped)) {
 533             style = "bold";
 534             color = "green";
 535 
 536         } else if ((action->rsc != NULL)
 537                    && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
 538             color = "red";
 539             font = "purple";
 540             if (all_actions == FALSE) {
 541                 goto do_not_write;
 542             }
 543 
 544         } else if (pcmk_is_set(action->flags, pe_action_optional)) {
 545             color = "blue";
 546             if (all_actions == FALSE) {
 547                 goto do_not_write;
 548             }
 549 
 550         } else {
 551             color = "red";
 552             CRM_CHECK(!pcmk_is_set(action->flags, pe_action_runnable), ;);
 553         }
 554 
 555         pe__set_action_flags(action, pe_action_dumped);
 556         crm_trace("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]",
 557                 action_name, style, color, font);
 558         fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
 559                 action_name, style, color, font);
 560   do_not_write:
 561         free(action_name);
 562     }
 563 
 564     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 565         pe_action_t *action = (pe_action_t *) gIter->data;
 566 
 567         GList *gIter2 = NULL;
 568 
 569         for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
 570             pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
 571 
 572             char *before_name = NULL;
 573             char *after_name = NULL;
 574             const char *style = "dashed";
 575             gboolean optional = TRUE;
 576 
 577             if (before->state == pe_link_dumped) {
 578                 optional = FALSE;
 579                 style = "bold";
 580             } else if (pcmk_is_set(action->flags, pe_action_pseudo)
 581                        && (before->type & pe_order_stonith_stop)) {
 582                 continue;
 583             } else if (before->type == pe_order_none) {
 584                 continue;
 585             } else if (pcmk_is_set(before->action->flags, pe_action_dumped)
 586                        && pcmk_is_set(action->flags, pe_action_dumped)
 587                        && before->type != pe_order_load) {
 588                 optional = FALSE;
 589             }
 590 
 591             if (all_actions || optional == FALSE) {
 592                 before_name = create_action_name(before->action);
 593                 after_name = create_action_name(action);
 594                 crm_trace("\"%s\" -> \"%s\" [ style = %s]",
 595                         before_name, after_name, style);
 596                 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
 597                         before_name, after_name, style);
 598                 free(before_name);
 599                 free(after_name);
 600             }
 601         }
 602     }
 603 
 604     fprintf(dot_strm, "}\n");
 605     fflush(dot_strm);
 606     fclose(dot_strm);
 607     return true;
 608 }
 609 
 610 static int
 611 setup_input(const char *input, const char *output, GError **error)
     /* [previous][next][first][last][top][bottom][index][help] */
 612 {
 613     int rc = pcmk_rc_ok;
 614     cib_t *cib_conn = NULL;
 615     xmlNode *cib_object = NULL;
 616     char *local_output = NULL;
 617 
 618     if (input == NULL) {
 619         /* Use live CIB */
 620         cib_conn = cib_new();
 621         rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
 622         rc = pcmk_legacy2rc(rc);
 623 
 624         if (rc == pcmk_rc_ok) {
 625             rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, cib_scope_local | cib_sync_call);
 626         }
 627 
 628         cib_conn->cmds->signoff(cib_conn);
 629         cib_delete(cib_conn);
 630         cib_conn = NULL;
 631 
 632         if (rc != pcmk_rc_ok) {
 633             rc = pcmk_legacy2rc(rc);
 634             g_set_error(error, PCMK__RC_ERROR, rc,
 635                         "Live CIB query failed: %s (%d)", pcmk_rc_str(rc), rc);
 636             return rc;
 637 
 638         } else if (cib_object == NULL) {
 639             g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_NOINPUT,
 640                         "Live CIB query failed: empty result");
 641             return pcmk_rc_no_input;
 642         }
 643 
 644     } else if (pcmk__str_eq(input, "-", pcmk__str_casei)) {
 645         cib_object = filename2xml(NULL);
 646 
 647     } else {
 648         cib_object = filename2xml(input);
 649     }
 650 
 651     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
 652         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 653     }
 654 
 655     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 656         free_xml(cib_object);
 657         return pcmk_rc_transform_failed;
 658     }
 659 
 660     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
 661         free_xml(cib_object);
 662         return pcmk_rc_schema_validation;
 663     }
 664 
 665     if (output == NULL) {
 666         char *pid = pcmk__getpid_s();
 667 
 668         local_output = get_shadow_file(pid);
 669         temp_shadow = strdup(local_output);
 670         output = local_output;
 671         free(pid);
 672     }
 673 
 674     rc = write_xml_file(cib_object, output, FALSE);
 675     free_xml(cib_object);
 676     cib_object = NULL;
 677 
 678     if (rc < 0) {
 679         rc = pcmk_legacy2rc(rc);
 680         g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_CANTCREAT,
 681                     "Could not create '%s': %s", output, pcmk_rc_str(rc));
 682         return rc;
 683     } else {
 684         setenv("CIB_file", output, 1);
 685         free(local_output);
 686         return pcmk_rc_ok;
 687     }
 688 }
 689 
 690 static void
 691 profile_one(const char *xml_file, long long repeat, pe_working_set_t *data_set, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 692 {
 693     pcmk__output_t *out = data_set->priv;
 694     xmlNode *cib_object = NULL;
 695     clock_t start = 0;
 696     clock_t end;
 697 
 698     cib_object = filename2xml(xml_file);
 699     start = clock();
 700 
 701     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
 702         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 703     }
 704 
 705 
 706     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 707         free_xml(cib_object);
 708         return;
 709     }
 710 
 711     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
 712         free_xml(cib_object);
 713         return;
 714     }
 715 
 716     for (int i = 0; i < repeat; ++i) {
 717         xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
 718 
 719         data_set->input = input;
 720         get_date(data_set, false, use_date);
 721         pcmk__schedule_actions(data_set, input, NULL);
 722         pe_reset_working_set(data_set);
 723     }
 724 
 725     end = clock();
 726     out->message(out, "profile", xml_file, start, end);
 727 }
 728 
 729 #ifndef FILENAME_MAX
 730 #  define FILENAME_MAX 512
 731 #endif
 732 
 733 static void
 734 profile_all(const char *dir, long long repeat, pe_working_set_t *data_set, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 735 {
 736     pcmk__output_t *out = data_set->priv;
 737     struct dirent **namelist;
 738 
 739     int file_num = scandir(dir, &namelist, 0, alphasort);
 740 
 741     if (file_num > 0) {
 742         struct stat prop;
 743         char buffer[FILENAME_MAX];
 744 
 745         out->begin_list(out, NULL, NULL, "Timings");
 746 
 747         while (file_num--) {
 748             if ('.' == namelist[file_num]->d_name[0]) {
 749                 free(namelist[file_num]);
 750                 continue;
 751 
 752             } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
 753                                             ".xml")) {
 754                 free(namelist[file_num]);
 755                 continue;
 756             }
 757             snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
 758             if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
 759                 profile_one(buffer, repeat, data_set, use_date);
 760             }
 761             free(namelist[file_num]);
 762         }
 763         free(namelist);
 764 
 765         out->end_list(out);
 766     }
 767 }
 768 
 769 PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 770 static int
 771 profile_default(pcmk__output_t *out, va_list args) {
 772     const char *xml_file = va_arg(args, const char *);
 773     clock_t start = va_arg(args, clock_t);
 774     clock_t end = va_arg(args, clock_t);
 775 
 776     out->list_item(out, NULL, "Testing %s ... %.2f secs", xml_file,
 777                    (end - start) / (float) CLOCKS_PER_SEC);
 778 
 779     return pcmk_rc_ok;
 780 }
 781 
 782 PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 783 static int
 784 profile_xml(pcmk__output_t *out, va_list args) {
 785     const char *xml_file = va_arg(args, const char *);
 786     clock_t start = va_arg(args, clock_t);
 787     clock_t end = va_arg(args, clock_t);
 788 
 789     char *duration = pcmk__ftoa((end - start) / (float) CLOCKS_PER_SEC);
 790 
 791     pcmk__output_create_xml_node(out, "timing",
 792                                  "file", xml_file,
 793                                  "duration", duration,
 794                                  NULL);
 795 
 796     free(duration);
 797     return pcmk_rc_ok;
 798 }
 799 
 800 static pcmk__message_entry_t fmt_functions[] = {
 801     { "profile", "default", profile_default, },
 802     { "profile", "xml", profile_xml },
 803 
 804     { NULL }
 805 };
 806 
 807 static void
 808 crm_simulate_register_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 809     pcmk__register_messages(out, fmt_functions);
 810 }
 811 
 812 static GOptionContext *
 813 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 814     GOptionContext *context = NULL;
 815 
 816     GOptionEntry extra_prog_entries[] = {
 817         { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
 818           "Display only essential output",
 819           NULL },
 820 
 821         { NULL }
 822     };
 823 
 824     const char *description = "Operation Specification:\n\n"
 825                               "The OPSPEC in any command line option is of the form\n"
 826                               "${resource}_${task}_${interval_in_ms}@${node}=${rc}\n"
 827                               "(memcached_monitor_20000@bart.example.com=7, for example).\n"
 828                               "${rc} is an OCF return code.  For more information on these\n"
 829                               "return codes, refer to https://clusterlabs.org/pacemaker/doc/2.1/Pacemaker_Administration/html/agents.html#ocf-return-codes\n\n"
 830                               "Examples:\n\n"
 831                               "Pretend a recurring monitor action found memcached stopped on node\n"
 832                               "fred.example.com and, during recovery, that the memcached stop\n"
 833                               "action failed:\n\n"
 834                               "\tcrm_simulate -LS --op-inject memcached:0_monitor_20000@bart.example.com=7 "
 835                               "--op-fail memcached:0_stop_0@fred.example.com=1 --save-output /tmp/memcached-test.xml\n\n"
 836                               "Now see what the reaction to the stop failed would be:\n\n"
 837                               "\tcrm_simulate -S --xml-file /tmp/memcached-test.xml\n\n";
 838 
 839     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 840     pcmk__add_main_args(context, extra_prog_entries);
 841     g_option_context_set_description(context, description);
 842 
 843     pcmk__add_arg_group(context, "operations", "Operations:",
 844                         "Show operations options", operation_entries);
 845     pcmk__add_arg_group(context, "synthetic", "Synthetic Cluster Events:",
 846                         "Show synthetic cluster event options", synthetic_entries);
 847     pcmk__add_arg_group(context, "artifact", "Artifact Options:",
 848                         "Show artifact options", artifact_entries);
 849     pcmk__add_arg_group(context, "source", "Data Source:",
 850                         "Show data source options", source_entries);
 851 
 852     return context;
 853 }
 854 
 855 int
 856 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 857 {
 858     int printed = pcmk_rc_no_output;
 859     int rc = pcmk_rc_ok;
 860     pe_working_set_t *data_set = NULL;
 861     pcmk__output_t *out = NULL;
 862     xmlNode *input = NULL;
 863 
 864     GError *error = NULL;
 865 
 866     GOptionGroup *output_group = NULL;
 867     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 868     gchar **processed_args = pcmk__cmdline_preproc(argv, "bdefgiqrtuwxDFGINOP");
 869     GOptionContext *context = build_arg_context(args, &output_group);
 870 
 871     /* This must come before g_option_context_parse_strv. */
 872     options.xml_file = strdup("-");
 873 
 874     pcmk__register_formats(output_group, formats);
 875     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 876         exit_code = CRM_EX_USAGE;
 877         goto done;
 878     }
 879 
 880     pcmk__cli_init_logging("crm_simulate", args->verbosity);
 881 
 882     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 883     if (rc != pcmk_rc_ok) {
 884         fprintf(stderr, "Error creating output format %s: %s\n",
 885                 args->output_ty, pcmk_rc_str(rc));
 886         exit_code = CRM_EX_ERROR;
 887         goto done;
 888     }
 889 
 890     if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches) &&
 891         !options.show_scores && !options.show_utilization) {
 892         pcmk__force_args(context, &error, "%s --text-fancy", g_get_prgname());
 893     } else if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
 894         pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname());
 895     }
 896 
 897     crm_simulate_register_messages(out);
 898     pe__register_messages(out);
 899     pcmk__register_lib_messages(out);
 900 
 901     out->quiet = args->quiet;
 902 
 903     if (args->version) {
 904         out->version(out, false);
 905         goto done;
 906     }
 907 
 908     if (args->verbosity > 0) {
 909 #ifdef PCMK__COMPAT_2_0
 910         /* Redirect stderr to stdout so we can grep the output */
 911         close(STDERR_FILENO);
 912         dup2(STDOUT_FILENO, STDERR_FILENO);
 913 #endif
 914         action_numbers = TRUE;
 915     }
 916 
 917     data_set = pe_new_working_set();
 918     if (data_set == NULL) {
 919         rc = ENOMEM;
 920         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not allocate working set");
 921         goto done;
 922     }
 923 
 924     if (options.show_scores) {
 925         pe__set_working_set_flags(data_set, pe_flag_show_scores);
 926     }
 927     if (options.show_utilization) {
 928         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 929     }
 930     pe__set_working_set_flags(data_set, pe_flag_no_compat);
 931 
 932     if (options.test_dir != NULL) {
 933         data_set->priv = out;
 934         profile_all(options.test_dir, options.repeat, data_set, options.use_date);
 935         rc = pcmk_rc_ok;
 936         goto done;
 937     }
 938 
 939     rc = setup_input(options.xml_file, options.store ? options.xml_file : options.output_file, &error);
 940     if (rc != pcmk_rc_ok) {
 941         goto done;
 942     }
 943 
 944     global_cib = cib_new();
 945     rc = global_cib->cmds->signon(global_cib, crm_system_name, cib_command);
 946     if (rc != pcmk_rc_ok) {
 947         rc = pcmk_legacy2rc(rc);
 948         g_set_error(&error, PCMK__RC_ERROR, rc,
 949                     "Could not connect to the CIB: %s", pcmk_rc_str(rc));
 950         goto done;
 951     }
 952 
 953     rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call | cib_scope_local);
 954     if (rc != pcmk_rc_ok) {
 955         rc = pcmk_legacy2rc(rc);
 956         g_set_error(&error, PCMK__RC_ERROR, rc,
 957                     "Could not get local CIB: %s", pcmk_rc_str(rc));
 958         goto done;
 959     }
 960 
 961     data_set->input = input;
 962     data_set->priv = out;
 963     get_date(data_set, true, options.use_date);
 964     if(options.xml_file) {
 965         pe__set_working_set_flags(data_set, pe_flag_sanitized);
 966     }
 967     if (options.show_scores) {
 968         pe__set_working_set_flags(data_set, pe_flag_show_scores);
 969     }
 970     if (options.show_utilization) {
 971         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 972     }
 973     cluster_status(data_set);
 974 
 975     if (!out->is_quiet(out)) {
 976         unsigned int show_opts = options.print_pending ? pcmk_show_pending : 0;
 977 
 978         if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 979             printed = out->message(out, "maint-mode", data_set->flags);
 980         }
 981 
 982         if (data_set->disabled_resources || data_set->blocked_resources) {
 983             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 984             printed = out->info(out, "%d of %d resource instances DISABLED and %d BLOCKED "
 985                                 "from further action due to failure",
 986                                 data_set->disabled_resources, data_set->ninstances,
 987                                 data_set->blocked_resources);
 988         }
 989 
 990         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 991         /* Most formatted output headers use caps for each word, but this one
 992          * only has the first word capitalized for compatibility with pcs.
 993          */
 994         out->begin_list(out, NULL, NULL, "Current cluster status");
 995         print_cluster_status(data_set, show_opts);
 996         out->end_list(out);
 997         printed = pcmk_rc_ok;
 998     }
 999 
1000     if (options.modified) {
1001         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1002         modify_configuration(data_set, global_cib, options.quorum, options.watchdog, options.node_up,
1003                              options.node_down, options.node_fail, options.op_inject,
1004                              options.ticket_grant, options.ticket_revoke, options.ticket_standby,
1005                              options.ticket_activate);
1006         printed = pcmk_rc_ok;
1007 
1008         rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call);
1009         if (rc != pcmk_rc_ok) {
1010             rc = pcmk_legacy2rc(rc);
1011             g_set_error(&error, PCMK__RC_ERROR, rc,
1012                         "Could not get modified CIB: %s", pcmk_rc_str(rc));
1013             goto done;
1014         }
1015 
1016         cleanup_calculations(data_set);
1017         data_set->input = input;
1018         data_set->priv = out;
1019         get_date(data_set, true, options.use_date);
1020 
1021         if(options.xml_file) {
1022             pe__set_working_set_flags(data_set, pe_flag_sanitized);
1023         }
1024         if (options.show_scores) {
1025             pe__set_working_set_flags(data_set, pe_flag_show_scores);
1026         }
1027         if (options.show_utilization) {
1028             pe__set_working_set_flags(data_set, pe_flag_show_utilization);
1029         }
1030         cluster_status(data_set);
1031     }
1032 
1033     if (options.input_file != NULL) {
1034         rc = write_xml_file(input, options.input_file, FALSE);
1035         if (rc < 0) {
1036             rc = pcmk_legacy2rc(rc);
1037             g_set_error(&error, PCMK__RC_ERROR, rc,
1038                         "Could not create '%s': %s", options.input_file, pcmk_rc_str(rc));
1039             goto done;
1040         }
1041     }
1042 
1043     if (options.process || options.simulate) {
1044         crm_time_t *local_date = NULL;
1045         pcmk__output_t *logger_out = NULL;
1046 
1047         if (pcmk_all_flags_set(data_set->flags, pe_flag_show_scores|pe_flag_show_utilization)) {
1048             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1049             out->begin_list(out, NULL, NULL, "Allocation Scores and Utilization Information");
1050             printed = pcmk_rc_ok;
1051         } else if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
1052             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1053             out->begin_list(out, NULL, NULL, "Allocation Scores");
1054             printed = pcmk_rc_ok;
1055         } else if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
1056             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1057             out->begin_list(out, NULL, NULL, "Utilization Information");
1058             printed = pcmk_rc_ok;
1059         } else {
1060             logger_out = pcmk__new_logger();
1061             if (logger_out == NULL) {
1062                 goto done;
1063             }
1064 
1065             data_set->priv = logger_out;
1066         }
1067 
1068         pcmk__schedule_actions(data_set, input, local_date);
1069 
1070         if (logger_out == NULL) {
1071             out->end_list(out);
1072         } else {
1073             logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
1074             pcmk__output_free(logger_out);
1075             data_set->priv = out;
1076         }
1077 
1078         input = NULL;           /* Don't try and free it twice */
1079 
1080         if (options.graph_file != NULL) {
1081             write_xml_file(data_set->graph, options.graph_file, FALSE);
1082         }
1083 
1084         if (options.dot_file != NULL) {
1085             if (!create_dotfile(data_set, options.dot_file, options.all_actions, &error)) {
1086                 goto done;
1087             }
1088         }
1089 
1090         if (!out->is_quiet(out)) {
1091             GList *gIter = NULL;
1092 
1093             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1094             out->begin_list(out, NULL, NULL, "Transition Summary");
1095 
1096             LogNodeActions(data_set);
1097             for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
1098                 pe_resource_t *rsc = (pe_resource_t *) gIter->data;
1099 
1100                 LogActions(rsc, data_set);
1101             }
1102 
1103             out->end_list(out);
1104             printed = pcmk_rc_ok;
1105         }
1106     }
1107 
1108     rc = pcmk_rc_ok;
1109 
1110     if (options.simulate) {
1111         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1112         if (run_simulation(data_set, global_cib, options.op_fail) != pcmk_rc_ok) {
1113             rc = pcmk_rc_error;
1114         }
1115 
1116         printed = pcmk_rc_ok;
1117 
1118         if (!out->is_quiet(out)) {
1119             get_date(data_set, true, options.use_date);
1120 
1121             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1122             out->begin_list(out, NULL, NULL, "Revised Cluster Status");
1123 
1124             if (options.show_scores) {
1125                 pe__set_working_set_flags(data_set, pe_flag_show_scores);
1126             }
1127             if (options.show_utilization) {
1128                 pe__set_working_set_flags(data_set, pe_flag_show_utilization);
1129             }
1130 
1131             cluster_status(data_set);
1132             print_cluster_status(data_set, 0);
1133 
1134             out->end_list(out);
1135         }
1136     }
1137 
1138   done:
1139     pcmk__output_and_clear_error(error, NULL);
1140 
1141     /* There sure is a lot to free in options. */
1142     free(options.dot_file);
1143     free(options.graph_file);
1144     g_free(options.input_file);
1145     g_list_free_full(options.node_up, g_free);
1146     g_list_free_full(options.node_down, g_free);
1147     g_list_free_full(options.node_fail, g_free);
1148     g_list_free_full(options.op_fail, g_free);
1149     g_list_free_full(options.op_inject, g_free);
1150     g_free(options.output_file);
1151     free(options.quorum);
1152     g_free(options.test_dir);
1153     g_list_free_full(options.ticket_grant, g_free);
1154     g_list_free_full(options.ticket_revoke, g_free);
1155     g_list_free_full(options.ticket_standby, g_free);
1156     g_list_free_full(options.ticket_activate, g_free);
1157     free(options.use_date);
1158     free(options.watchdog);
1159     free(options.xml_file);
1160 
1161     pcmk__free_arg_context(context);
1162     g_strfreev(processed_args);
1163 
1164     if (data_set) {
1165         pe_free_working_set(data_set);
1166     }
1167 
1168     if (global_cib) {
1169         global_cib->cmds->signoff(global_cib);
1170         cib_delete(global_cib);
1171     }
1172 
1173     fflush(stderr);
1174 
1175     if (temp_shadow) {
1176         unlink(temp_shadow);
1177         free(temp_shadow);
1178     }
1179 
1180     if (rc != pcmk_rc_ok) {
1181         exit_code = pcmk_rc2exitc(rc);
1182     }
1183 
1184     if (out != NULL) {
1185         out->finish(out, exit_code, true, NULL);
1186         pcmk__output_free(out);
1187     }
1188 
1189     crm_exit(exit_code);
1190 }

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