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. reset_options
  9. agent_provider_cb
  10. attr_set_type_cb
  11. class_cb
  12. cleanup_refresh_cb
  13. delete_cb
  14. expired_cb
  15. get_agent_spec
  16. list_agents_cb
  17. list_providers_cb
  18. list_standards_cb
  19. list_alternatives_cb
  20. metadata_cb
  21. option_cb
  22. fail_cb
  23. flag_cb
  24. get_param_prop_cb
  25. list_cb
  26. set_delete_param_cb
  27. set_prop_cb
  28. timeout_cb
  29. validate_or_force_cb
  30. restart_cb
  31. digests_cb
  32. wait_cb
  33. why_cb
  34. ban_or_move
  35. cleanup
  36. clear_constraints
  37. delete
  38. list_agents
  39. list_providers
  40. populate_working_set
  41. refresh
  42. refresh_resource
  43. set_property
  44. show_metadata
  45. validate_cmdline_config
  46. build_arg_context
  47. main

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

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