root/tools/crm_resource.c

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

DEFINITIONS

This source file includes following definitions.
  1. bye
  2. quit_main_loop
  3. resource_ipc_timeout
  4. controller_event_callback
  5. start_mainloop
  6. compare_id
  7. build_constraint_list
  8. reset_options
  9. agent_provider_cb
  10. attr_set_type_cb
  11. class_cb
  12. cleanup_refresh_cb
  13. delete_cb
  14. expired_cb
  15. get_agent_spec
  16. list_agents_cb
  17. list_providers_cb
  18. list_standards_cb
  19. list_alternatives_cb
  20. metadata_cb
  21. option_cb
  22. fail_cb
  23. flag_cb
  24. get_param_prop_cb
  25. list_cb
  26. set_delete_param_cb
  27. set_prop_cb
  28. timeout_cb
  29. validate_or_force_cb
  30. restart_cb
  31. digests_cb
  32. wait_cb
  33. why_cb
  34. ban_or_move
  35. cleanup
  36. clear_constraints
  37. delete
  38. list_agents
  39. list_providers
  40. populate_working_set
  41. refresh
  42. refresh_resource
  43. set_property
  44. show_metadata
  45. validate_cmdline_config
  46. build_arg_context
  47. main

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

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