diff options
Diffstat (limited to 'tools/lvmcmdline.c')
-rw-r--r-- | tools/lvmcmdline.c | 346 |
1 files changed, 241 insertions, 105 deletions
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index 9a4deb7d5..cfce57182 100644 --- a/tools/lvmcmdline.c +++ b/tools/lvmcmdline.c @@ -49,6 +49,11 @@ extern char *optarg; # define OPTIND_INIT 1 #endif +#if 0 +#include "command-lines-count.h" /* #define COMMAND_COUNT, generated from command-lines.in */ +#endif +#define COMMAND_COUNT 128 + /* * Table of valid switches */ @@ -58,12 +63,32 @@ static struct arg_props _arg_props[ARG_COUNT + 1] = { #undef arg }; +/* + * Table of valid command names + */ +#define MAX_COMMAND_NAMES 64 +struct command_name { + const char *name; + const char *desc; + unsigned int flags; +}; + +struct command_name command_names[MAX_COMMAND_NAMES] = { +#define xx(a, b, c...) { # a, b, c } +#include "commands.h" +#undef xx +} + +/* + * Table of valid command lines + */ +static struct command commands[COMMAND_COUNT]; static struct cmdline_context _cmdline; /* Command line args */ unsigned arg_count(const struct cmd_context *cmd, int a) { - return cmd->arg_values ? cmd->arg_values[a].count : 0; + return cmd->opt_arg_values ? cmd->opt_arg_values[a].count : 0; } unsigned grouped_arg_count(const struct arg_values *av, int a) @@ -182,12 +207,12 @@ const char *arg_long_option_name(int a) const char *arg_value(const struct cmd_context *cmd, int a) { - return cmd->arg_values ? cmd->arg_values[a].value : NULL; + return cmd->opt_arg_values ? cmd->opt_arg_values[a].value : NULL; } const char *arg_str_value(const struct cmd_context *cmd, int a, const char *def) { - return arg_is_set(cmd, a) ? cmd->arg_values[a].value : def; + return arg_is_set(cmd, a) ? cmd->opt_arg_values[a].value : def; } const char *grouped_arg_str_value(const struct arg_values *av, int a, const char *def) @@ -217,44 +242,44 @@ int32_t first_grouped_arg_int_value(const struct cmd_context *cmd, int a, const int32_t arg_int_value(const struct cmd_context *cmd, int a, const int32_t def) { return (_cmdline.arg_props[a].flags & ARG_GROUPABLE) ? - first_grouped_arg_int_value(cmd, a, def) : (arg_is_set(cmd, a) ? cmd->arg_values[a].i_value : def); + first_grouped_arg_int_value(cmd, a, def) : (arg_is_set(cmd, a) ? cmd->opt_arg_values[a].i_value : def); } uint32_t arg_uint_value(const struct cmd_context *cmd, int a, const uint32_t def) { - return arg_is_set(cmd, a) ? cmd->arg_values[a].ui_value : def; + return arg_is_set(cmd, a) ? cmd->opt_arg_values[a].ui_value : def; } int64_t arg_int64_value(const struct cmd_context *cmd, int a, const int64_t def) { - return arg_is_set(cmd, a) ? cmd->arg_values[a].i64_value : def; + return arg_is_set(cmd, a) ? cmd->opt_arg_values[a].i64_value : def; } uint64_t arg_uint64_value(const struct cmd_context *cmd, int a, const uint64_t def) { - return arg_is_set(cmd, a) ? cmd->arg_values[a].ui64_value : def; + return arg_is_set(cmd, a) ? cmd->opt_arg_values[a].ui64_value : def; } /* No longer used. const void *arg_ptr_value(struct cmd_context *cmd, int a, const void *def) { - return arg_is_set(cmd, a) ? cmd->arg_values[a].ptr : def; + return arg_is_set(cmd, a) ? cmd->opt_arg_values[a].ptr : def; } */ sign_t arg_sign_value(const struct cmd_context *cmd, int a, const sign_t def) { - return arg_is_set(cmd, a) ? cmd->arg_values[a].sign : def; + return arg_is_set(cmd, a) ? cmd->opt_arg_values[a].sign : def; } percent_type_t arg_percent_value(const struct cmd_context *cmd, int a, const percent_type_t def) { - return arg_is_set(cmd, a) ? cmd->arg_values[a].percent : def; + return arg_is_set(cmd, a) ? cmd->opt_arg_values[a].percent : def; } int arg_count_increment(struct cmd_context *cmd, int a) { - return cmd->arg_values[a].count++; + return cmd->opt_arg_values[a].count++; } int yes_no_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) @@ -709,104 +734,227 @@ int metadatacopies_arg(struct cmd_context *cmd, struct arg_values *av) return int_arg(cmd, av); } -static void __alloc(int size) +static struct command_name *_find_command_name(char *name) { - if (!(_cmdline.commands = dm_realloc(_cmdline.commands, sizeof(*_cmdline.commands) * size))) { - log_fatal("Couldn't allocate memory."); - exit(ECMD_FAILED); + int i; + + for (i = 0; i < MAX_COMMAND_NAMES; i++) { + if (!command_names[i].name) + break; + if (!strcmp(command_names[i].name, name)) + return &command_names[i]; } + return NULL; +} - _cmdline.commands_size = size; +void _define_commands(void) +{ +/* command-lines.h defines command[] structs, generated from command-lines.in */ +#include "command-lines.h" /* generated from command-lines.in */ } -static void _alloc_command(void) +void lvm_register_commands(void) { - if (!_cmdline.commands_size) - __alloc(32); + struct command_name *cname; + int i; + + _define_commands(); + + _cmdline.commands = &commands; + _cmdline.num_commands = COMMAND_COUNT; - if (_cmdline.commands_size <= _cmdline.num_commands) - __alloc(2 * _cmdline.commands_size); + for (i = 0; i < COMMAND_COUNT; i++) { + if (!(cname = _find_command_name(commands[i].name))) + log_error(INTERNAL_ERROR "Failed to find command name %s.", commands[i].name); + commands[i].flags = cname->flags; + } } -static void _create_new_command(const char *name, command_fn command, - unsigned flags, - const char *desc, const char *usagestr, - int nargs, int *args) +/* + * Match what the user typed with a one specific command definition/prototype + * from commands[]. If nothing matches, it's not a valid command. The match + * is based on command name, required opt args and required pos args. + * + * Find an entry in the commands array that matches based the arg values. + * + * If the cmd has opt or pos args set that are not accepted by command, + * we can: silently ignore them, warn they are not being used, or fail. + * Default should probably be to warn and continue. + * + * For each command[i], check how many required opt/pos args cmd matches. + * Save the command[i] that matches the most. + * + * commands[i].cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT means + * any one item from commands[i].required_opt_args needs to be + * set to match. + * + * required_pos_args[0].flags & ARG_DEF_TYPE_SELECT means + * cmd->pos_arg_values[0] can be NULL if arg_is_set(select_ARG) + */ + +static int _opt_equivalent_is_set(struct cmd_context *cmd, int opt) { - struct command *nc; + if ((opt == mirrorlog_ARG) && arg_is_set(cmd, corelog_ARG, NULL)) + return 1; + + if ((opt == resizeable_ARG) && arg_is_set(cmd, resizable_ARG, NULL)) + return 1; + + if ((opt == allocatable_ARG) && arg_is_set(cmd, allocation_ARG, NULL)) + return 1; - _alloc_command(); + if ((opt == resizeable_ARG) && arg_is_set(cmd, allocation_ARG, NULL)) + return 1; - nc = _cmdline.commands + _cmdline.num_commands++; + if ((opt == activate_ARG) && arg_is_set(cmd, available_ARG, NULL)) + return 1; + + if ((opt == rebuild_ARG) && arg_is_set(cmd, raidrebuild_ARG, NULL)) + return 1; + + if ((opt == syncaction_ARG) && arg_is_set(cmd, raidsyncaction_ARG, NULL)) + return 1; + + if ((opt == writemostly_ARG) && arg_is_set(cmd, raidwritemostly_ARG, NULL)) + return 1; - nc->name = name; - nc->desc = desc; - nc->usage = usagestr; - nc->fn = command; - nc->flags = flags; - nc->num_args = nargs; - nc->valid_args = args; + if ((opt == minrecoveryrate_ARG) && arg_is_set(cmd, raidminrecoveryrate_ARG, NULL)) + return 1; + + if ((opt == maxrecoveryrate_ARG) && arg_is_set(cmd, raidmaxrecoveryrate_ARG, NULL)) + return 1; + + if ((opt == writebehind_ARG) && arg_is_set(cmd, raidwritebehind_ARG, NULL)) + return 1; + + return 0; } -static void _register_command(const char *name, command_fn fn, const char *desc, - unsigned flags, const char *usagestr, ...) +static int _command_required_opt_matches(struct cmd_context *cmd, int ci, int ro) { - int nargs = 0, i; - int *args; - va_list ap; - - /* count how many arguments we have */ - va_start(ap, usagestr); - while (va_arg(ap, int) >= 0) - nargs++; - va_end(ap); + if (arg_is_set(cmd, commands[ci].required_opt_args[ro].opt, NULL)) + return 1; - /* allocate space for them */ - if (!(args = dm_malloc(sizeof(*args) * nargs))) { - log_fatal("Out of memory."); - exit(ECMD_FAILED); + /* + * For some commands, --size and --extents are interchanable, + * but command[] definitions use only --size. + */ + if ((commands[ci].required_opt_args[ro].opt == size_ARG) && arg_is_set(cmd, extents_ARG, NULL)) { + if (!strcmp(commands[i].name, "lvcreate") || + !strcmp(commands[i].name, "lvresize") + !strcmp(commands[i].name, "lvextend") + !strcmp(commands[i].name, "lvreduce")) + return 1; } - /* fill them in */ - va_start(ap, usagestr); - for (i = 0; i < nargs; i++) - args[i] = va_arg(ap, int); - va_end(ap); + /* TODO: for lvmconfig, recognize --type in place of --typeconfig? */ - /* enter the command in the register */ - _create_new_command(name, fn, flags, desc, usagestr, nargs, args); + if (_opt_equivalent_is_set(cmd, commands[ci].required_opt_args[ro].opt)) + return 1; } -void lvm_register_commands(void) +static int _command_required_pos_matches(struct cmd_context *cmd, int ci, int rp) { -#define xx(a, b, c, d...) _register_command(# a, a, b, c, ## d, \ - driverloaded_ARG, \ - debug_ARG, help_ARG, help2_ARG, \ - version_ARG, verbose_ARG, \ - yes_ARG, \ - quiet_ARG, config_ARG, \ - commandprofile_ARG, \ - profile_ARG, -1); -#include "commands.h" -#undef xx + if (cmd->pos_arg_values[rp]) { + /* FIXME: can we match object type better than just checking something exists? */ + /* Some cases could be validated by looking at defs.types and at the value. */ + return 1; + } + + /* + * If Select is specified as a pos arg, then that pos arg can be + * empty if --select is used. + */ + if ((commands[ci].required_pos_args[rp].def.types & ARG_DEF_TYPE_SELECT) && + arg_is_set(cmd, select_ARG, NULL)) + return 1; + + return 0; } -static struct command *_find_command(const char *name) +static struct command *_find_command(struct cmd_context *cmd, const char *path) { + const char *name; + int ro, rp; + int found = 0; int i; - const char *base; - base = last_path_component(name); + name = last_path_component(path); - for (i = 0; i < _cmdline.num_commands; i++) { - if (!strcmp(base, _cmdline.commands[i].name)) - break; + for (i = 0; i < COMMAND_COUNT; i++) { + if (strcmp(name, commands[i].name)) + continue; + + match_count = 1; /* for command name matching */ + mismatch_count = 0; + + /* match required_opt_args */ + + for (ro = 0; ro < commands[i].ro_count; ro++) { + if (_command_required_opt_matches(cmd, i, ro)) + match_count++; + else + mismatch_count++; + } + + /* + * One item in required_opt_args must be set for + * a match, and the rest are optional (don't count + * as mismatches). + */ + if (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) { + if (match_count >= 2) { + match_count = 2; + mismatch_count = 0; + } + } + + /* match required_pos_args */ + + for (rp = 0; rp < commands[i].rp_count; rp++) { + if (_command_required_pos_matches(cmd, i, rp)) + match_count++; + else + mismatch_count++; + } + + if (mismatch_count) { + /* save i/match_count for "closest" command that doesn't match */ + if (!closest_count || (match_count > closest_count)) { + closest_i = i; + closest_count = match_count; + } + continue; + } + + if (!best_match_count || (match_count > best_match_count)) { + best_match_i = i; + best_match_count = match_count; + } } - if (i >= _cmdline.num_commands) - return 0; + if (!best_match_count) { + /* nothing matches */ + /* TODO: report closest matching command and report missing required opt/pos args for that? */ + log_error("Failed to find a matching command definition."); + if (closest_count) { + log_warn("Closest command usage is:"); + log_warn("%s", commands[closest_i].usage); + } + return NULL; + } + + /* + * Check if all arg_is_set values from cmd are accepted as + * optional_opt_args, and warn (or fail per config?) if options are set + * in cmd that are not accepted by the chosen command[i]. + * + * Same for pos args. + */ - return _cmdline.commands + i; + log_debug("matched command usage: %.80s ...", commands[best_match_i].usage); + + return &commands[best_match_i]; } static void _short_usage(const char *name) @@ -816,14 +964,16 @@ static void _short_usage(const char *name) static int _usage(const char *name) { - struct command *com = _find_command(name); + struct command_name *cname = _find_command_name(name); - if (!com) { + if (!cname) { log_print("%s: no such command.", name); return 0; } - log_print("%s: %s\n\n%s", com->name, com->desc, com->usage); + /* FIXME: print usage strings from matching commands[] entries? */ + + log_print("%s: %s", cname->name, cname->desc); return 1; } @@ -890,7 +1040,7 @@ static int _process_command_line(struct cmd_context *cmd, int *argc, struct arg_values *av; struct arg_value_group_list *current_group = NULL; - if (!(cmd->arg_values = dm_pool_zalloc(cmd->mem, sizeof(*cmd->arg_values) * ARG_COUNT))) { + if (!(cmd->opt_arg_values = dm_pool_zalloc(cmd->mem, sizeof(*cmd->opt_arg_values) * ARG_COUNT))) { log_fatal("Unable to allocate memory for command line arguments."); return 0; } @@ -917,7 +1067,7 @@ static int _process_command_line(struct cmd_context *cmd, int *argc, a = _cmdline.arg_props + arg; - av = &cmd->arg_values[arg]; + av = &cmd->opt_arg_values[arg]; if (a->flags & ARG_GROUPABLE) { /* @@ -930,7 +1080,7 @@ static int _process_command_line(struct cmd_context *cmd, int *argc, (current_group->arg_values[arg].count && !(a->flags & ARG_COUNTABLE)) || (current_group->prio < a->prio)) { /* FIXME Reduce size including only groupable args */ - if (!(current_group = dm_pool_zalloc(cmd->mem, sizeof(struct arg_value_group_list) + sizeof(*cmd->arg_values) * ARG_COUNT))) { + if (!(current_group = dm_pool_zalloc(cmd->mem, sizeof(struct arg_value_group_list) + sizeof(*cmd->opt_arg_values) * ARG_COUNT))) { log_fatal("Unable to allocate memory for command line arguments."); return 0; } @@ -1002,12 +1152,12 @@ static int _merge_synonym(struct cmd_context *cmd, int oldarg, int newarg) /* Not groupable? */ if (!(_cmdline.arg_props[oldarg].flags & ARG_GROUPABLE)) { if (arg_is_set(cmd, oldarg)) - _copy_arg_values(cmd->arg_values, oldarg, newarg); + _copy_arg_values(cmd->opt_arg_values, oldarg, newarg); return 1; } if (arg_is_set(cmd, oldarg)) - cmd->arg_values[newarg].count = cmd->arg_values[oldarg].count; + cmd->opt_arg_values[newarg].count = cmd->opt_arg_values[oldarg].count; /* Groupable */ dm_list_iterate_items(current_group, &cmd->arg_value_groups) { @@ -1431,7 +1581,7 @@ static int _prepare_profiles(struct cmd_context *cmd) log_debug(_setting_global_profile_msg, _command_profile_source_name, profile->name); cmd->profile_params->global_command_profile = profile; - if (!cmd->arg_values) + if (!cmd->opt_arg_values) cmd->profile_params->shell_profile = profile; } @@ -1553,14 +1703,14 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) log_debug("Parsing: %s", cmd->cmd_line); - if (!(cmd->command = _find_command(argv[0]))) - return ENO_SUCH_CMD; - if (!_process_command_line(cmd, &argc, &argv)) { log_error("Error during parsing of command line."); return EINVALID_CMD_LINE; } + if (!(cmd->command = _find_command(cmd, argv[0]))) + return ENO_SUCH_CMD; + set_cmd_name(cmd->command->name); if (arg_is_set(cmd, backgroundfork_ARG)) { @@ -2040,23 +2190,8 @@ struct cmd_context *init_lvm(unsigned set_connections, unsigned set_filters) return cmd; } -static void _fin_commands(void) -{ - int i; - - for (i = 0; i < _cmdline.num_commands; i++) - dm_free(_cmdline.commands[i].valid_args); - - dm_free(_cmdline.commands); - - _cmdline.commands = NULL; - _cmdline.num_commands = 0; - _cmdline.commands_size = 0; -} - void lvm_fin(struct cmd_context *cmd) { - _fin_commands(); destroy_toolcontext(cmd); udev_fin_library_context(); } @@ -2202,7 +2337,8 @@ int lvm2_main(int argc, char **argv) if (!(cmd = init_lvm(0, 0))) return -1; - cmd->argv = argv; + cmd->pos_arg_values = argv; + lvm_register_commands(); if (_lvm1_fallback(cmd)) { |