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

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

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