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