Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F7632506
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
35 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/include/crm/common/cmdline_internal.h b/include/crm/common/cmdline_internal.h
index b27b236204..bbe8ff71f5 100644
--- a/include/crm/common/cmdline_internal.h
+++ b/include/crm/common/cmdline_internal.h
@@ -1,38 +1,77 @@
/*
* Copyright 2019 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef CMDLINE_INTERNAL__H
#define CMDLINE_INTERNAL__H
#ifdef __cplusplus
extern "C" {
#endif
#include <glib.h>
typedef struct {
char *summary;
gboolean version;
gboolean quiet;
unsigned int verbosity;
char *output_ty;
char *output_ty_desc;
char *output_dest;
} pcmk__common_args_t;
GOptionContext *
pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts);
+/*!
+ * \internal
+ * \brief Pre-process command line arguments to preserve compatibility with
+ * getopt behavior.
+ *
+ * getopt and glib have slightly different behavior when it comes to processing
+ * single command line arguments. getopt allows this: -x<val>, while glib will
+ * try to handle <val> like it is additional single letter arguments. glib
+ * prefers -x <val> instead.
+ *
+ * This function scans argv, looking for any single letter command line options
+ * (indicated by the 'special' parameter). When one is found, everything after
+ * that argument to the next whitespace is converted into its own value. Single
+ * letter command line options can come in a group after a single dash, but
+ * this function will expand each group into many arguments.
+ *
+ * Long options and anything after "--" is preserved. The result of this function
+ * can then be passed to ::g_option_context_parse_strv for actual processing.
+ *
+ * In pseudocode, this:
+ *
+ * pcmk__cmdline_preproc(4, ["-XbA", "--blah=foo", "-aF", "-Fval", "--", "--extra", "-args"], "aF")
+ *
+ * Would be turned into this:
+ *
+ * ["-X", "-b", "-A", "--blah=foo", "-a", "F", "-F", "val", "--", "--extra", "-args"]
+ *
+ * This function does not modify argv, and the return value is built of copies
+ * of all the command line arguments. It is up to the caller to free this memory
+ * after use.
+ *
+ * \param[in] argc The length of argv.
+ * \param[in] argv The command line arguments.
+ * \param[in] special Single-letter command line arguments that take a value.
+ * These letters will all have pre-processing applied.
+ */
+char **
+pcmk__cmdline_preproc(int argc, char **argv, const char *special);
+
#ifdef __cplusplus
}
#endif
#endif
diff --git a/lib/common/cmdline.c b/lib/common/cmdline.c
index 7e370e645a..c4c5848f47 100644
--- a/lib/common/cmdline.c
+++ b/lib/common/cmdline.c
@@ -1,69 +1,151 @@
/*
* Copyright 2019 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <config.h>
#include <glib.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/util.h>
static gboolean
bump_verbosity(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
common_args->verbosity++;
return TRUE;
}
static void
free_common_args(gpointer data) {
pcmk__common_args_t *common_args = (pcmk__common_args_t *) data;
free(common_args->summary);
free(common_args->output_ty);
free(common_args->output_ty_desc);
free(common_args->output_dest);
}
GOptionContext *
pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts) {
char *desc = crm_strdup_printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
GOptionContext *context;
GOptionGroup *main_group;
GOptionEntry main_entries[5] = {
{ "version", '$', 0, G_OPTION_ARG_NONE, &(common_args->version),
"Display version information and exit.",
NULL },
{ "verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, bump_verbosity,
"Increase debug output (may be specified multiple times).",
NULL },
{ "output-as", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_ty),
NULL,
"FORMAT" },
{ "output-to", 0, 0, G_OPTION_ARG_STRING, &(common_args->output_dest),
"Specify the destination for output, \"-\" for stdout or a filename.", "DEST" },
{ NULL }
};
common_args->output_ty_desc = crm_strdup_printf("Specify the format for output, one of: %s", fmts);
main_entries[2].description = common_args->output_ty_desc;
main_group = g_option_group_new(NULL, "Application Options:", NULL, common_args, free_common_args);
g_option_group_add_entries(main_group, main_entries);
context = g_option_context_new(NULL);
g_option_context_set_summary(context, common_args->summary);
g_option_context_set_description(context, desc);
g_option_context_set_main_group(context, main_group);
free(desc);
return context;
}
+
+char **
+pcmk__cmdline_preproc(int argc, char **argv, const char *special) {
+ char **retval = NULL;
+ GPtrArray *arr = g_ptr_array_new();
+ bool saw_dash_dash = false;
+
+ for (int i = 0; i < argc; i++) {
+ /* If this is the first time we saw "--" in the command line, set
+ * a flag so we know to just copy everything after it over. We also
+ * want to copy the "--" over so whatever actually parses the command
+ * line when we're done knows where arguments end.
+ */
+ if (saw_dash_dash == false && strcmp(argv[i], "--") == 0) {
+ saw_dash_dash = true;
+ }
+
+ if (saw_dash_dash == true) {
+ g_ptr_array_add(arr, strdup(argv[i]));
+ continue;
+ }
+
+ /* This is a short argument, or perhaps several. Iterate over it
+ * and explode them out into individual arguments.
+ */
+ if (g_str_has_prefix(argv[i], "-") && !g_str_has_prefix(argv[i], "--")) {
+ /* Skip over leading dash */
+ char *ch = argv[i]+1;
+
+ while (*ch != '\0') {
+ /* This is a special short argument that takes an option. getopt
+ * allows values to be interspersed with a list of arguments, but
+ * glib does not. Grab both the argument and its value and
+ * separate them into a new argument.
+ */
+ if (strchr(special, *ch) != NULL) {
+ /* The argument does not occur at the end of this string of
+ * arguments. Take everything through the end as its value.
+ */
+ if (*(ch+1) != '\0') {
+ g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
+ g_ptr_array_add(arr, strdup(ch+1));
+ break;
+
+ /* The argument occurs at the end of this string. Hopefully
+ * whatever comes next in argv is its value. It may not be,
+ * but that is not for us to decide.
+ */
+ } else {
+ g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
+ ch++;
+ }
+
+ /* This is a regular short argument. Just copy it over. */
+ } else {
+ g_ptr_array_add(arr, (gpointer) crm_strdup_printf("-%c", *ch));
+ ch++;
+ }
+ }
+
+ /* This is a long argument, or an option, or something else.
+ * Copy it over - everything else is copied, so this keeps it easy for
+ * the caller to know what to do with the memory when it's done.
+ */
+ } else {
+ g_ptr_array_add(arr, strdup(argv[i]));
+ }
+ }
+
+ /* Convert the GPtrArray into a char **, which the command line parsing
+ * code knows how to deal with. Then we can free the array (but not its
+ * contents).
+ */
+ retval = calloc(arr->len+1, sizeof(char *));
+ for (int i = 0; i < arr->len; i++) {
+ retval [i] = (char *) g_ptr_array_index(arr, i);
+ }
+
+ g_ptr_array_free(arr, FALSE);
+
+ return retval;
+}
diff --git a/tools/stonith_admin.c b/tools/stonith_admin.c
index 1d848abfbe..3e29073bec 100644
--- a/tools/stonith_admin.c
+++ b/tools/stonith_admin.c
@@ -1,844 +1,840 @@
/*
* Copyright 2009-2019 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/mainloop.h>
#include <crm/common/output.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/cib.h>
#include <crm/pengine/status.h>
#include <crm/common/xml.h>
char action = 0;
struct {
gboolean as_nodeid;
gboolean broadcast;
gboolean cleanup;
gboolean installed;
gboolean metadata;
gboolean registered;
gboolean validate_cfg;
stonith_key_value_t *devices;
stonith_key_value_t *params;
int fence_level;
int timeout ;
int tolerance;
int verbose;
char *agent;
char *confirm_host;
char *fence_host;
char *history;
char *last_fenced;
char *query;
char *reboot_host;
char *register_dev;
char *register_level;
char *targets;
char *terminate;
char *unfence_host;
char *unregister_dev;
char *unregister_level;
} options = {
.timeout = 120
};
gboolean add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean add_stonith_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
#define INDENT " "
/* *INDENT-OFF* */
static GOptionEntry defn_entries[] = {
{ "register", 'R', 0, G_OPTION_ARG_STRING, &options.register_dev,
"Register the named stonith device. Requires: --agent.\n"
INDENT "Optional: --option, --env-option.",
"DEVICE" },
{ "deregister", 'D', 0, G_OPTION_ARG_STRING, &options.unregister_dev,
"De-register the named stonith device.",
"DEVICE" },
{ "register-level", 'r', 0, G_OPTION_ARG_STRING, &options.register_level,
"Register a stonith level for the named target,\n"
INDENT "specified as one of NAME, @PATTERN, or ATTR=VALUE.\n"
INDENT "Requires: --index and one or more --device entries.",
"TARGET" },
{ "deregister-level", 'd', 0, G_OPTION_ARG_STRING, &options.unregister_level,
"Unregister a stonith level for the named target,\n"
INDENT "specified as for --register-level. Requires: --index",
"TARGET" },
{ NULL }
};
static GOptionEntry query_entries[] = {
{ "list", 'l', 0, G_OPTION_ARG_STRING, &options.terminate,
"List devices that can terminate the specified host.\n"
INDENT "Optional: --timeout",
"HOST" },
{ "list-registered", 'L', 0, G_OPTION_ARG_NONE, &options.registered,
"List all registered devices. Optional: --timeout.",
NULL },
{ "list-installed", 'I', 0, G_OPTION_ARG_NONE, &options.installed,
"List all installed devices. Optional: --timeout.",
NULL },
{ "list-targets", 's', 0, G_OPTION_ARG_STRING, &options.targets,
"List the targets that can be fenced by the\n"
INDENT "named device. Optional: --timeout.",
"DEVICE" },
{ "metadata", 'M', 0, G_OPTION_ARG_NONE, &options.metadata,
"Show agent metadata. Requires: --agent.\n"
INDENT "Optional: --timeout.",
NULL },
{ "query", 'Q', 0, G_OPTION_ARG_STRING, &options.query,
"Check the named device's status. Optional: --timeout.",
"DEVICE" },
{ "history", 'H', 0, G_OPTION_ARG_STRING, &options.history,
"Show last successful fencing operation for named node\n"
INDENT "(or '*' for all nodes). Optional: --timeout, --cleanup,\n"
INDENT "--quiet (show only the operation's epoch timestamp),\n"
INDENT "--verbose (show all recorded and pending operations),\n"
INDENT "--broadcast (update history from all nodes available).",
"NODE" },
{ "last", 'h', 0, G_OPTION_ARG_STRING, &options.last_fenced,
"Indicate when the named node was last fenced.\n"
INDENT "Optional: --as-node-id.",
"NODE" },
{ "validate", 'K', 0, G_OPTION_ARG_NONE, &options.validate_cfg,
"Validate a fence device configuration.\n"
INDENT "Requires: --agent. Optional: --option, --env-option,\n"
INDENT "--quiet (print no output, only return status).",
NULL },
{ NULL }
};
static GOptionEntry fence_entries[] = {
{ "fence", 'F', 0, G_OPTION_ARG_STRING, &options.fence_host,
"Fence named host. Optional: --timeout, --tolerance.",
"HOST" },
{ "unfence", 'U', 0, G_OPTION_ARG_STRING, &options.unfence_host,
"Unfence named host. Optional: --timeout, --tolerance.",
"HOST" },
{ "reboot", 'B', 0, G_OPTION_ARG_STRING, &options.reboot_host,
"Reboot named host. Optional: --timeout, --tolerance.",
"HOST" },
{ "confirm", 'C', 0, G_OPTION_ARG_STRING, &options.confirm_host,
"Tell clusted that named host is now safely down.",
"HOST", },
{ NULL }
};
static GOptionEntry addl_entries[] = {
{ "cleanup", 'c', 0, G_OPTION_ARG_NONE, &options.cleanup,
"Cleanup wherever appropriate. Requires --history.",
NULL },
{ "broadcast", 'b', 0, G_OPTION_ARG_NONE, &options.broadcast,
"Broadcast wherever appropriate.",
NULL },
{ "agent", 'a', 0, G_OPTION_ARG_STRING, &options.agent,
"The agent to use (for example, fence_xvm;\n"
INDENT "with --register, --metadata, --validate).",
"AGENT" },
{ "option", 'o', 0, G_OPTION_ARG_CALLBACK, add_stonith_params,
"Specify a device configuration parameter as NAME=VALUE\n"
INDENT "(may be specified multiple times; with --register,\n"
INDENT "--validate).",
"PARAM" },
{ "env-option", 'e', 0, G_OPTION_ARG_CALLBACK, add_env_params,
"Specify a device configuration parameter with the\n"
INDENT "specified name, using the value of the\n"
INDENT "environment variable of the same name prefixed with\n"
INDENT "OCF_RESKEY_ (may be specified multiple times;\n"
INDENT "with --register, --validate).",
"PARAM" },
{ "tag", 'T', 0, G_OPTION_ARG_CALLBACK, set_tag,
"Identify fencing operations in logs with the specified\n"
INDENT "tag; useful when multiple entities might invoke\n"
INDENT "stonith_admin (used with most commands).",
"TAG" },
{ "device", 'v', 0, G_OPTION_ARG_CALLBACK, add_stonith_device,
"Device ID (with --register-level, device to associate with\n"
INDENT "a given host and level; may be specified multiple times)"
#if SUPPORT_CIBSECRETS
"\n" INDENT "(with --validate, name to use to load CIB secrets)"
#endif
".",
"DEVICE" },
{ "index", 'i', 0, G_OPTION_ARG_INT, &options.fence_level,
"The stonith level (1-9) (with --register-level,\n"
INDENT "--deregister-level).",
"LEVEL" },
{ "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout,
"Operation timeout in seconds (default 120;\n"
INDENT "used with most commands).",
"SECONDS" },
{ "as-node-id", 'n', 0, G_OPTION_ARG_NONE, &options.as_nodeid,
"(Advanced) The supplied node is the corosync node ID\n"
INDENT "(with --last).",
NULL },
{ "tolerance", 0, 0, G_OPTION_ARG_CALLBACK, add_tolerance,
"(Advanced) Do nothing if an equivalent --fence request\n"
INDENT "succeeded less than this many seconds earlier\n"
INDENT "(with --fence, --unfence, --reboot).",
"SECONDS" },
{ NULL }
};
/* *INDENT-ON* */
static pcmk__supported_format_t formats[] = {
PCMK__SUPPORTED_FORMAT_HTML,
PCMK__SUPPORTED_FORMAT_TEXT,
PCMK__SUPPORTED_FORMAT_XML,
{ NULL, NULL, NULL }
};
static int st_opts = st_opt_sync_call | st_opt_allow_suicide;
static GMainLoop *mainloop = NULL;
struct {
stonith_t *st;
const char *target;
const char *action;
char *name;
int timeout;
int tolerance;
int rc;
} async_fence_data;
gboolean
add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
char *key = crm_concat("OCF_RESKEY", optarg, '_');
const char *env = getenv(key);
gboolean retval = TRUE;
if (env == NULL) {
crm_err("Invalid option: -e %s", optarg);
g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, "Invalid option: -e %s", optarg);
retval = FALSE;
} else {
crm_info("Got: '%s'='%s'", optarg, env);
options.params = stonith_key_value_add(options.params, optarg, env);
}
free(key);
return retval;
}
gboolean
add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.devices = stonith_key_value_add(options.devices, NULL, optarg);
return TRUE;
}
gboolean
add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.tolerance = crm_get_msec(optarg) / 1000;
return TRUE;
}
gboolean
add_stonith_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
char *name = NULL;
char *value = NULL;
int rc = 0;
gboolean retval = TRUE;
crm_info("Scanning: -o %s", optarg);
rc = pcmk_scan_nvpair(optarg, &name, &value);
if (rc != 2) {
crm_err("Invalid option: -o %s: %s", optarg, pcmk_strerror(rc));
g_set_error(error, G_OPTION_ERROR, rc, "Invalid option: -o %s: %s", optarg, pcmk_strerror(rc));
retval = FALSE;
} else {
crm_info("Got: '%s'='%s'", name, value);
options.params = stonith_key_value_add(options.params, name, value);
}
free(name);
free(value);
return retval;
}
gboolean
set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
free(async_fence_data.name);
async_fence_data.name = crm_strdup_printf("%s.%s", crm_system_name, optarg);
return TRUE;
}
static void
notify_callback(stonith_t * st, stonith_event_t * e)
{
if (e->result != pcmk_ok) {
return;
}
if (safe_str_eq(async_fence_data.target, e->target) &&
safe_str_eq(async_fence_data.action, e->action)) {
async_fence_data.rc = e->result;
g_main_loop_quit(mainloop);
}
}
static void
fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
{
async_fence_data.rc = data->rc;
g_main_loop_quit(mainloop);
}
static gboolean
async_fence_helper(gpointer user_data)
{
stonith_t *st = async_fence_data.st;
int call_id = 0;
int rc = stonith_api_connect_retry(st, async_fence_data.name, 10);
if (rc != pcmk_ok) {
fprintf(stderr, "Could not connect to fencer: %s\n", pcmk_strerror(rc));
g_main_loop_quit(mainloop);
return TRUE;
}
st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, notify_callback);
call_id = st->cmds->fence(st,
st_opt_allow_suicide,
async_fence_data.target,
async_fence_data.action,
async_fence_data.timeout, async_fence_data.tolerance);
if (call_id < 0) {
g_main_loop_quit(mainloop);
return TRUE;
}
st->cmds->register_callback(st,
call_id,
async_fence_data.timeout,
st_opt_timeout_updates, NULL, "callback", fence_callback);
return TRUE;
}
static int
mainloop_fencing(stonith_t * st, const char *target, const char *action, int timeout, int tolerance)
{
crm_trigger_t *trig;
async_fence_data.st = st;
async_fence_data.target = target;
async_fence_data.action = action;
async_fence_data.timeout = timeout;
async_fence_data.tolerance = tolerance;
async_fence_data.rc = -1;
trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
mainloop_set_trigger(trig);
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
return async_fence_data.rc;
}
static int
handle_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices, bool added)
{
char *node = NULL;
char *pattern = NULL;
char *name = NULL;
char *value = strchr(target, '=');
/* Determine if targeting by attribute, node name pattern or node name */
if (value != NULL) {
name = target;
*value++ = '\0';
} else if (*target == '@') {
pattern = target + 1;
} else {
node = target;
}
/* Register or unregister level as appropriate */
if (added) {
return st->cmds->register_level_full(st, st_opts, node, pattern,
name, value, fence_level,
devices);
}
return st->cmds->remove_level_full(st, st_opts, node, pattern,
name, value, fence_level);
}
static int
handle_history(stonith_t *st, const char *target, int timeout, int quiet,
int verbose, int cleanup, int broadcast, pcmk__output_t *out)
{
stonith_history_t *history = NULL, *hp, *latest = NULL;
int rc = 0;
if (!quiet) {
if (cleanup) {
out->info(out, "cleaning up fencing-history%s%s",
target ? " for node " : "", target ? target : "");
}
if (broadcast) {
out->info(out, "gather fencing-history from all nodes");
}
}
rc = st->cmds->history(st, st_opts | (cleanup?st_opt_cleanup:0) |
(broadcast?st_opt_broadcast:0),
(safe_str_eq(target, "*")? NULL : target),
&history, timeout);
out->begin_list(out, "Fencing history", "event", "events");
for (hp = history; hp; hp = hp->next) {
if (hp->state == st_done) {
latest = hp;
}
if (quiet || !verbose) {
continue;
}
out->message(out, "stonith-event", hp, 1);
}
if (latest) {
if (quiet && out->supports_quiet) {
out->info(out, "%lld", (long long) latest->completed);
} else if (!verbose) { // already printed if verbose
out->message(out, "stonith-event", latest, 0);
}
}
out->end_list(out);
stonith_history_free(history);
return rc;
}
static int
validate(stonith_t *st, const char *agent, const char *id,
stonith_key_value_t *params, int timeout, int quiet,
pcmk__output_t *out)
{
int rc = 1;
char *output = NULL;
char *error_output = NULL;
rc = st->cmds->validate(st, st_opt_sync_call, id, NULL, agent, params,
timeout, &output, &error_output);
if (quiet) {
return rc;
}
out->message(out, "validate", agent, id, output, error_output, rc);
return rc;
}
static GOptionContext *
build_arg_context(pcmk__common_args_t *args) {
GOptionContext *context = NULL;
GOptionGroup *defn_group, *query_group, *fence_group, *addl_group;
GOptionGroup *main_group;
GOptionEntry extra_prog_entries[] = {
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
"Be less descriptive in output.",
NULL },
{ NULL }
};
context = pcmk__build_arg_context(args, "text (default), html, xml");
/* Add the -q option, which cannot be part of the globally supported options
* because some tools use that flag for something else.
*/
main_group = g_option_context_get_main_group(context);
g_option_group_add_entries(main_group, extra_prog_entries);
defn_group = g_option_group_new("definition", "Device Definition Commands:",
"Show device definition help", NULL, NULL);
g_option_group_add_entries(defn_group, defn_entries);
g_option_context_add_group(context, defn_group);
query_group = g_option_group_new("queries", "Queries:", "Show query help", NULL, NULL);
g_option_group_add_entries(query_group, query_entries);
g_option_context_add_group(context, query_group);
fence_group = g_option_group_new("fence", "Fencing Commands:", "Show fence help", NULL, NULL);
g_option_group_add_entries(fence_group, fence_entries);
g_option_context_add_group(context, fence_group);
addl_group = g_option_group_new("additional", "Additional Options:", "Show additional options", NULL, NULL);
g_option_group_add_entries(addl_group, addl_entries);
g_option_context_add_group(context, addl_group);
return context;
}
int
main(int argc, char **argv)
{
int rc = 0;
bool no_connect = false;
bool required_agent = false;
char *target = NULL;
char *lists = NULL;
const char *device = NULL;
crm_exit_t exit_code = CRM_EX_OK;
stonith_t *st = NULL;
stonith_key_value_t *dIter = NULL;
pcmk__output_t *out = NULL;
pcmk__common_args_t *args = calloc(1, sizeof(pcmk__common_args_t));
GError *error = NULL;
GOptionContext *context = NULL;
- gchar **argv_copy = NULL;
+ gchar **processed_args = NULL;
if (args == NULL) {
crm_exit(crm_errno2exit(-ENOMEM));
}
args->summary = strdup("stonith_admin - Access the Pacemaker fencing API");
context = build_arg_context(args);
pcmk__register_formats(context, formats);
crm_log_cli_init("stonith_admin");
async_fence_data.name = strdup(crm_system_name);
- /* g_option_context_parse will remove items from argv as it proceses them,
- * but we also need the list of arguments to pass to pcmk__output_new.
- * So make a copy here and free it when the program quits.
- */
- argv_copy = g_strdupv(argv);
+ processed_args = pcmk__cmdline_preproc(argc, argv, "adehilorstvBCDFHQRTU");
- if (!g_option_context_parse(context, &argc, &argv, &error)) {
+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
}
for (int i = 0; i < options.verbose; i++) {
crm_bump_log_level(argc, argv);
}
- rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv_copy);
+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
if (rc != 0) {
fprintf(stderr, "Error creating output format %s: %s\n", args->output_ty, pcmk_strerror(rc));
exit_code = CRM_EX_ERROR;
goto done;
}
stonith__register_messages(out);
if (args->version) {
out->version(out, false);
goto done;
}
if (options.validate_cfg) {
required_agent = true;
no_connect = true;
action = 'K';
}
if (options.installed) {
no_connect = true;
action = 'I';
}
if (options.registered) {
action = 'L';
}
if (options.register_dev != NULL) {
required_agent = true;
action = 'R';
device = options.register_dev;
}
if (options.query != NULL) {
action = 'Q';
device = options.query;
}
if (options.unregister_dev != NULL) {
action = 'D';
device = options.unregister_dev;
}
if (options.targets != NULL) {
action = 's';
device = options.targets;
}
if (options.terminate != NULL) {
action = 'L';
target = options.terminate;
}
if (options.metadata) {
no_connect = true;
required_agent = true;
action = 'M';
}
if (options.reboot_host != NULL) {
no_connect = true;
action = 'B';
target = options.reboot_host;
- crm_log_args(argc, argv_copy);
+ crm_log_args(argc, argv);
}
if (options.fence_host != NULL) {
no_connect = true;
action = 'F';
target = options.fence_host;
- crm_log_args(argc, argv_copy);
+ crm_log_args(argc, argv);
}
if (options.unfence_host != NULL) {
no_connect = true;
action = 'U';
target = options.unfence_host;
- crm_log_args(argc, argv_copy);
+ crm_log_args(argc, argv);
}
if (options.confirm_host != NULL) {
action = 'C';
target = options.confirm_host;
- crm_log_args(argc, argv_copy);
+ crm_log_args(argc, argv);
}
if (options.last_fenced != NULL) {
action = 'h';
target = options.last_fenced;
}
if (options.history != NULL) {
action = 'H';
target = options.history;
}
if (options.register_level != NULL) {
action = 'r';
target = options.register_level;
}
if (options.unregister_level != NULL) {
action = 'd';
target = options.unregister_level;
}
if (optind > argc || action == 0) {
out->err(out, "%s", g_option_context_get_help(context, TRUE, NULL));
exit_code = CRM_EX_USAGE;
goto done;
}
if (required_agent && options.agent == NULL) {
out->err(out, "Please specify an agent to query using -a,--agent [value]");
out->err(out, "%s", g_option_context_get_help(context, TRUE, NULL));
exit_code = CRM_EX_USAGE;
goto done;
}
st = stonith_api_new();
if (!no_connect) {
rc = st->cmds->connect(st, async_fence_data.name, NULL);
if (rc < 0) {
out->err(out, "Could not connect to fencer: %s", pcmk_strerror(rc));
exit_code = CRM_EX_DISCONNECT;
goto done;
}
}
switch (action) {
case 'I':
rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &options.devices, options.timeout);
if (rc < 0) {
out->err(out, "Failed to list installed devices: %s", pcmk_strerror(rc));
break;
}
out->begin_list(out, "Installed fence devices", "fence device", "fence devices");
for (dIter = options.devices; dIter; dIter = dIter->next) {
out->list_item(out, "device", dIter->value);
}
out->end_list(out);
rc = 0;
stonith_key_value_freeall(options.devices, 1, 1);
break;
case 'L':
rc = st->cmds->query(st, st_opts, target, &options.devices, options.timeout);
if (rc < 0) {
out->err(out, "Failed to list registered devices: %s", pcmk_strerror(rc));
break;
}
out->begin_list(out, "Registered fence devices", "fence device", "fence devices");
for (dIter = options.devices; dIter; dIter = dIter->next) {
out->list_item(out, "device", dIter->value);
}
out->end_list(out);
rc = 0;
stonith_key_value_freeall(options.devices, 1, 1);
break;
case 'Q':
rc = st->cmds->monitor(st, st_opts, device, options.timeout);
if (rc < 0) {
rc = st->cmds->list(st, st_opts, device, NULL, options.timeout);
}
break;
case 's':
rc = st->cmds->list(st, st_opts, device, &lists, options.timeout);
if (rc == 0) {
GList *targets = stonith__parse_targets(lists);
out->begin_list(out, "Fence targets", "fence target", "fence targets");
while (targets != NULL) {
out->list_item(out, NULL, (const char *) targets->data);
targets = targets->next;
}
out->end_list(out);
free(lists);
} else if (rc != 0) {
out->err(out, "List command returned error. rc : %d", rc);
}
break;
case 'R':
rc = st->cmds->register_device(st, st_opts, device, NULL, options.agent,
options.params);
break;
case 'D':
rc = st->cmds->remove_device(st, st_opts, device);
break;
case 'd':
case 'r':
rc = handle_level(st, target, options.fence_level, options.devices, action == 'r');
break;
case 'M':
{
char *buffer = NULL;
rc = st->cmds->metadata(st, st_opt_sync_call, options.agent, NULL,
&buffer, options.timeout);
if (rc == pcmk_ok) {
out->output_xml(out, "metadata", buffer);
}
free(buffer);
}
break;
case 'C':
rc = st->cmds->confirm(st, st_opts, target);
break;
case 'B':
rc = mainloop_fencing(st, target, "reboot", options.timeout, options.tolerance);
break;
case 'F':
rc = mainloop_fencing(st, target, "off", options.timeout, options.tolerance);
break;
case 'U':
rc = mainloop_fencing(st, target, "on", options.timeout, options.tolerance);
break;
case 'h':
{
time_t when = 0;
if(options.as_nodeid) {
uint32_t nodeid = atol(target);
when = stonith_api_time(nodeid, NULL, FALSE);
} else {
when = stonith_api_time(0, target, FALSE);
}
out->message(out, "last-fenced", target, when);
}
break;
case 'H':
rc = handle_history(st, target, options.timeout, args->quiet,
options.verbose, options.cleanup,
options.broadcast, out);
break;
case 'K':
device = (options.devices ? options.devices->key : NULL);
rc = validate(st, options.agent, device, options.params,
options.timeout, args->quiet, out);
break;
}
crm_info("Command returned: %s (%d)", pcmk_strerror(rc), rc);
exit_code = crm_errno2exit(rc);
done:
- g_strfreev(argv_copy);
+ g_strfreev(processed_args);
g_option_context_free(context);
if (out != NULL) {
out->finish(out, exit_code, true, NULL);
pcmk__output_free(out);
}
free(async_fence_data.name);
stonith_key_value_freeall(options.params, 1, 1);
if (st != NULL) {
st->cmds->disconnect(st);
stonith_api_delete(st);
}
return exit_code;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Oct 16, 3:27 PM (1 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2536575
Default Alt Text
(35 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment