root/tools/crm_resource.c

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

DEFINITIONS

This source file includes following definitions.
  1. bye
  2. quit_main_loop
  3. resource_ipc_timeout
  4. controller_event_callback
  5. start_mainloop
  6. compare_id
  7. build_constraint_list
  8. validate_opt_list
  9. command_cb
  10. attr_set_type_cb
  11. cmdline_config_cb
  12. option_cb
  13. timeout_cb
  14. ban_or_move
  15. cleanup
  16. clear_constraints
  17. initialize_scheduler_data
  18. list_options
  19. refresh
  20. refresh_resource
  21. show_metadata
  22. validate_cmdline_config
  23. get_find_flags
  24. is_node_required
  25. is_resource_required
  26. is_cib_required
  27. is_controller_required
  28. is_scheduler_required
  29. accept_clone_instance
  30. build_arg_context
  31. main

   1 /*
   2  * Copyright 2004-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 
  12 #include <crm_resource.h>
  13 #include <crm/lrmd_internal.h>
  14 #include <crm/common/cmdline_internal.h>
  15 #include <crm/common/ipc_attrd_internal.h>
  16 #include <crm/common/lists_internal.h>
  17 #include <crm/common/output.h>
  18 #include <pacemaker-internal.h>
  19 
  20 #include <sys/param.h>
  21 #include <stdint.h>         // uint32_t
  22 #include <stdio.h>
  23 #include <sys/types.h>
  24 #include <unistd.h>
  25 #include <stdlib.h>
  26 #include <errno.h>
  27 #include <fcntl.h>
  28 #include <libgen.h>
  29 #include <time.h>
  30 
  31 #include <crm/crm.h>
  32 #include <crm/stonith-ng.h>
  33 #include <crm/common/ipc_controld.h>
  34 #include <crm/cib/internal.h>
  35 
  36 #define SUMMARY "crm_resource - perform tasks related to Pacemaker cluster resources"
  37 
  38 enum rsc_command {
  39     cmd_none = 0,           // No command option given (yet)
  40     cmd_ban,
  41     cmd_cleanup,
  42     cmd_clear,
  43     cmd_colocations,
  44     cmd_cts,
  45     cmd_delete,
  46     cmd_delete_param,
  47     cmd_digests,
  48     cmd_execute_agent,
  49     cmd_fail,
  50     cmd_get_param,
  51     cmd_list_active_ops,
  52     cmd_list_agents,
  53     cmd_list_all_ops,
  54     cmd_list_alternatives,
  55     cmd_list_instances,
  56     cmd_list_options,
  57     cmd_list_providers,
  58     cmd_list_resources,
  59     cmd_list_standards,
  60     cmd_locate,
  61     cmd_metadata,
  62     cmd_move,
  63     cmd_query_xml,
  64     cmd_query_xml_raw,
  65     cmd_refresh,
  66     cmd_restart,
  67     cmd_set_param,
  68     cmd_wait,
  69     cmd_why,
  70 };
  71 
  72 struct {
  73     enum rsc_command rsc_cmd;     // crm_resource command to perform
  74 
  75     // Command-line option values
  76     gchar *rsc_id;                // Value of --resource
  77     gchar *rsc_type;              // Value of --resource-type
  78     gboolean all;                 // --all was given
  79     gboolean force;               // --force was given
  80     gboolean clear_expired;       // --expired was given
  81     gboolean recursive;           // --recursive was given
  82     gboolean promoted_role_only;  // --promoted was given
  83     gchar *host_uname;            // Value of --node
  84     gchar *interval_spec;         // Value of --interval
  85     gchar *move_lifetime;         // Value of --lifetime
  86     gchar *operation;             // Value of --operation
  87     enum pcmk__opt_flags opt_list;  // Parsed from --list-options
  88     const char *attr_set_type;    // Instance, meta, utilization, or element attribute
  89     gchar *prop_id;               // --nvpair (attribute XML ID)
  90     char *prop_name;              // Attribute name
  91     gchar *prop_set;              // --set-name (attribute block XML ID)
  92     gchar *prop_value;            // --parameter-value (attribute value)
  93     guint timeout_ms;             // Parsed from --timeout value
  94     char *agent_spec;             // Standard and/or provider and/or agent
  95     int check_level;              // Optional value of --validate or --force-check
  96 
  97     // Resource configuration specified via command-line arguments
  98     bool cmdline_config;          // Resource configuration was via arguments
  99     char *v_agent;                // Value of --agent
 100     char *v_class;                // Value of --class
 101     char *v_provider;             // Value of --provider
 102     GHashTable *cmdline_params;   // Resource parameters specified
 103 
 104     // Positional command-line arguments
 105     gchar **remainder;            // Positional arguments as given
 106     GHashTable *override_params;  // Resource parameter values that override config
 107 } options = {
 108     .attr_set_type = PCMK_XE_INSTANCE_ATTRIBUTES,
 109     .check_level = -1,
 110     .rsc_cmd = cmd_list_resources,  // List all resources if no command given
 111 };
 112 
 113 gboolean attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
 114 gboolean cmdline_config_cb(const gchar *option_name, const gchar *optarg,
 115                            gpointer data, GError **error);
 116 gboolean option_cb(const gchar *option_name, const gchar *optarg,
 117                    gpointer data, GError **error);
 118 gboolean timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
 119 
 120 static crm_exit_t exit_code = CRM_EX_OK;
 121 static pcmk__output_t *out = NULL;
 122 static pcmk__common_args_t *args = NULL;
 123 
 124 // Things that should be cleaned up on exit
 125 static GError *error = NULL;
 126 static GMainLoop *mainloop = NULL;
 127 static cib_t *cib_conn = NULL;
 128 static pcmk_ipc_api_t *controld_api = NULL;
 129 static pcmk_scheduler_t *scheduler = NULL;
 130 
 131 #define MESSAGE_TIMEOUT_S 60
 132 
 133 #define INDENT "                                    "
 134 
 135 static pcmk__supported_format_t formats[] = {
 136     PCMK__SUPPORTED_FORMAT_NONE,
 137     PCMK__SUPPORTED_FORMAT_TEXT,
 138     PCMK__SUPPORTED_FORMAT_XML,
 139     { NULL, NULL, NULL }
 140 };
 141 
 142 // Clean up and exit
 143 static crm_exit_t
 144 bye(crm_exit_t ec)
     /* [previous][next][first][last][top][bottom][index][help] */
 145 {
 146     pcmk__output_and_clear_error(&error, out);
 147 
 148     if (out != NULL) {
 149         out->finish(out, ec, true, NULL);
 150         pcmk__output_free(out);
 151     }
 152     pcmk__unregister_formats();
 153 
 154     if (cib_conn != NULL) {
 155         cib_t *save_cib_conn = cib_conn;
 156 
 157         cib_conn = NULL; // Ensure we can't free this twice
 158         cib__clean_up_connection(&save_cib_conn);
 159     }
 160 
 161     if (controld_api != NULL) {
 162         pcmk_ipc_api_t *save_controld_api = controld_api;
 163 
 164         controld_api = NULL; // Ensure we can't free this twice
 165         pcmk_free_ipc_api(save_controld_api);
 166     }
 167 
 168     if (mainloop != NULL) {
 169         g_main_loop_unref(mainloop);
 170         mainloop = NULL;
 171     }
 172 
 173     pe_free_working_set(scheduler);
 174     scheduler = NULL;
 175     crm_exit(ec);
 176     return ec;
 177 }
 178 
 179 static void
 180 quit_main_loop(crm_exit_t ec)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182     exit_code = ec;
 183     if (mainloop != NULL) {
 184         GMainLoop *mloop = mainloop;
 185 
 186         mainloop = NULL; // Don't re-enter this block
 187         pcmk_quit_main_loop(mloop, 10);
 188         g_main_loop_unref(mloop);
 189     }
 190 }
 191 
 192 static gboolean
 193 resource_ipc_timeout(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 194 {
 195     // Start with newline because "Waiting for ..." message doesn't have one
 196     if (error != NULL) {
 197         g_clear_error(&error);
 198     }
 199 
 200     g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_TIMEOUT,
 201                 _("Aborting because no messages received in %d seconds"), MESSAGE_TIMEOUT_S);
 202 
 203     quit_main_loop(CRM_EX_TIMEOUT);
 204     return FALSE;
 205 }
 206 
 207 static void
 208 controller_event_callback(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type,
     /* [previous][next][first][last][top][bottom][index][help] */
 209                           crm_exit_t status, void *event_data, void *user_data)
 210 {
 211     switch (event_type) {
 212         case pcmk_ipc_event_disconnect:
 213             if (exit_code == CRM_EX_DISCONNECT) { // Unexpected
 214                 crm_info("Connection to controller was terminated");
 215             }
 216             quit_main_loop(exit_code);
 217             break;
 218 
 219         case pcmk_ipc_event_reply:
 220             if (status != CRM_EX_OK) {
 221                 out->err(out, "Error: bad reply from controller: %s",
 222                          crm_exit_str(status));
 223                 pcmk_disconnect_ipc(api);
 224                 quit_main_loop(status);
 225             } else {
 226                 if ((pcmk_controld_api_replies_expected(api) == 0)
 227                     && mainloop && g_main_loop_is_running(mainloop)) {
 228                     out->info(out, "... got reply (done)");
 229                     crm_debug("Got all the replies we expected");
 230                     pcmk_disconnect_ipc(api);
 231                     quit_main_loop(CRM_EX_OK);
 232                 } else {
 233                     out->info(out, "... got reply");
 234                 }
 235             }
 236             break;
 237 
 238         default:
 239             break;
 240     }
 241 }
 242 
 243 static void
 244 start_mainloop(pcmk_ipc_api_t *capi)
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246     unsigned int count = pcmk_controld_api_replies_expected(capi);
 247 
 248     if (count > 0) {
 249         out->info(out, "Waiting for %u %s from the controller",
 250                   count, pcmk__plural_alt(count, "reply", "replies"));
 251         exit_code = CRM_EX_DISCONNECT; // For unexpected disconnects
 252         mainloop = g_main_loop_new(NULL, FALSE);
 253         pcmk__create_timer(MESSAGE_TIMEOUT_S * 1000, resource_ipc_timeout, NULL);
 254         g_main_loop_run(mainloop);
 255     }
 256 }
 257 
 258 static int
 259 compare_id(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 260 {
 261     return strcmp((const char *)a, (const char *)b);
 262 }
 263 
 264 static GList *
 265 build_constraint_list(xmlNode *root)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267     GList *retval = NULL;
 268     xmlNode *cib_constraints = NULL;
 269     xmlXPathObjectPtr xpathObj = NULL;
 270     int ndx = 0;
 271 
 272     cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS);
 273     xpathObj = xpath_search(cib_constraints, "//" PCMK_XE_RSC_LOCATION);
 274 
 275     for (ndx = 0; ndx < numXpathResults(xpathObj); ndx++) {
 276         xmlNode *match = getXpathResult(xpathObj, ndx);
 277         retval = g_list_insert_sorted(retval, (gpointer) pcmk__xe_id(match),
 278                                       compare_id);
 279     }
 280 
 281     freeXpathObject(xpathObj);
 282     return retval;
 283 }
 284 
 285 static gboolean
 286 validate_opt_list(const gchar *optarg)
     /* [previous][next][first][last][top][bottom][index][help] */
 287 {
 288     if (pcmk__str_eq(optarg, PCMK_VALUE_FENCING, pcmk__str_none)) {
 289         options.opt_list = pcmk__opt_fencing;
 290 
 291     } else if (pcmk__str_eq(optarg, PCMK__VALUE_PRIMITIVE, pcmk__str_none)) {
 292         options.opt_list = pcmk__opt_primitive;
 293 
 294     } else {
 295         return FALSE;
 296     }
 297 
 298     return TRUE;
 299 }
 300 
 301 /*!
 302  * \internal
 303  * \brief Process options that set the command
 304  *
 305  * Nothing else should set \c options.rsc_cmd.
 306  *
 307  * \param[in]  option_name  Name of the option being parsed
 308  * \param[in]  optarg       Value to be parsed
 309  * \param[in]  data         Ignored
 310  * \param[out] error        Where to store recoverable error, if any
 311  *
 312  * \return \c TRUE if the option was successfully parsed, or \c FALSE if an
 313  *         error occurred, in which case \p *error is set
 314  */
 315 static gboolean
 316 command_cb(const gchar *option_name, const gchar *optarg, gpointer data,
     /* [previous][next][first][last][top][bottom][index][help] */
 317            GError **error)
 318 {
 319     // Sorted by enum rsc_command name
 320     if (pcmk__str_any_of(option_name, "-B", "--ban", NULL)) {
 321         options.rsc_cmd = cmd_ban;
 322 
 323     } else if (pcmk__str_any_of(option_name, "-C", "--cleanup", NULL)) {
 324         options.rsc_cmd = cmd_cleanup;
 325 
 326     } else if (pcmk__str_any_of(option_name, "-U", "--clear", NULL)) {
 327         options.rsc_cmd = cmd_clear;
 328 
 329     } else if (pcmk__str_any_of(option_name, "-a", "--constraints", NULL)) {
 330         options.rsc_cmd = cmd_colocations;
 331 
 332     } else if (pcmk__str_any_of(option_name, "-A", "--stack", NULL)) {
 333         options.rsc_cmd = cmd_colocations;
 334         options.recursive = TRUE;
 335 
 336     } else if (pcmk__str_any_of(option_name, "-c", "--list-cts", NULL)) {
 337         options.rsc_cmd = cmd_cts;
 338 
 339     } else if (pcmk__str_any_of(option_name, "-D", "--delete", NULL)) {
 340         options.rsc_cmd = cmd_delete;
 341 
 342     } else if (pcmk__str_any_of(option_name, "-d", "--delete-parameter",
 343                                 NULL)) {
 344         options.rsc_cmd = cmd_delete_param;
 345         pcmk__str_update(&options.prop_name, optarg);
 346 
 347     } else if (pcmk__str_eq(option_name, "--digests", pcmk__str_none)) {
 348         options.rsc_cmd = cmd_digests;
 349 
 350         if (options.override_params == NULL) {
 351             options.override_params = pcmk__strkey_table(free, free);
 352         }
 353 
 354     } else if (pcmk__str_any_of(option_name,
 355                                 "--force-demote", "--force-promote",
 356                                 "--force-start", "--force-stop",
 357                                 "--force-check", "--validate", NULL)) {
 358         options.rsc_cmd = cmd_execute_agent;
 359 
 360         g_free(options.operation);
 361         options.operation = g_strdup(option_name + 2);  // skip "--"
 362 
 363         if (options.override_params == NULL) {
 364             options.override_params = pcmk__strkey_table(free, free);
 365         }
 366 
 367         if (optarg != NULL) {
 368             if (pcmk__scan_min_int(optarg, &options.check_level,
 369                                    0) != pcmk_rc_ok) {
 370                 g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
 371                             _("Invalid check level setting: %s"), optarg);
 372                 return FALSE;
 373             }
 374         }
 375 
 376     } else if (pcmk__str_any_of(option_name, "-F", "--fail", NULL)) {
 377         options.rsc_cmd = cmd_fail;
 378 
 379     } else if (pcmk__str_any_of(option_name, "-g", "--get-parameter", NULL)) {
 380         options.rsc_cmd = cmd_get_param;
 381         pcmk__str_update(&options.prop_name, optarg);
 382 
 383     } else if (pcmk__str_any_of(option_name, "-O", "--list-operations", NULL)) {
 384         options.rsc_cmd = cmd_list_active_ops;
 385 
 386     } else if (pcmk__str_eq(option_name, "--list-agents", pcmk__str_none)) {
 387         options.rsc_cmd = cmd_list_agents;
 388         pcmk__str_update(&options.agent_spec, optarg);
 389 
 390     } else if (pcmk__str_any_of(option_name, "-o", "--list-all-operations",
 391                                 NULL)) {
 392         options.rsc_cmd = cmd_list_all_ops;
 393 
 394     } else if (pcmk__str_eq(option_name, "--list-ocf-alternatives",
 395                             pcmk__str_none)) {
 396         options.rsc_cmd = cmd_list_alternatives;
 397         pcmk__str_update(&options.agent_spec, optarg);
 398 
 399     } else if (pcmk__str_eq(option_name, "--list-options", pcmk__str_none)) {
 400         options.rsc_cmd = cmd_list_options;
 401         return validate_opt_list(optarg);
 402 
 403     } else if (pcmk__str_any_of(option_name, "-l", "--list-raw", NULL)) {
 404         options.rsc_cmd = cmd_list_instances;
 405 
 406     } else if (pcmk__str_eq(option_name, "--list-ocf-providers",
 407                             pcmk__str_none)) {
 408         options.rsc_cmd = cmd_list_providers;
 409         pcmk__str_update(&options.agent_spec, optarg);
 410 
 411     } else if (pcmk__str_any_of(option_name, "-L", "--list", NULL)) {
 412         options.rsc_cmd = cmd_list_resources;
 413 
 414     } else if (pcmk__str_eq(option_name, "--list-standards", pcmk__str_none)) {
 415         options.rsc_cmd = cmd_list_standards;
 416 
 417     } else if (pcmk__str_any_of(option_name, "-W", "--locate", NULL)) {
 418         options.rsc_cmd = cmd_locate;
 419 
 420     } else if (pcmk__str_eq(option_name, "--show-metadata", pcmk__str_none)) {
 421         options.rsc_cmd = cmd_metadata;
 422         pcmk__str_update(&options.agent_spec, optarg);
 423 
 424     } else if (pcmk__str_any_of(option_name, "-M", "--move", NULL)) {
 425         options.rsc_cmd = cmd_move;
 426 
 427     } else if (pcmk__str_any_of(option_name, "-q", "--query-xml", NULL)) {
 428         options.rsc_cmd = cmd_query_xml;
 429 
 430     } else if (pcmk__str_any_of(option_name, "-w", "--query-xml-raw", NULL)) {
 431         options.rsc_cmd = cmd_query_xml_raw;
 432 
 433     } else if (pcmk__str_any_of(option_name, "-R", "--refresh", NULL)) {
 434         options.rsc_cmd = cmd_refresh;
 435 
 436     } else if (pcmk__str_eq(option_name, "--restart", pcmk__str_none)) {
 437         options.rsc_cmd = cmd_restart;
 438 
 439     } else if (pcmk__str_any_of(option_name, "-p", "--set-parameter", NULL)) {
 440         options.rsc_cmd = cmd_set_param;
 441         pcmk__str_update(&options.prop_name, optarg);
 442 
 443     } else if (pcmk__str_eq(option_name, "--wait", pcmk__str_none)) {
 444         options.rsc_cmd = cmd_wait;
 445 
 446     } else if (pcmk__str_any_of(option_name, "-Y", "--why", NULL)) {
 447         options.rsc_cmd = cmd_why;
 448     }
 449 
 450     return TRUE;
 451 }
 452 
 453 /* short option letters still available: eEJkKXyYZ */
 454 
 455 static GOptionEntry query_entries[] = {
 456     { "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 457       "List all cluster resources with status",
 458       NULL },
 459     { "list-raw", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 460       "List IDs of all instantiated resources (individual members\n"
 461       INDENT "rather than groups etc.)",
 462       NULL },
 463     { "list-cts", 'c', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG,
 464           G_OPTION_ARG_CALLBACK, command_cb,
 465       NULL,
 466       NULL },
 467     { "list-operations", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 468           command_cb,
 469       "List active resource operations, optionally filtered by\n"
 470       INDENT "--resource and/or --node",
 471       NULL },
 472     { "list-all-operations", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 473           command_cb,
 474       "List all resource operations, optionally filtered by\n"
 475       INDENT "--resource and/or --node",
 476       NULL },
 477     { "list-options", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb,
 478       "List all available options of the given type.\n"
 479       INDENT "Allowed values:\n"
 480       INDENT PCMK__VALUE_PRIMITIVE " (primitive resource meta-attributes),\n"
 481       INDENT PCMK_VALUE_FENCING " (parameters common to all fencing resources)",
 482       "TYPE" },
 483     { "list-standards", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 484           command_cb,
 485       "List supported standards",
 486       NULL },
 487     { "list-ocf-providers", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 488           command_cb,
 489       "List all available OCF providers",
 490       NULL },
 491     { "list-agents", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
 492           command_cb,
 493       "List all agents available for the named standard and/or provider",
 494       "STD:PROV" },
 495     { "list-ocf-alternatives", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
 496           command_cb,
 497       "List all available providers for the named OCF agent",
 498       "AGENT" },
 499     { "show-metadata", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb,
 500       "Show the metadata for the named class:provider:agent",
 501       "SPEC" },
 502     { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 503       "Show XML configuration of resource (after any template expansion)",
 504       NULL },
 505     { "query-xml-raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 506           command_cb,
 507       "Show XML configuration of resource (before any template expansion)",
 508       NULL },
 509     { "get-parameter", 'g', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
 510           command_cb,
 511       "Display named parameter for resource (use instance attribute\n"
 512       INDENT "unless --element, --meta, or --utilization is specified)",
 513       "PARAM" },
 514     { "locate", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 515       "Show node(s) currently running resource",
 516       NULL },
 517     { "constraints", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 518           command_cb,
 519       "Display the location and colocation constraints that apply to a\n"
 520       INDENT "resource, and if --recursive is specified, to the resources\n"
 521       INDENT "directly or indirectly involved in those colocations.\n"
 522       INDENT "If the named resource is part of a group, or a clone or\n"
 523       INDENT "bundle instance, constraints for the collective resource\n"
 524       INDENT "will be shown unless --force is given.",
 525       NULL },
 526     { "stack", 'A', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 527       "Equivalent to --constraints --recursive",
 528       NULL },
 529     { "why", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 530       "Show why resources are not running, optionally filtered by\n"
 531       INDENT "--resource and/or --node",
 532       NULL },
 533 
 534     { NULL }
 535 };
 536 
 537 static GOptionEntry command_entries[] = {
 538     { "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
 539           command_cb,
 540       "Validate resource configuration by calling agent's validate-all\n"
 541       INDENT "action. The configuration may be specified either by giving an\n"
 542       INDENT "existing resource name with -r, or by specifying --class,\n"
 543       INDENT "--agent, and --provider arguments, along with any number of\n"
 544       INDENT "--option arguments. An optional LEVEL argument can be given\n"
 545       INDENT "to control the level of checking performed.",
 546       "LEVEL" },
 547     { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 548       "If resource has any past failures, clear its history and fail\n"
 549       INDENT "count. Optionally filtered by --resource, --node, --operation\n"
 550       INDENT "and --interval (otherwise all). --operation and --interval\n"
 551       INDENT "apply to fail counts, but entire history is always clear, to\n"
 552       INDENT "allow current state to be rechecked. If the named resource is\n"
 553       INDENT "part of a group, or one numbered instance of a clone or bundled\n"
 554       INDENT "resource, the clean-up applies to the whole collective resource\n"
 555       INDENT "unless --force is given.",
 556       NULL },
 557     { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 558       "Delete resource's history (including failures) so its current state\n"
 559       INDENT "is rechecked. Optionally filtered by --resource and --node\n"
 560       INDENT "(otherwise all). If the named resource is part of a group, or one\n"
 561       INDENT "numbered instance of a clone or bundled resource, the refresh\n"
 562       INDENT "applies to the whole collective resource unless --force is given.",
 563       NULL },
 564     { "set-parameter", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
 565           command_cb,
 566       "Set named parameter for resource (requires -v). Use instance\n"
 567       INDENT "attribute unless --element, --meta, or --utilization is "
 568       "specified.",
 569       "PARAM" },
 570     { "delete-parameter", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
 571           command_cb,
 572       "Delete named parameter for resource. Use instance attribute\n"
 573       INDENT "unless --element, --meta or, --utilization is specified.",
 574       "PARAM" },
 575 
 576     { NULL }
 577 };
 578 
 579 static GOptionEntry location_entries[] = {
 580     { "move", 'M', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 581       "Create a constraint to move resource. If --node is specified,\n"
 582       INDENT "the constraint will be to move to that node, otherwise it\n"
 583       INDENT "will be to ban the current node. Unless --force is specified\n"
 584       INDENT "this will return an error if the resource is already running\n"
 585       INDENT "on the specified node. If --force is specified, this will\n"
 586       INDENT "always ban the current node.\n"
 587       INDENT "Optional: --lifetime, --promoted. NOTE: This may prevent the\n"
 588       INDENT "resource from running on its previous location until the\n"
 589       INDENT "implicit constraint expires or is removed with --clear.",
 590       NULL },
 591     { "ban", 'B', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 592       "Create a constraint to keep resource off a node.\n"
 593       INDENT "Optional: --node, --lifetime, --promoted.\n"
 594       INDENT "NOTE: This will prevent the resource from running on the\n"
 595       INDENT "affected node until the implicit constraint expires or is\n"
 596       INDENT "removed with --clear. If --node is not specified, it defaults\n"
 597       INDENT "to the node currently running the resource for primitives\n"
 598       INDENT "and groups, or the promoted instance of promotable clones with\n"
 599       INDENT PCMK_META_PROMOTED_MAX "=1 (all other situations result in an\n"
 600       INDENT "error as there is no sane default).",
 601       NULL },
 602     { "clear", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 603       "Remove all constraints created by the --ban and/or --move\n"
 604       INDENT "commands. Requires: --resource. Optional: --node, --promoted,\n"
 605       INDENT "--expired. If --node is not specified, all constraints created\n"
 606       INDENT "by --ban and --move will be removed for the named resource. If\n"
 607       INDENT "--node and --force are specified, any constraint created by\n"
 608       INDENT "--move will be cleared, even if it is not for the specified\n"
 609       INDENT "node. If --expired is specified, only those constraints whose\n"
 610       INDENT "lifetimes have expired will be removed.",
 611       NULL },
 612     { "expired", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
 613           &options.clear_expired,
 614       "Modifies the --clear argument to remove constraints with\n"
 615       INDENT "expired lifetimes.",
 616       NULL },
 617     { "lifetime", 'u', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.move_lifetime,
 618       "Lifespan (as ISO 8601 duration) of created constraints (with\n"
 619       INDENT "-B, -M) see https://en.wikipedia.org/wiki/ISO_8601#Durations)",
 620       "TIMESPEC" },
 621     { "promoted", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
 622       &options.promoted_role_only,
 623       "Limit scope of command to promoted role (with -B, -M, -U). For\n"
 624       INDENT "-B and -M, previously promoted instances may remain\n"
 625       INDENT "active in the unpromoted role.",
 626       NULL },
 627 
 628     // Deprecated since 2.1.0
 629     { "master", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
 630       &options.promoted_role_only,
 631       "Deprecated: Use --promoted instead", NULL },
 632 
 633     { NULL }
 634 };
 635 
 636 static GOptionEntry advanced_entries[] = {
 637     { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 638       "(Advanced) Delete a resource from the CIB. Required: -t",
 639       NULL },
 640     { "fail", 'F', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 641       "(Advanced) Tell the cluster this resource has failed",
 642       NULL },
 643     { "restart", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 644       "(Advanced) Tell the cluster to restart this resource and\n"
 645       INDENT "anything that depends on it",
 646       NULL },
 647     { "wait", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 648       "(Advanced) Wait until the cluster settles into a stable state",
 649       NULL },
 650     { "digests", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 651       "(Advanced) Show parameter hashes that Pacemaker uses to detect\n"
 652       INDENT "configuration changes (only accurate if there is resource\n"
 653       INDENT "history on the specified node). Required: --resource, --node.\n"
 654       INDENT "Optional: any NAME=VALUE parameters will be used to override\n"
 655       INDENT "the configuration (to see what the hash would be with those\n"
 656       INDENT "changes).",
 657       NULL },
 658     { "force-demote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 659           command_cb,
 660       "(Advanced) Bypass the cluster and demote a resource on the local\n"
 661       INDENT "node. Unless --force is specified, this will refuse to do so if\n"
 662       INDENT "the cluster believes the resource is a clone instance already\n"
 663       INDENT "running on the local node.",
 664       NULL },
 665     { "force-stop", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 666       "(Advanced) Bypass the cluster and stop a resource on the local node",
 667       NULL },
 668     { "force-start", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
 669       "(Advanced) Bypass the cluster and start a resource on the local\n"
 670       INDENT "node. Unless --force is specified, this will refuse to do so if\n"
 671       INDENT "the cluster believes the resource is a clone instance already\n"
 672       INDENT "running on the local node.",
 673       NULL },
 674     { "force-promote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 675           command_cb,
 676       "(Advanced) Bypass the cluster and promote a resource on the local\n"
 677       INDENT "node. Unless --force is specified, this will refuse to do so if\n"
 678       INDENT "the cluster believes the resource is a clone instance already\n"
 679       INDENT "running on the local node.",
 680       NULL },
 681     { "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
 682           command_cb,
 683       "(Advanced) Bypass the cluster and check the state of a resource on\n"
 684       INDENT "the local node. An optional LEVEL argument can be given\n"
 685       INDENT "to control the level of checking performed.",
 686       "LEVEL" },
 687 
 688     { NULL }
 689 };
 690 
 691 static GOptionEntry addl_entries[] = {
 692     { "node", 'N', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.host_uname,
 693       "Node name",
 694       "NAME" },
 695     { "recursive", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.recursive,
 696       "Follow colocation chains when using --set-parameter or --constraints",
 697       NULL },
 698     { "resource-type", 't', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.rsc_type,
 699       "Resource XML element (primitive, group, etc.) (with -D)",
 700       "ELEMENT" },
 701     { "parameter-value", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.prop_value,
 702       "Value to use with -p",
 703       "PARAM" },
 704     { "meta", 'm', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
 705       "Use resource meta-attribute instead of instance attribute\n"
 706       INDENT "(with -p, -g, -d)",
 707       NULL },
 708     { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
 709       "Use resource utilization attribute instead of instance attribute\n"
 710       INDENT "(with -p, -g, -d)",
 711       NULL },
 712     { "element", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
 713       "Use resource element attribute instead of instance attribute\n"
 714       INDENT "(with -p, -g, -d)",
 715       NULL },
 716     { "operation", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.operation,
 717       "Operation to clear instead of all (with -C -r)",
 718       "OPERATION" },
 719     { "interval", 'I', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.interval_spec,
 720       "Interval of operation to clear (default 0) (with -C -r -n)",
 721       "N" },
 722     { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, cmdline_config_cb,
 723       "The standard the resource agent conforms to (for example, ocf).\n"
 724       INDENT "Use with --agent, --provider, --option, and --validate.",
 725       "CLASS" },
 726     { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, cmdline_config_cb,
 727       "The agent to use (for example, IPaddr). Use with --class,\n"
 728       INDENT "--provider, --option, and --validate.",
 729       "AGENT" },
 730     { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
 731           cmdline_config_cb,
 732       "The vendor that supplies the resource agent (for example,\n"
 733       INDENT "heartbeat). Use with --class, --agent, --option, and --validate.",
 734       "PROVIDER" },
 735     { "option", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, option_cb,
 736       "Specify a device configuration parameter as NAME=VALUE (may be\n"
 737       INDENT "specified multiple times). Use with --validate and without the\n"
 738       INDENT "-r option.",
 739       "PARAM" },
 740     { "set-name", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.prop_set,
 741       "(Advanced) XML ID of attributes element to use (with -p, -d)",
 742       "ID" },
 743     { "nvpair", 'i', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.prop_id,
 744       "(Advanced) XML ID of nvpair element to use (with -p, -d)",
 745       "ID" },
 746     { "timeout", 'T', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, timeout_cb,
 747       "(Advanced) Abort if command does not finish in this time (with\n"
 748       INDENT "--restart, --wait, --force-*)",
 749       "N" },
 750     { "all", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all,
 751       "List all options, including advanced and deprecated (with\n"
 752       INDENT "--list-options)",
 753       NULL },
 754     { "force", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.force,
 755       "Force the action to be performed. See help for individual commands for\n"
 756       INDENT "additional behavior.",
 757       NULL },
 758 
 759     // @COMPAT Used in resource-agents prior to v4.2.0
 760     { "host-uname", 'H', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.host_uname,
 761       NULL,
 762       "HOST" },
 763 
 764     { NULL }
 765 };
 766 
 767 gboolean
 768 attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 769     if (pcmk__str_any_of(option_name, "-m", "--meta", NULL)) {
 770         options.attr_set_type = PCMK_XE_META_ATTRIBUTES;
 771     } else if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) {
 772         options.attr_set_type = PCMK_XE_UTILIZATION;
 773     } else if (pcmk__str_eq(option_name, "--element", pcmk__str_none)) {
 774         options.attr_set_type = ATTR_SET_ELEMENT;
 775     }
 776     return TRUE;
 777 }
 778 
 779 gboolean
 780 cmdline_config_cb(const gchar *option_name, const gchar *optarg, gpointer data,
     /* [previous][next][first][last][top][bottom][index][help] */
 781                   GError **error)
 782 {
 783     options.cmdline_config = true;
 784 
 785     if (pcmk__str_eq(option_name, "--class", pcmk__str_none)) {
 786         pcmk__str_update(&options.v_class, optarg);
 787 
 788     } else if (pcmk__str_eq(option_name, "--provider", pcmk__str_none)) {
 789         pcmk__str_update(&options.v_provider, optarg);
 790 
 791     } else {    // --agent
 792         pcmk__str_update(&options.v_agent, optarg);
 793     }
 794     return TRUE;
 795 }
 796 
 797 gboolean
 798 option_cb(const gchar *option_name, const gchar *optarg, gpointer data,
     /* [previous][next][first][last][top][bottom][index][help] */
 799           GError **error)
 800 {
 801     char *name = NULL;
 802     char *value = NULL;
 803 
 804     if (pcmk__scan_nvpair(optarg, &name, &value) != 2) {
 805         return FALSE;
 806     }
 807     if (options.cmdline_params == NULL) {
 808         options.cmdline_params = pcmk__strkey_table(free, free);
 809     }
 810     g_hash_table_replace(options.cmdline_params, name, value);
 811     return TRUE;
 812 }
 813 
 814 gboolean
 815 timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 816     long long timeout_ms = crm_get_msec(optarg);
 817 
 818     if (timeout_ms < 0) {
 819         return FALSE;
 820     }
 821     options.timeout_ms = (guint) QB_MIN(timeout_ms, UINT_MAX);
 822     return TRUE;
 823 }
 824 
 825 static int
 826 ban_or_move(pcmk__output_t *out, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 827             const char *move_lifetime)
 828 {
 829     int rc = pcmk_rc_ok;
 830     pcmk_node_t *current = NULL;
 831     unsigned int nactive = 0;
 832 
 833     CRM_CHECK(rsc != NULL, return EINVAL);
 834 
 835     current = pe__find_active_requires(rsc, &nactive);
 836 
 837     if (nactive == 1) {
 838         rc = cli_resource_ban(out, options.rsc_id, current->priv->name,
 839                               move_lifetime, cib_conn,
 840                               options.promoted_role_only, PCMK_ROLE_PROMOTED);
 841 
 842     } else if (pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) {
 843         int count = 0;
 844         GList *iter = NULL;
 845 
 846         current = NULL;
 847         for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
 848             pcmk_resource_t *child = (pcmk_resource_t *)iter->data;
 849             enum rsc_role_e child_role = child->priv->fns->state(child, TRUE);
 850 
 851             if (child_role == pcmk_role_promoted) {
 852                 count++;
 853                 current = pcmk__current_node(child);
 854             }
 855         }
 856 
 857         if(count == 1 && current) {
 858             rc = cli_resource_ban(out, options.rsc_id, current->priv->name,
 859                                   move_lifetime, cib_conn,
 860                                   options.promoted_role_only,
 861                                   PCMK_ROLE_PROMOTED);
 862 
 863         } else {
 864             rc = EINVAL;
 865             g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
 866                         _("Resource '%s' not moved: active in %d locations (promoted in %d).\n"
 867                         "To prevent '%s' from running on a specific location, "
 868                         "specify a node."
 869                         "To prevent '%s' from being promoted at a specific "
 870                         "location, specify a node and the --promoted option."),
 871                         options.rsc_id, nactive, count, options.rsc_id, options.rsc_id);
 872         }
 873 
 874     } else {
 875         rc = EINVAL;
 876         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
 877                     _("Resource '%s' not moved: active in %d locations.\n"
 878                     "To prevent '%s' from running on a specific location, "
 879                     "specify a node."),
 880                     options.rsc_id, nactive, options.rsc_id);
 881     }
 882 
 883     return rc;
 884 }
 885 
 886 static void
 887 cleanup(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 888 {
 889     int rc = pcmk_rc_ok;
 890 
 891     if (options.force == FALSE) {
 892         rsc = uber_parent(rsc);
 893     }
 894 
 895     crm_debug("Erasing failures of %s (%s requested) on %s",
 896               rsc->id, options.rsc_id, (options.host_uname? options.host_uname: "all nodes"));
 897     rc = cli_resource_delete(controld_api, options.host_uname, rsc,
 898                              options.operation, options.interval_spec, TRUE,
 899                              scheduler, options.force);
 900 
 901     if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
 902         // Show any reasons why resource might stay stopped
 903         cli_resource_check(out, rsc, node);
 904     }
 905 
 906     if (rc == pcmk_rc_ok) {
 907         start_mainloop(controld_api);
 908     }
 909 }
 910 
 911 static int
 912 clear_constraints(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 913 {
 914     GList *before = NULL;
 915     GList *after = NULL;
 916     GList *remaining = NULL;
 917     GList *ele = NULL;
 918     pcmk_node_t *dest = NULL;
 919     int rc = pcmk_rc_ok;
 920 
 921     if (!out->is_quiet(out)) {
 922         before = build_constraint_list(scheduler->input);
 923     }
 924 
 925     if (options.clear_expired) {
 926         rc = cli_resource_clear_all_expired(scheduler->input, cib_conn,
 927                                             options.rsc_id, options.host_uname,
 928                                             options.promoted_role_only);
 929 
 930     } else if (options.host_uname) {
 931         dest = pcmk_find_node(scheduler, options.host_uname);
 932         if (dest == NULL) {
 933             rc = pcmk_rc_node_unknown;
 934             if (!out->is_quiet(out)) {
 935                 g_list_free(before);
 936             }
 937             return rc;
 938         }
 939         rc = cli_resource_clear(options.rsc_id, dest->priv->name, NULL,
 940                                 cib_conn, true, options.force);
 941 
 942     } else {
 943         rc = cli_resource_clear(options.rsc_id, NULL, scheduler->nodes,
 944                                 cib_conn, true, options.force);
 945     }
 946 
 947     if (!out->is_quiet(out)) {
 948         xmlNode *cib_xml = NULL;
 949 
 950         rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml, cib_sync_call);
 951         rc = pcmk_legacy2rc(rc);
 952 
 953         if (rc != pcmk_rc_ok) {
 954             g_set_error(&error, PCMK__RC_ERROR, rc,
 955                         _("Could not get modified CIB: %s\n"), pcmk_rc_str(rc));
 956             g_list_free(before);
 957             pcmk__xml_free(cib_xml);
 958             return rc;
 959         }
 960 
 961         scheduler->input = cib_xml;
 962         cluster_status(scheduler);
 963 
 964         after = build_constraint_list(scheduler->input);
 965         remaining = pcmk__subtract_lists(before, after, (GCompareFunc) strcmp);
 966 
 967         for (ele = remaining; ele != NULL; ele = ele->next) {
 968             out->info(out, "Removing constraint: %s", (char *) ele->data);
 969         }
 970 
 971         g_list_free(before);
 972         g_list_free(after);
 973         g_list_free(remaining);
 974     }
 975 
 976     return rc;
 977 }
 978 
 979 static int
 980 initialize_scheduler_data(xmlNode **cib_xml_orig)
     /* [previous][next][first][last][top][bottom][index][help] */
 981 {
 982     int rc = pcmk_rc_ok;
 983 
 984     scheduler = pe_new_working_set();
 985     if (scheduler == NULL) {
 986         return ENOMEM;
 987     }
 988 
 989     pcmk__set_scheduler_flags(scheduler, pcmk__sched_no_counts);
 990     scheduler->priv->out = out;
 991     rc = update_scheduler_input(out, scheduler, cib_conn, cib_xml_orig);
 992     if (rc != pcmk_rc_ok) {
 993         return rc;
 994     }
 995 
 996     cluster_status(scheduler);
 997     return pcmk_rc_ok;
 998 }
 999 
1000 static void
1001 list_options(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1002 {
1003     switch (options.opt_list) {
1004         case pcmk__opt_fencing:
1005             exit_code = pcmk_rc2exitc(pcmk__list_fencing_params(out,
1006                                                                 options.all));
1007             break;
1008         case pcmk__opt_primitive:
1009             exit_code = pcmk_rc2exitc(pcmk__list_primitive_meta(out,
1010                                                                 options.all));
1011             break;
1012         default:
1013             exit_code = CRM_EX_SOFTWARE;
1014             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1015                         "BUG: Invalid option list type");
1016             break;
1017     }
1018 }
1019 
1020 static int
1021 refresh(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
1022 {
1023     int rc = pcmk_rc_ok;
1024     const char *router_node = options.host_uname;
1025     int attr_options = pcmk__node_attr_none;
1026 
1027     if (options.host_uname) {
1028         pcmk_node_t *node = pcmk_find_node(scheduler, options.host_uname);
1029 
1030         if (pcmk__is_pacemaker_remote_node(node)) {
1031             node = pcmk__current_node(node->priv->remote);
1032             if (node == NULL) {
1033                 rc = ENXIO;
1034                 g_set_error(&error, PCMK__RC_ERROR, rc,
1035                             _("No cluster connection to Pacemaker Remote node %s detected"),
1036                             options.host_uname);
1037                 return rc;
1038             }
1039             router_node = node->priv->name;
1040             attr_options |= pcmk__node_attr_remote;
1041         }
1042     }
1043 
1044     if (controld_api == NULL) {
1045         out->info(out, "Dry run: skipping clean-up of %s due to CIB_file",
1046                   options.host_uname? options.host_uname : "all nodes");
1047         rc = pcmk_rc_ok;
1048         return rc;
1049     }
1050 
1051     crm_debug("Re-checking the state of all resources on %s", options.host_uname?options.host_uname:"all nodes");
1052 
1053     rc = pcmk__attrd_api_clear_failures(NULL, options.host_uname, NULL,
1054                                         NULL, NULL, NULL, attr_options);
1055 
1056     if (pcmk_controld_api_reprobe(controld_api, options.host_uname,
1057                                   router_node) == pcmk_rc_ok) {
1058         start_mainloop(controld_api);
1059     }
1060 
1061     return rc;
1062 }
1063 
1064 static void
1065 refresh_resource(pcmk__output_t *out, pcmk_resource_t *rsc, pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1066 {
1067     int rc = pcmk_rc_ok;
1068 
1069     if (options.force == FALSE) {
1070         rsc = uber_parent(rsc);
1071     }
1072 
1073     crm_debug("Re-checking the state of %s (%s requested) on %s",
1074               rsc->id, options.rsc_id, (options.host_uname? options.host_uname: "all nodes"));
1075     rc = cli_resource_delete(controld_api, options.host_uname, rsc, NULL, 0,
1076                              FALSE, scheduler, options.force);
1077 
1078     if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
1079         // Show any reasons why resource might stay stopped
1080         cli_resource_check(out, rsc, node);
1081     }
1082 
1083     if (rc == pcmk_rc_ok) {
1084         start_mainloop(controld_api);
1085     }
1086 }
1087 
1088 static int
1089 show_metadata(pcmk__output_t *out, const char *agent_spec)
     /* [previous][next][first][last][top][bottom][index][help] */
1090 {
1091     int rc = pcmk_rc_ok;
1092     char *standard = NULL;
1093     char *provider = NULL;
1094     char *type = NULL;
1095     char *metadata = NULL;
1096     lrmd_t *lrmd_conn = NULL;
1097 
1098     rc = lrmd__new(&lrmd_conn, NULL, NULL, 0);
1099     if (rc != pcmk_rc_ok) {
1100         g_set_error(&error, PCMK__RC_ERROR, rc,
1101                     _("Could not create executor connection"));
1102         lrmd_api_delete(lrmd_conn);
1103         return rc;
1104     }
1105 
1106     rc = crm_parse_agent_spec(agent_spec, &standard, &provider, &type);
1107     rc = pcmk_legacy2rc(rc);
1108 
1109     if (rc == pcmk_rc_ok) {
1110         rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard,
1111                                            provider, type,
1112                                            &metadata, 0);
1113         rc = pcmk_legacy2rc(rc);
1114 
1115         if (metadata) {
1116             out->output_xml(out, PCMK_XE_METADATA, metadata);
1117             free(metadata);
1118         } else {
1119             /* We were given a validly formatted spec, but it doesn't necessarily
1120              * match up with anything that exists.  Use ENXIO as the return code
1121              * here because that maps to an exit code of CRM_EX_NOSUCH, which
1122              * probably is the most common reason to get here.
1123              */
1124             rc = ENXIO;
1125             g_set_error(&error, PCMK__RC_ERROR, rc,
1126                         _("Metadata query for %s failed: %s"),
1127                         agent_spec, pcmk_rc_str(rc));
1128         }
1129     } else {
1130         rc = ENXIO;
1131         g_set_error(&error, PCMK__RC_ERROR, rc,
1132                     _("'%s' is not a valid agent specification"), agent_spec);
1133     }
1134 
1135     lrmd_api_delete(lrmd_conn);
1136     return rc;
1137 }
1138 
1139 static void
1140 validate_cmdline_config(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1141 {
1142     // Cannot use both --resource and command-line resource configuration
1143     if (options.rsc_id != NULL) {
1144         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1145                     _("--resource cannot be used with --class, --agent, and --provider"));
1146 
1147     // Not all commands support command-line resource configuration
1148     } else if (options.rsc_cmd != cmd_execute_agent) {
1149         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1150                     _("--class, --agent, and --provider can only be used with "
1151                     "--validate and --force-*"));
1152 
1153     // Not all of --class, --agent, and --provider need to be given.  Not all
1154     // classes support the concept of a provider.  Check that what we were given
1155     // is valid.
1156     } else if (pcmk__str_eq(options.v_class, "stonith", pcmk__str_none)) {
1157         if (options.v_provider != NULL) {
1158             g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1159                         _("stonith does not support providers"));
1160 
1161         } else if (stonith_agent_exists(options.v_agent, 0) == FALSE) {
1162             g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1163                         _("%s is not a known stonith agent"), options.v_agent ? options.v_agent : "");
1164         }
1165 
1166     } else if (resources_agent_exists(options.v_class, options.v_provider, options.v_agent) == FALSE) {
1167         g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1168                     _("%s:%s:%s is not a known resource"),
1169                     options.v_class ? options.v_class : "",
1170                     options.v_provider ? options.v_provider : "",
1171                     options.v_agent ? options.v_agent : "");
1172     }
1173 
1174     if ((error == NULL) && (options.cmdline_params == NULL)) {
1175         options.cmdline_params = pcmk__strkey_table(free, free);
1176     }
1177 }
1178 
1179 /*!
1180  * \internal
1181  * \brief Get the <tt>enum pe_find</tt> flags for a given command
1182  *
1183  * \return <tt>enum pe_find</tt> flag group appropriate for \c options.rsc_cmd.
1184  */
1185 static uint32_t
1186 get_find_flags(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1187 {
1188     switch (options.rsc_cmd) {
1189         case cmd_ban:
1190         case cmd_cleanup:
1191         case cmd_clear:
1192         case cmd_colocations:
1193         case cmd_digests:
1194         case cmd_execute_agent:
1195         case cmd_locate:
1196         case cmd_move:
1197         case cmd_refresh:
1198         case cmd_restart:
1199         case cmd_why:
1200             return pcmk_rsc_match_history|pcmk_rsc_match_anon_basename;
1201 
1202         case cmd_delete_param:
1203         case cmd_get_param:
1204         case cmd_query_xml_raw:
1205         case cmd_query_xml:
1206         case cmd_set_param:
1207             return pcmk_rsc_match_history|pcmk_rsc_match_basename;
1208 
1209         default:
1210             return 0;
1211     }
1212 }
1213 
1214 /*!
1215  * \internal
1216  * \brief Check whether a node argument is required
1217  *
1218  * \return \c true if a \c --node argument is required, or \c false otherwise
1219  */
1220 static bool
1221 is_node_required(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1222 {
1223     switch (options.rsc_cmd) {
1224         case cmd_digests:
1225         case cmd_fail:
1226             return true;
1227         default:
1228             return false;
1229     }
1230 }
1231 
1232 /*!
1233  * \internal
1234  * \brief Check whether a resource argument is required
1235  *
1236  * \return \c true if a \c --resource argument is required, or \c false
1237  *         otherwise
1238  */
1239 static bool
1240 is_resource_required(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1241 {
1242     if (options.cmdline_config) {
1243         return false;
1244     }
1245 
1246     switch (options.rsc_cmd) {
1247         case cmd_clear:
1248             return !options.clear_expired;
1249 
1250         case cmd_cleanup:
1251         case cmd_cts:
1252         case cmd_list_active_ops:
1253         case cmd_list_agents:
1254         case cmd_list_all_ops:
1255         case cmd_list_alternatives:
1256         case cmd_list_instances:
1257         case cmd_list_options:
1258         case cmd_list_providers:
1259         case cmd_list_resources:
1260         case cmd_list_standards:
1261         case cmd_metadata:
1262         case cmd_refresh:
1263         case cmd_wait:
1264         case cmd_why:
1265             return false;
1266 
1267         default:
1268             return true;
1269     }
1270 }
1271 
1272 /*!
1273  * \internal
1274  * \brief Check whether a CIB connection is required
1275  *
1276  * \return \c true if a CIB connection is required, or \c false otherwise
1277  */
1278 static bool
1279 is_cib_required(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1280 {
1281     if (options.cmdline_config) {
1282         return false;
1283     }
1284 
1285     switch (options.rsc_cmd) {
1286         case cmd_list_agents:
1287         case cmd_list_alternatives:
1288         case cmd_list_options:
1289         case cmd_list_providers:
1290         case cmd_list_standards:
1291         case cmd_metadata:
1292             return false;
1293         default:
1294             return true;
1295     }
1296 }
1297 
1298 /*!
1299  * \internal
1300  * \brief Check whether a controller IPC connection is required
1301  *
1302  * \return \c true if a controller connection is required, or \c false otherwise
1303  */
1304 static bool
1305 is_controller_required(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1306 {
1307     switch (options.rsc_cmd) {
1308         case cmd_cleanup:
1309         case cmd_refresh:
1310             return getenv("CIB_file") == NULL;
1311 
1312         case cmd_fail:
1313             return true;
1314 
1315         default:
1316             return false;
1317     }
1318 }
1319 
1320 /*!
1321  * \internal
1322  * \brief Check whether a scheduler IPC connection is required
1323  *
1324  * \return \c true if a scheduler connection is required, or \c false otherwise
1325  */
1326 static bool
1327 is_scheduler_required(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1328 {
1329     if (options.cmdline_config) {
1330         return false;
1331     }
1332 
1333     switch (options.rsc_cmd) {
1334         case cmd_delete:
1335         case cmd_list_agents:
1336         case cmd_list_alternatives:
1337         case cmd_list_options:
1338         case cmd_list_providers:
1339         case cmd_list_standards:
1340         case cmd_metadata:
1341         case cmd_wait:
1342             return false;
1343         default:
1344             return true;
1345     }
1346 }
1347 
1348 /*!
1349  * \internal
1350  * \brief Check whether the chosen command accepts clone instances
1351  *
1352  * \return \c true if \p options.rsc_cmd accepts or ignores clone instances, or
1353  *         \c false otherwise
1354  */
1355 static bool
1356 accept_clone_instance(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1357 {
1358     switch (options.rsc_cmd) {
1359         case cmd_ban:
1360         case cmd_clear:
1361         case cmd_delete:
1362         case cmd_move:
1363         case cmd_restart:
1364             return false;
1365         default:
1366             return true;
1367     }
1368 }
1369 
1370 static GOptionContext *
1371 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
1372     GOptionContext *context = NULL;
1373 
1374     GOptionEntry extra_prog_entries[] = {
1375         { "quiet", 'Q', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &(args->quiet),
1376           "Be less descriptive in output.",
1377           NULL },
1378         { "resource", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.rsc_id,
1379           "Resource ID",
1380           "ID" },
1381         { G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING_ARRAY, &options.remainder,
1382           NULL,
1383           NULL },
1384 
1385         { NULL }
1386     };
1387 
1388     const char *description = "Examples:\n\n"
1389                               "List the available OCF agents:\n\n"
1390                               "\t# crm_resource --list-agents ocf\n\n"
1391                               "List the available OCF agents from the linux-ha project:\n\n"
1392                               "\t# crm_resource --list-agents ocf:heartbeat\n\n"
1393                               "Move 'myResource' to a specific node:\n\n"
1394                               "\t# crm_resource --resource myResource --move --node altNode\n\n"
1395                               "Allow (but not force) 'myResource' to move back to its original "
1396                               "location:\n\n"
1397                               "\t# crm_resource --resource myResource --clear\n\n"
1398                               "Stop 'myResource' (and anything that depends on it):\n\n"
1399                               "\t# crm_resource --resource myResource --set-parameter "
1400                               PCMK_META_TARGET_ROLE "--meta --parameter-value Stopped\n\n"
1401                               "Tell the cluster not to manage 'myResource' (the cluster will not "
1402                               "attempt to start or stop the\n"
1403                               "resource under any circumstances; useful when performing maintenance "
1404                               "tasks on a resource):\n\n"
1405                               "\t# crm_resource --resource myResource --set-parameter "
1406                               PCMK_META_IS_MANAGED "--meta --parameter-value false\n\n"
1407                               "Erase the operation history of 'myResource' on 'aNode' (the cluster "
1408                               "will 'forget' the existing\n"
1409                               "resource state, including any errors, and attempt to recover the"
1410                               "resource; useful when a resource\n"
1411                               "had failed permanently and has been repaired by an administrator):\n\n"
1412                               "\t# crm_resource --resource myResource --cleanup --node aNode\n\n";
1413 
1414     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
1415     g_option_context_set_description(context, description);
1416 
1417     /* Add the -Q option, which cannot be part of the globally supported options
1418      * because some tools use that flag for something else.
1419      */
1420     pcmk__add_main_args(context, extra_prog_entries);
1421 
1422     pcmk__add_arg_group(context, "queries", "Queries:",
1423                         "Show query help", query_entries);
1424     pcmk__add_arg_group(context, "commands", "Commands:",
1425                         "Show command help", command_entries);
1426     pcmk__add_arg_group(context, "locations", "Locations:",
1427                         "Show location help", location_entries);
1428     pcmk__add_arg_group(context, "advanced", "Advanced:",
1429                         "Show advanced option help", advanced_entries);
1430     pcmk__add_arg_group(context, "additional", "Additional Options:",
1431                         "Show additional options", addl_entries);
1432     return context;
1433 }
1434 
1435 int
1436 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
1437 {
1438     xmlNode *cib_xml_orig = NULL;
1439     pcmk_resource_t *rsc = NULL;
1440     pcmk_node_t *node = NULL;
1441     uint32_t find_flags = 0;
1442     int rc = pcmk_rc_ok;
1443 
1444     GOptionGroup *output_group = NULL;
1445     gchar **processed_args = NULL;
1446     GOptionContext *context = NULL;
1447 
1448     /*
1449      * Parse command line arguments
1450      */
1451 
1452     args = pcmk__new_common_args(SUMMARY);
1453     processed_args = pcmk__cmdline_preproc(argv, "GHINSTdginpstuvx");
1454     context = build_arg_context(args, &output_group);
1455 
1456     pcmk__register_formats(output_group, formats);
1457     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
1458         exit_code = CRM_EX_USAGE;
1459         goto done;
1460     }
1461 
1462     pcmk__cli_init_logging("crm_resource", args->verbosity);
1463 
1464     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
1465     if (rc != pcmk_rc_ok) {
1466         exit_code = CRM_EX_ERROR;
1467         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("Error creating output format %s: %s"),
1468                     args->output_ty, pcmk_rc_str(rc));
1469         goto done;
1470     }
1471 
1472     pe__register_messages(out);
1473     crm_resource_register_messages(out);
1474     lrmd__register_messages(out);
1475     pcmk__register_lib_messages(out);
1476 
1477     out->quiet = args->quiet;
1478 
1479     crm_log_args(argc, argv);
1480 
1481     /*
1482      * Validate option combinations
1483      */
1484 
1485     // --expired without --clear/-U doesn't make sense
1486     if (options.clear_expired && (options.rsc_cmd != cmd_clear)) {
1487         exit_code = CRM_EX_USAGE;
1488         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, _("--expired requires --clear or -U"));
1489         goto done;
1490     }
1491 
1492     if ((options.remainder != NULL) && (options.override_params != NULL)) {
1493         // Commands that use positional arguments will create override_params
1494         for (gchar **s = options.remainder; *s; s++) {
1495             char *name = pcmk__assert_alloc(1, strlen(*s));
1496             char *value = pcmk__assert_alloc(1, strlen(*s));
1497             int rc = sscanf(*s, "%[^=]=%s", name, value);
1498 
1499             if (rc == 2) {
1500                 g_hash_table_replace(options.override_params, name, value);
1501 
1502             } else {
1503                 exit_code = CRM_EX_USAGE;
1504                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1505                             _("Error parsing '%s' as a name=value pair"),
1506                             argv[optind]);
1507                 free(value);
1508                 free(name);
1509                 goto done;
1510             }
1511         }
1512 
1513     } else if (options.remainder != NULL) {
1514         gchar **strv = NULL;
1515         gchar *msg = NULL;
1516         int i = 1;
1517         int len = 0;
1518 
1519         for (gchar **s = options.remainder; *s; s++) {
1520             len++;
1521         }
1522 
1523         pcmk__assert(len > 0);
1524 
1525         /* Add 1 for the strv[0] string below, and add another 1 for the NULL
1526          * at the end of the array so g_strjoinv knows when to stop.
1527          */
1528         strv = pcmk__assert_alloc(len+2, sizeof(char *));
1529         strv[0] = strdup("non-option ARGV-elements:\n");
1530 
1531         for (gchar **s = options.remainder; *s; s++) {
1532             strv[i] = crm_strdup_printf("[%d of %d] %s\n", i, len, *s);
1533             i++;
1534         }
1535 
1536         strv[i] = NULL;
1537 
1538         exit_code = CRM_EX_USAGE;
1539         msg = g_strjoinv("", strv);
1540         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "%s", msg);
1541         g_free(msg);
1542 
1543         /* Don't try to free the last element, which is just NULL. */
1544         for(i = 0; i < len+1; i++) {
1545             free(strv[i]);
1546         }
1547         free(strv);
1548 
1549         goto done;
1550     }
1551 
1552     if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
1553         switch (options.rsc_cmd) {
1554             /* These are the only commands that have historically used the <list>
1555              * elements in their XML schema.  For all others, use the simple list
1556              * argument.
1557              */
1558             case cmd_get_param:
1559             case cmd_list_instances:
1560             case cmd_list_standards:
1561                 pcmk__output_enable_list_element(out);
1562                 break;
1563 
1564             default:
1565                 break;
1566         }
1567 
1568     } else if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches)) {
1569         switch (options.rsc_cmd) {
1570             case cmd_colocations:
1571             case cmd_list_resources:
1572                 pcmk__output_text_set_fancy(out, true);
1573                 break;
1574             default:
1575                 break;
1576         }
1577     }
1578 
1579     if (args->version) {
1580         out->version(out, false);
1581         goto done;
1582     }
1583 
1584     if (options.cmdline_config) {
1585         /* A resource configuration was given on the command line. Sanity-check
1586          * the values and set error if they don't make sense.
1587          */
1588         validate_cmdline_config();
1589         if (error != NULL) {
1590             exit_code = CRM_EX_USAGE;
1591             goto done;
1592         }
1593 
1594     } else if (options.cmdline_params != NULL) {
1595         exit_code = CRM_EX_USAGE;
1596         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1597                     _("--option must be used with --validate and without -r"));
1598         g_hash_table_destroy(options.cmdline_params);
1599         options.cmdline_params = NULL;
1600         goto done;
1601     }
1602 
1603     if (is_resource_required() && (options.rsc_id == NULL)) {
1604         exit_code = CRM_EX_USAGE;
1605         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1606                     _("Must supply a resource id with -r"));
1607         goto done;
1608     }
1609     if (is_node_required() && (options.host_uname == NULL)) {
1610         exit_code = CRM_EX_USAGE;
1611         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1612                     _("Must supply a node name with -N"));
1613         goto done;
1614     }
1615 
1616     /*
1617      * Set up necessary connections
1618      */
1619 
1620     // Establish a connection to the CIB if needed
1621     if (is_cib_required()) {
1622         cib_conn = cib_new();
1623         if ((cib_conn == NULL) || (cib_conn->cmds == NULL)) {
1624             exit_code = CRM_EX_DISCONNECT;
1625             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1626                         _("Could not create CIB connection"));
1627             goto done;
1628         }
1629         rc = cib__signon_attempts(cib_conn, cib_command, 5);
1630         rc = pcmk_legacy2rc(rc);
1631         if (rc != pcmk_rc_ok) {
1632             exit_code = pcmk_rc2exitc(rc);
1633             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1634                         _("Could not connect to the CIB: %s"), pcmk_rc_str(rc));
1635             goto done;
1636         }
1637     }
1638 
1639     // Populate scheduler data from XML file if specified or CIB query otherwise
1640     if (is_scheduler_required()) {
1641         rc = initialize_scheduler_data(&cib_xml_orig);
1642         if (rc != pcmk_rc_ok) {
1643             exit_code = pcmk_rc2exitc(rc);
1644             goto done;
1645         }
1646     }
1647 
1648     find_flags = get_find_flags();
1649 
1650     // If command requires that resource exist if specified, find it
1651     if ((find_flags != 0) && (options.rsc_id != NULL)) {
1652         rsc = pe_find_resource_with_flags(scheduler->priv->resources,
1653                                           options.rsc_id, find_flags);
1654         if (rsc == NULL) {
1655             exit_code = CRM_EX_NOSUCH;
1656             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1657                         _("Resource '%s' not found"), options.rsc_id);
1658             goto done;
1659         }
1660 
1661         /* The --ban, --clear, --move, and --restart commands do not work with
1662          * instances of clone resourcs.
1663          */
1664         if (pcmk__is_clone(rsc->priv->parent)
1665             && (strchr(options.rsc_id, ':') != NULL)
1666             && !accept_clone_instance()) {
1667 
1668             exit_code = CRM_EX_INVALID_PARAM;
1669             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1670                         _("Cannot operate on clone resource instance '%s'"), options.rsc_id);
1671             goto done;
1672         }
1673     }
1674 
1675     // If user supplied a node name, check whether it exists
1676     if ((options.host_uname != NULL) && (scheduler != NULL)) {
1677         node = pcmk_find_node(scheduler, options.host_uname);
1678 
1679         if (node == NULL) {
1680             exit_code = CRM_EX_NOSUCH;
1681             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1682                         _("Node '%s' not found"), options.host_uname);
1683             goto done;
1684         }
1685     }
1686 
1687     // Establish a connection to the controller if needed
1688     if (is_controller_required()) {
1689         rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
1690         if (rc != pcmk_rc_ok) {
1691             exit_code = pcmk_rc2exitc(rc);
1692             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1693                         _("Error connecting to the controller: %s"), pcmk_rc_str(rc));
1694             goto done;
1695         }
1696         pcmk_register_ipc_callback(controld_api, controller_event_callback,
1697                                    NULL);
1698         rc = pcmk__connect_ipc(controld_api, pcmk_ipc_dispatch_main, 5);
1699         if (rc != pcmk_rc_ok) {
1700             exit_code = pcmk_rc2exitc(rc);
1701             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1702                         _("Error connecting to %s: %s"),
1703                         pcmk_ipc_name(controld_api, true), pcmk_rc_str(rc));
1704             goto done;
1705         }
1706     }
1707 
1708     /*
1709      * Handle requested command
1710      */
1711 
1712     switch (options.rsc_cmd) {
1713         case cmd_list_resources: {
1714             GList *all = NULL;
1715             uint32_t show_opts = pcmk_show_inactive_rscs | pcmk_show_rsc_only | pcmk_show_pending;
1716 
1717             all = g_list_prepend(all, (gpointer) "*");
1718             rc = out->message(out, "resource-list", scheduler,
1719                               show_opts, true, all, all, false);
1720             g_list_free(all);
1721 
1722             if (rc == pcmk_rc_no_output) {
1723                 rc = ENXIO;
1724             }
1725             break;
1726         }
1727 
1728         case cmd_list_instances:
1729             rc = out->message(out, "resource-names-list",
1730                               scheduler->priv->resources);
1731 
1732             if (rc != pcmk_rc_ok) {
1733                 rc = ENXIO;
1734             }
1735 
1736             break;
1737 
1738         case cmd_list_options:
1739             list_options();
1740             break;
1741 
1742         case cmd_list_alternatives:
1743             rc = pcmk__list_alternatives(out, options.agent_spec);
1744             break;
1745 
1746         case cmd_list_agents:
1747             rc = pcmk__list_agents(out, options.agent_spec);
1748             break;
1749 
1750         case cmd_list_standards:
1751             rc = pcmk__list_standards(out);
1752             break;
1753 
1754         case cmd_list_providers:
1755             rc = pcmk__list_providers(out, options.agent_spec);
1756             break;
1757 
1758         case cmd_metadata:
1759             rc = show_metadata(out, options.agent_spec);
1760             break;
1761 
1762         case cmd_restart:
1763             /* We don't pass scheduler because rsc needs to stay valid for the
1764              * entire lifetime of cli_resource_restart(), but it will reset and
1765              * update the scheduler data multiple times, so it needs to use its
1766              * own copy.
1767              */
1768             rc = cli_resource_restart(out, rsc, node, options.move_lifetime,
1769                                       options.timeout_ms, cib_conn,
1770                                       options.promoted_role_only,
1771                                       options.force);
1772             break;
1773 
1774         case cmd_wait:
1775             rc = wait_till_stable(out, options.timeout_ms, cib_conn);
1776             break;
1777 
1778         case cmd_execute_agent:
1779             if (options.cmdline_config) {
1780                 exit_code = cli_resource_execute_from_params(out, NULL,
1781                     options.v_class, options.v_provider, options.v_agent,
1782                     options.operation, options.cmdline_params,
1783                     options.override_params, options.timeout_ms,
1784                     args->verbosity, options.force, options.check_level);
1785             } else {
1786                 exit_code = cli_resource_execute(rsc, options.rsc_id,
1787                     options.operation, options.override_params,
1788                     options.timeout_ms, cib_conn, scheduler,
1789                     args->verbosity, options.force, options.check_level);
1790             }
1791             goto done;
1792 
1793         case cmd_digests:
1794             node = pcmk_find_node(scheduler, options.host_uname);
1795             if (node == NULL) {
1796                 rc = pcmk_rc_node_unknown;
1797             } else {
1798                 rc = pcmk__resource_digests(out, rsc, node,
1799                                             options.override_params);
1800             }
1801             break;
1802 
1803         case cmd_colocations:
1804             rc = out->message(out, "locations-and-colocations", rsc,
1805                               options.recursive, (bool) options.force);
1806             break;
1807 
1808         case cmd_cts:
1809             rc = pcmk_rc_ok;
1810             g_list_foreach(scheduler->priv->resources,
1811                            (GFunc) cli_resource_print_cts, out);
1812             cli_resource_print_cts_constraints(scheduler);
1813             break;
1814 
1815         case cmd_fail:
1816             rc = cli_resource_fail(controld_api, options.host_uname,
1817                                    options.rsc_id, scheduler);
1818             if (rc == pcmk_rc_ok) {
1819                 start_mainloop(controld_api);
1820             }
1821             break;
1822 
1823         case cmd_list_active_ops:
1824             rc = cli_resource_print_operations(options.rsc_id,
1825                                                options.host_uname, TRUE,
1826                                                scheduler);
1827             break;
1828 
1829         case cmd_list_all_ops:
1830             rc = cli_resource_print_operations(options.rsc_id,
1831                                                options.host_uname, FALSE,
1832                                                scheduler);
1833             break;
1834 
1835         case cmd_locate: {
1836             GList *nodes = cli_resource_search(rsc, options.rsc_id, scheduler);
1837             rc = out->message(out, "resource-search-list", nodes, options.rsc_id);
1838             g_list_free_full(nodes, free);
1839             break;
1840         }
1841 
1842         case cmd_query_xml:
1843             rc = cli_resource_print(rsc, scheduler, true);
1844             break;
1845 
1846         case cmd_query_xml_raw:
1847             rc = cli_resource_print(rsc, scheduler, false);
1848             break;
1849 
1850         case cmd_why:
1851             if ((options.host_uname != NULL) && (node == NULL)) {
1852                 rc = pcmk_rc_node_unknown;
1853             } else {
1854                 rc = out->message(out, "resource-reasons-list",
1855                                   scheduler->priv->resources, rsc, node);
1856             }
1857             break;
1858 
1859         case cmd_clear:
1860             rc = clear_constraints(out);
1861             break;
1862 
1863         case cmd_move:
1864             if (options.host_uname == NULL) {
1865                 rc = ban_or_move(out, rsc, options.move_lifetime);
1866             } else {
1867                 rc = cli_resource_move(rsc, options.rsc_id, options.host_uname,
1868                                        options.move_lifetime, cib_conn,
1869                                        scheduler, options.promoted_role_only,
1870                                        options.force);
1871             }
1872 
1873             if (rc == EINVAL) {
1874                 exit_code = CRM_EX_USAGE;
1875                 goto done;
1876             }
1877 
1878             break;
1879 
1880         case cmd_ban:
1881             if (options.host_uname == NULL) {
1882                 rc = ban_or_move(out, rsc, options.move_lifetime);
1883             } else if (node == NULL) {
1884                 rc = pcmk_rc_node_unknown;
1885             } else {
1886                 rc = cli_resource_ban(out, options.rsc_id, node->priv->name,
1887                                       options.move_lifetime, cib_conn,
1888                                       options.promoted_role_only,
1889                                       PCMK_ROLE_PROMOTED);
1890             }
1891 
1892             if (rc == EINVAL) {
1893                 exit_code = CRM_EX_USAGE;
1894                 goto done;
1895             }
1896 
1897             break;
1898 
1899         case cmd_get_param: {
1900             unsigned int count = 0;
1901             GHashTable *params = NULL;
1902             pcmk_node_t *current = rsc->priv->fns->active_node(rsc, &count,
1903                                                                NULL);
1904             bool free_params = true;
1905             const char* value = NULL;
1906 
1907             if (count > 1) {
1908                 out->err(out, "%s is active on more than one node,"
1909                          " returning the default value for %s", rsc->id,
1910                          pcmk__s(options.prop_name, "unspecified property"));
1911                 current = NULL;
1912             }
1913 
1914             crm_debug("Looking up %s in %s", options.prop_name, rsc->id);
1915 
1916             if (pcmk__str_eq(options.attr_set_type, PCMK_XE_INSTANCE_ATTRIBUTES,
1917                              pcmk__str_none)) {
1918                 params = pe_rsc_params(rsc, current, scheduler);
1919                 free_params = false;
1920 
1921                 value = g_hash_table_lookup(params, options.prop_name);
1922 
1923             } else if (pcmk__str_eq(options.attr_set_type,
1924                                     PCMK_XE_META_ATTRIBUTES, pcmk__str_none)) {
1925                 params = pcmk__strkey_table(free, free);
1926                 get_meta_attributes(params, rsc, NULL, scheduler);
1927 
1928                 value = g_hash_table_lookup(params, options.prop_name);
1929 
1930             } else if (pcmk__str_eq(options.attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) {
1931 
1932                 value = crm_element_value(rsc->priv->xml, options.prop_name);
1933                 free_params = false;
1934 
1935             } else {
1936                 pe_rule_eval_data_t rule_data = {
1937                     .now = scheduler->priv->now,
1938                 };
1939 
1940                 params = pcmk__strkey_table(free, free);
1941                 pe__unpack_dataset_nvpairs(rsc->priv->xml, PCMK_XE_UTILIZATION,
1942                                            &rule_data, params, NULL, scheduler);
1943 
1944                 value = g_hash_table_lookup(params, options.prop_name);
1945             }
1946 
1947             rc = out->message(out, "attribute-list", rsc, options.prop_name, value);
1948             if (free_params) {
1949                 g_hash_table_destroy(params);
1950             }
1951 
1952             break;
1953         }
1954 
1955         case cmd_set_param:
1956             if (pcmk__str_empty(options.prop_value)) {
1957                 exit_code = CRM_EX_USAGE;
1958                 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
1959                             _("You need to supply a value with the -v option"));
1960                 goto done;
1961             }
1962 
1963             /* coverity[var_deref_model] False positive */
1964             rc = cli_resource_update_attribute(rsc, options.rsc_id,
1965                                                options.prop_set,
1966                                                options.attr_set_type,
1967                                                options.prop_id,
1968                                                options.prop_name,
1969                                                options.prop_value,
1970                                                options.recursive, cib_conn,
1971                                                cib_xml_orig, options.force);
1972             break;
1973 
1974         case cmd_delete_param:
1975             /* coverity[var_deref_model] False positive */
1976             rc = cli_resource_delete_attribute(rsc, options.rsc_id,
1977                                                options.prop_set,
1978                                                options.attr_set_type,
1979                                                options.prop_id,
1980                                                options.prop_name, cib_conn,
1981                                                cib_xml_orig, options.force);
1982             break;
1983 
1984         case cmd_cleanup:
1985             if (rsc == NULL) {
1986                 rc = cli_cleanup_all(controld_api, options.host_uname,
1987                                      options.operation, options.interval_spec,
1988                                      scheduler);
1989                 if (rc == pcmk_rc_ok) {
1990                     start_mainloop(controld_api);
1991                 }
1992             } else {
1993                 cleanup(out, rsc, node);
1994             }
1995             break;
1996 
1997         case cmd_refresh:
1998             if (rsc == NULL) {
1999                 rc = refresh(out);
2000             } else {
2001                 refresh_resource(out, rsc, node);
2002             }
2003             break;
2004 
2005         case cmd_delete:
2006             /* rsc_id was already checked for NULL much earlier when validating
2007              * command line arguments.
2008              */
2009             if (options.rsc_type == NULL) {
2010                 exit_code = CRM_EX_USAGE;
2011                 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
2012                             _("You need to specify a resource type with -t"));
2013             } else {
2014                 rc = pcmk__resource_delete(cib_conn, cib_sync_call,
2015                                            options.rsc_id, options.rsc_type);
2016 
2017                 if (rc != pcmk_rc_ok) {
2018                     g_set_error(&error, PCMK__RC_ERROR, rc,
2019                                 _("Could not delete resource %s: %s"),
2020                                 options.rsc_id, pcmk_rc_str(rc));
2021                 }
2022             }
2023 
2024             break;
2025 
2026         default:
2027             exit_code = CRM_EX_USAGE;
2028             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2029                         _("Unimplemented command: %d"), (int) options.rsc_cmd);
2030             goto done;
2031     }
2032 
2033     /* Convert rc into an exit code. */
2034     if (rc != pcmk_rc_ok && rc != pcmk_rc_no_output) {
2035         exit_code = pcmk_rc2exitc(rc);
2036     }
2037 
2038     /*
2039      * Clean up and exit
2040      */
2041 
2042 done:
2043     /* When we get here, exit_code has been set one of two ways - either at one of
2044      * the spots where there's a "goto done" (which itself could have happened either
2045      * directly or by calling pcmk_rc2exitc), or just up above after any of the break
2046      * statements.
2047      *
2048      * Thus, we can use just exit_code here to decide what to do.
2049      */
2050     if (exit_code != CRM_EX_OK && exit_code != CRM_EX_USAGE) {
2051         if (error != NULL) {
2052             char *msg = crm_strdup_printf("%s\nError performing operation: %s",
2053                                           error->message, crm_exit_str(exit_code));
2054             g_clear_error(&error);
2055             g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "%s", msg);
2056             free(msg);
2057         } else {
2058             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
2059                         _("Error performing operation: %s"), crm_exit_str(exit_code));
2060         }
2061     }
2062 
2063     pcmk__xml_free(cib_xml_orig);
2064 
2065     g_free(options.host_uname);
2066     g_free(options.interval_spec);
2067     g_free(options.move_lifetime);
2068     g_free(options.operation);
2069     g_free(options.prop_id);
2070     free(options.prop_name);
2071     g_free(options.prop_set);
2072     g_free(options.prop_value);
2073     g_free(options.rsc_id);
2074     g_free(options.rsc_type);
2075     free(options.agent_spec);
2076     free(options.v_agent);
2077     free(options.v_class);
2078     free(options.v_provider);
2079     g_strfreev(options.remainder);
2080 
2081     if (options.override_params != NULL) {
2082         g_hash_table_destroy(options.override_params);
2083     }
2084 
2085     /* options.cmdline_params does not need to be destroyed here.  See the
2086      * comments in cli_resource_execute_from_params.
2087      */
2088 
2089     g_strfreev(processed_args);
2090     g_option_context_free(context);
2091 
2092     return bye(exit_code);
2093 }

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