root/tools/crm_resource.c

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

DEFINITIONS

This source file includes following definitions.
  1. quit_main_loop
  2. resource_ipc_timeout
  3. controller_event_callback
  4. start_mainloop
  5. build_constraint_list
  6. validate_opt_list
  7. attr_set_type_cb
  8. command_cb
  9. option_cb
  10. timeout_cb
  11. ban_or_move
  12. cleanup
  13. initialize_scheduler_data
  14. refresh
  15. refresh_resource
  16. has_cmdline_config
  17. validate_cmdline_config
  18. handle_ban
  19. handle_cleanup
  20. handle_clear
  21. handle_colocations
  22. handle_cts
  23. handle_delete
  24. handle_delete_param
  25. handle_digests
  26. handle_execute_agent
  27. handle_fail
  28. handle_get_param
  29. handle_list_active_ops
  30. handle_list_agents
  31. handle_list_all_ops
  32. handle_list_alternatives
  33. handle_list_instances
  34. handle_list_options
  35. handle_list_providers
  36. handle_list_resources
  37. handle_list_standards
  38. handle_locate
  39. handle_metadata
  40. handle_move
  41. handle_query_xml
  42. handle_query_xml_raw
  43. handle_refresh
  44. handle_restart
  45. handle_set_param
  46. handle_wait
  47. handle_why
  48. build_arg_context
  49. main

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

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