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
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();
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);
984 if (rc != pcmk_rc_ok) {
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);
|
(1) Event path: |
Condition "!g_option_context_parse_strv(context, &processed_args, &error)", taking true branch. |
2083 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
2084 exit_code = CRM_EX_USAGE;
|
(2) Event path: |
Jumping to label "done". |
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
|
(3) Event path: |
Condition "exit_code != CRM_EX_OK", taking true branch. |
|
(4) Event path: |
Condition "exit_code != CRM_EX_USAGE", taking false branch. |
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);
|
CID (unavailable; MK=983727b523ac41ac10bb365179e72375) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(5) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(6) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
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