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

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