1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-10-03 01:44:19 +03:00

Compare commits

..

1 Commits

Author SHA1 Message Date
David Teigland
45e5f204be commands: new method for defining commands
. Define a prototype for every lvm command.
. Match every user command with one definition.
. Generate help text and man pages from them.

The new file command-lines.in defines a prototype for every
unique lvm command.  A unique lvm command is a unique
combination of: command name + required option args +
required positional args.  Each of these prototypes also
includes the optional option args and optional positional
args that the command will accept, a description, and a
unique string ID for the definition.  Any valid command
will match one of the prototypes.

Here's an example of the lvresize command definitions from
command-lines.in, there are three unique lvresize commands:

lvresize --size SizeMB LV
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync, --reportformat String, --resizefs,
--stripes Number, --stripesize SizeKB, --poolmetadatasize SizeMB
OP: PV ...
ID: lvresize_by_size
DESC: Resize an LV by a specified size.

lvresize LV PV ...
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --resizefs, --stripes Number, --stripesize SizeKB
ID: lvresize_by_pv
DESC: Resize an LV by specified PV extents.
FLAGS: SECONDARY_SYNTAX

lvresize --poolmetadatasize SizeMB LV_thinpool
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --stripes Number, --stripesize SizeKB
OP: PV ...
ID: lvresize_pool_metadata_by_size
DESC: Resize a pool metadata SubLV by a specified size.

The three commands have separate definitions because they have
different required parameters.  Required parameters are specified
on the first line of the definition.  Optional options are
listed after OO, and optional positional args are listed after OP.

This data is used to generate corresponding command definition
structures for lvm in command-lines.h.  usage/help output is also
auto generated, so it is always in sync with the definitions.

Example of the corresponding generated structure in
command-lines.h for the first lvresize prototype
(these structures are never edited directly):

commands[83].name = "lvresize";
commands[83].command_line_id = "lvresize_by_size";
commands[83].command_line_enum = lvresize_by_size_CMD;
commands[83].fn = lvresize;
commands[83].ro_count = 1;
commands[83].rp_count = 1;
commands[83].oo_count = 22;
commands[83].op_count = 1;
commands[83].cmd_flags = 0;
commands[83].desc = "DESC: Resize an LV by a specified size.";
commands[83].usage = "lvresize --size Number[m|unit] LV"
" [ --resizefs, --poolmetadatasize Number[m|unit], COMMON_OPTIONS ]"
" [ PV ... ]";
commands[83].usage_common =
" [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit, --nosync, --reportformat String, --autobackup y|n, --stripes Number, --stripesize Number[k|unit], --nofsck, --commandprofile String, --config String, --debug, --driverloaded y|n, --help, --profile String, --quiet, --verbose, --version, --yes, --test, --force, --noudevsync ]";
commands[83].required_opt_args[0].opt = size_ARG;
commands[83].required_opt_args[0].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].required_pos_args[0].pos = 1;
commands[83].required_pos_args[0].def.val_bits = val_enum_to_bit(lv_VAL);
commands[83].optional_opt_args[0].opt = commandprofile_ARG;
commands[83].optional_opt_args[0].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[1].opt = config_ARG;
commands[83].optional_opt_args[1].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[2].opt = debug_ARG;
commands[83].optional_opt_args[3].opt = driverloaded_ARG;
commands[83].optional_opt_args[3].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[4].opt = help_ARG;
commands[83].optional_opt_args[5].opt = profile_ARG;
commands[83].optional_opt_args[5].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[6].opt = quiet_ARG;
commands[83].optional_opt_args[7].opt = verbose_ARG;
commands[83].optional_opt_args[8].opt = version_ARG;
commands[83].optional_opt_args[9].opt = yes_ARG;
commands[83].optional_opt_args[10].opt = test_ARG;
commands[83].optional_opt_args[11].opt = alloc_ARG;
commands[83].optional_opt_args[11].def.val_bits = val_enum_to_bit(alloc_VAL);
commands[83].optional_opt_args[12].opt = autobackup_ARG;
commands[83].optional_opt_args[12].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[13].opt = force_ARG;
commands[83].optional_opt_args[14].opt = nofsck_ARG;
commands[83].optional_opt_args[15].opt = nosync_ARG;
commands[83].optional_opt_args[16].opt = noudevsync_ARG;
commands[83].optional_opt_args[17].opt = reportformat_ARG;
commands[83].optional_opt_args[17].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[18].opt = resizefs_ARG;
commands[83].optional_opt_args[19].opt = stripes_ARG;
commands[83].optional_opt_args[19].def.val_bits = val_enum_to_bit(number_VAL);
commands[83].optional_opt_args[20].opt = stripesize_ARG;
commands[83].optional_opt_args[20].def.val_bits = val_enum_to_bit(sizekb_VAL);
commands[83].optional_opt_args[21].opt = poolmetadatasize_ARG;
commands[83].optional_opt_args[21].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].optional_pos_args[0].pos = 2;
commands[83].optional_pos_args[0].def.val_bits = val_enum_to_bit(pv_VAL);
commands[83].optional_pos_args[0].def.flags = ARG_DEF_FLAG_MAY_REPEAT;

Every user-entered command is compared against the set of
command structures, and matched with one.  An error is
reported if an entered command does not have the required
parameters for any definition.  The closest match is printed
as a suggestion, and running lvresize --help will display
the usage for each possible lvresize command.

The prototype syntax used for help/man output includes
required --option and positional args on the first line,
and optional --option and positional args enclosed in [ ]
on subsequent lines.

  command_name <required_opt_args> <required_pos_args>
          [ <optional_opt_args> ]
          [ <optional_pos_args> ]

$ lvresize --help
  lvresize - Resize a logical volume

  Resize an LV by a specified size.
  lvresize --size Number[m|unit] LV
        [ --resizefs,
          --poolmetadatasize Number[m|unit],
          COMMON_OPTIONS ]
        [ PV ... ]

  Resize a pool metadata SubLV by a specified size.
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool
        [ COMMON_OPTIONS ]
        [ PV ... ]

  Common options:
        [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit,
          --nosync,
          --reportformat String,
          --autobackup y|n,
          --stripes Number,
          --stripesize Number[k|unit],
          --nofsck,
          --commandprofile String,
          --config String,
          --debug,
          --driverloaded y|n,
          --help,
          --profile String,
          --quiet,
          --verbose,
          --version,
          --yes,
          --test,
          --force,
          --noudevsync ]

  (Use --help --help for usage notes.)

$ lvresize --poolmetadatasize 4
  Failed to find a matching command definition.
  Closest command usage is:
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool

Command definitions that are not to be advertised/suggested
have the flag SECONDARY_SYNTAX.  These commands will not be
printed in the normal help output.

Man page prototypes are also generated from the same original
command definitions, and are always in sync with the code
and help text.

Very early in command execution, a matching command definition
is found.  lvm then knows the operation being done, and that
the provided args conform to the definition.  This will allow
lots of ad hoc checking/validation to be removed throughout
the code.

Each command definition can also be routed to a specific
function to implement it.  The function is associated with
an enum value for the command definition (generated from
the ID string.)  These per-command-definition implementation
functions have not yet been created, so all commands
currently fall back to the existing per-command-name
implementation functions.

Using per-command-definition functions will allow lots of
code to be removed which tries to figure out what the
command is meant to do.  This is currently based on ad hoc
and complicated option analysis.  When using the new
functions, what the command is doing is already known
from the associated command definition.

So, this first phase validates every user-entered command
against the set of command prototypes, then calls the existing
implementation.  The second phase can associate an implementation
function with each definition, and take further advantage of the
known operation to avoid the complicated option analysis.
2016-10-26 15:01:23 -05:00
17 changed files with 1287 additions and 8759 deletions

View File

@@ -56,7 +56,7 @@ fail lvcreate -l 1 --cachepool pool8 $vg
# no size specified
invalid lvcreate --cachepool pool $vg 2>&1 | tee err
grep "specify either size or extents" err
# grep "specify either size or extents" err
# Check nothing has been created yet
check vg_field $vg lv_count 0

View File

@@ -23,7 +23,6 @@ lvcreate -l 1 -n lv1 $vg "$dev1"
invalid vgextend
# --metadatacopies => use --pvmetadatacopies
invalid vgextend --metadatacopies 3 $vg "$dev1" 2>&1 | tee out
grep -- "use --pvmetadatacopies" out
# VG name should exist
fail vgextend --restoremissing $vg-invalid "$dev1"

View File

@@ -76,6 +76,7 @@ SOURCES2 =\
TARGETS =\
.commands \
command-lines.h \
liblvm2cmd.a \
lvm
@@ -99,7 +100,8 @@ LIB_VERSION = $(LIB_VERSION_LVM)
CLEAN_TARGETS = liblvm2cmd.$(LIB_SUFFIX) $(TARGETS_DM) \
liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION) lvm-static.o \
liblvm2cmd-static.a dmsetup.static lvm.static \
$(LDDEPS) .exported_symbols_generated
$(LDDEPS) .exported_symbols_generated \
ccmd command-lines.h command-lines-count.h
ifeq ("@CMDLIB@", "yes")
TARGETS += liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION)
@@ -171,6 +173,13 @@ liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION): liblvm2cmd.$(LIB_SUFFIX)
$(CC) -E -P $(srcdir)/cmdnames.h 2> /dev/null | \
egrep -v '^ *(|#.*|config|devtypes|dumpconfig|formats|fullreport|help|lastlog|lvpoll|pvdata|segtypes|systemid|tags|version) *$$' > .commands
ccmd: create-commands.c
$(CC) create-commands.c -o ccmd
command-lines.h: ccmd
./ccmd --output struct command-lines.in > command-lines.h
./ccmd --output count command-lines.in > command-lines-count.h
ifneq ("$(CFLOW_CMD)", "")
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
-include $(top_builddir)/libdm/libdevmapper.cflow

View File

@@ -87,7 +87,7 @@ arg(poolmetadata_ARG, '\0', "poolmetadata", lv_VAL, 0, 0)
arg(poolmetadatasize_ARG, '\0', "poolmetadatasize", sizemb_VAL, 0, 0)
arg(poolmetadataspare_ARG, '\0', "poolmetadataspare", bool_VAL, 0, 0)
arg(profile_ARG, '\0', "profile", string_VAL, 0, 0)
arg(pvmetadatacopies_ARG, '\0', "pvmetadatacopies", number_VAL, 0, 0)
arg(pvmetadatacopies_ARG, '\0', "pvmetadatacopies", pvmetadatacopies_VAL, 0, 0)
arg(raidrebuild_ARG, '\0', "raidrebuild", string_VAL, ARG_GROUPABLE, 0)
arg(raidmaxrecoveryrate_ARG, '\0', "raidmaxrecoveryrate", sizekb_VAL, 0, 0)
arg(raidminrecoveryrate_ARG, '\0', "raidminrecoveryrate", sizekb_VAL, 0, 0)
@@ -133,7 +133,7 @@ arg(unquoted_ARG, '\0', "unquoted", 0, 0, 0)
arg(usepolicies_ARG, '\0', "usepolicies", 0, 0, 0)
arg(validate_ARG, '\0', "validate", 0, 0, 0)
arg(version_ARG, '\0', "version", 0, 0, 0)
arg(vgmetadatacopies_ARG, '\0', "vgmetadatacopies", metadatacopies_VAL, 0, 0)
arg(vgmetadatacopies_ARG, '\0', "vgmetadatacopies", vgmetadatacopies_VAL, 0, 0)
arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", sizemb_VAL, 0, 0)
arg(withsummary_ARG, '\0', "withsummary", 0, 0, 0)
arg(withcomments_ARG, '\0', "withcomments", 0, 0, 0)

View File

@@ -1,133 +0,0 @@
/* Do not edit. This file is generated by scripts/create-commands */
/* using command definitions from scripts/command-lines.in */
#define COMMAND_COUNT 147
enum {
no_CMD,
lvchange_properties_CMD,
lvchange_resync_CMD,
lvchange_syncaction_CMD,
lvchange_rebuild_CMD,
lvchange_activate_CMD,
lvchange_refresh_CMD,
lvchange_monitor_CMD,
lvchange_poll_CMD,
lvchange_persistent_CMD,
lvconvert_merge_CMD,
lvconvert_combine_split_snapshot_CMD,
lvconvert_to_thin_with_external_CMD,
lvconvert_to_cache_vol_CMD,
lvconvert_to_thinpool_CMD,
lvconvert_to_cachepool_CMD,
lvconvert_to_mirror_CMD,
lvconvert_to_mirror_or_raid1_CMD,
lvconvert_raid1_to_mirror_CMD,
lvconvert_mirror_to_raid1_CMD,
lvconvert_general_to_raid_CMD,
lvconvert_change_mirror_images_CMD,
lvconvert_raid_to_striped_CMD,
lvconvert_raid_or_mirror_to_linear_CMD,
lvconvert_split_mirror_images_to_new_CMD,
lvconvert_split_mirror_images_and_track_CMD,
lvconvert_repair_pvs_or_thinpool_CMD,
lvconvert_replace_pv_CMD,
lvconvert_change_mirrorlog_CMD,
lvconvert_split_and_keep_cachepool_CMD,
lvconvert_split_and_delete_cachepool_CMD,
lvconvert_split_cow_snapshot_CMD,
lvconvert_poll_mirror_CMD,
lvconvert_swap_pool_metadata_CMD,
lvcreate_error_vol_CMD,
lvcreate_zero_vol_CMD,
lvcreate_linear_CMD,
lvcreate_striped_CMD,
lvcreate_mirror_CMD,
lvcreate_raid_any_CMD,
lvcreate_cow_snapshot_CMD,
lvcreate_cow_snapshot_with_virtual_origin_CMD,
lvcreate_thinpool_CMD,
lvcreate_cachepool_CMD,
lvcreate_thin_vol_CMD,
lvcreate_thin_snapshot_CMD,
lvcreate_thin_snapshot_of_external_CMD,
lvcreate_thin_vol_with_thinpool_CMD,
lvcreate_thin_vol_with_thinpool_or_sparse_snapshot_CMD,
lvcreate_convert_to_cache_vol_with_cachepool_CMD,
lvcreate_cache_vol_with_new_origin_CMD,
lvdisplay_general_CMD,
lvextend_by_size_CMD,
lvextend_by_pv_CMD,
lvextend_pool_metadata_by_size_CMD,
lvextend_by_policy_CMD,
lvmconfig_general_CMD,
lvreduce_general_CMD,
lvremove_general_CMD,
lvrename_vg_lv_lv_CMD,
lvrename_lv_lv_CMD,
lvresize_by_size_CMD,
lvresize_by_pv_CMD,
lvresize_pool_metadata_by_size_CMD,
lvs_general_CMD,
lvscan_general_CMD,
pvchange_properties_all_CMD,
pvchange_properties_some_CMD,
pvresize_general_CMD,
pvck_general_CMD,
pvcreate_general_CMD,
pvdisplay_general_CMD,
pvmove_one_CMD,
pvmove_any_CMD,
pvremove_general_CMD,
pvs_general_CMD,
pvscan_show_CMD,
pvscan_cache_CMD,
vgcfgbackup_general_CMD,
vgcfgrestore_by_vg_CMD,
vgcfgrestore_by_file_CMD,
vgchange_properties_CMD,
vgchange_monitor_CMD,
vgchange_poll_CMD,
vgchange_activate_CMD,
vgchange_refresh_CMD,
vgchange_lockstart_CMD,
vgchange_lockstop_CMD,
vgck_general_CMD,
vgconvert_general_CMD,
vgcreate_general_CMD,
vgdisplay_general_CMD,
vgexport_some_CMD,
vgexport_all_CMD,
vgextend_general_CMD,
vgimport_some_CMD,
vgimport_all_CMD,
vgimportclone_general_CMD,
vgmerge_general_CMD,
vgmknodes_general_CMD,
vgreduce_by_pv_CMD,
vgreduce_all_CMD,
vgreduce_missing_CMD,
vgremove_general_CMD,
vgrename_by_name_CMD,
vgrename_by_uuid_CMD,
vgs_general_CMD,
vgscan_general_CMD,
vgsplit_by_pv_to_existing_CMD,
vgsplit_by_lv_to_existing_CMD,
vgsplit_by_pv_to_new_CMD,
vgsplit_by_lv_to_new_CMD,
devtypes_general_CMD,
fullreport_general_CMD,
lastlog_general_CMD,
lvpoll_general_CMD,
formats_general_CMD,
help_general_CMD,
version_general_CMD,
pvdata_general_CMD,
segtypes_general_CMD,
systemid_general_CMD,
tags_general_CMD,
lvmchange_general_CMD,
lvmdiskscan_general_CMD,
lvmsadc_general_CMD,
lvmsar_general_CMD,
COMMAND_ID_COUNT,
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -51,8 +51,8 @@ struct command_name {
*/
/* arg_def flags */
#define ARG_DEF_FLAG_NEW 1 << 0
#define ARG_DEF_FLAG_MAY_REPEAT 1 << 1
#define ARG_DEF_FLAG_NEW 1 << 0
#define ARG_DEF_FLAG_MAY_REPEAT 1 << 1
/* arg_def lv_types */
enum {
@@ -123,7 +123,8 @@ struct pos_arg {
* one or more from required_opt_args is required,
* then the rest are optional.
*/
#define CMD_FLAG_ONE_REQUIRED_OPT 1
#define CMD_FLAG_ONE_REQUIRED_OPT 1
#define CMD_FLAG_SECONDARY_SYNTAX 2
/* a register of the lvm commands */
struct command {

View File

@@ -47,6 +47,8 @@ int segtype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int alloc_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int locktype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int readahead_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; }
int vgmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int pvmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; }
/* also see arg_props */
@@ -108,6 +110,8 @@ static struct opt_name opt_names[ARG_COUNT + 1] = {
struct cmd_name {
const char *name;
const char *desc;
int variants;
int common_options[ARG_COUNT + 1];
};
/* create table of command names, e.g. vgcreate */
@@ -133,7 +137,7 @@ struct oo_line {
int cmd_count;
struct command cmd_array[MAX_CMDS];
struct command common_options; /* for printing common usage */
struct command lvm_all; /* for printing common options for all lvm commands */
#define MAX_OO_LINES 256
int oo_line_count;
@@ -499,6 +503,19 @@ static const char *cmd_name_desc(const char *name)
return NULL;
}
static struct cmd_name *find_command_name(const char *str)
{
int i;
for (i = 0; i < MAX_CMD_NAMES; i++) {
if (!cmd_names[i].name)
break;
if (!strcmp(cmd_names[i].name, str))
return &cmd_names[i];
}
return NULL;
}
static int is_opt_name(char *str)
{
if (!strncmp(str, "--", 2))
@@ -562,6 +579,13 @@ static int is_desc_line(char *str)
return 0;
}
static int is_flags_line(char *str)
{
if (!strncmp(str, "FLAGS:", 6))
return 1;
return 0;
}
static int is_id_line(char *str)
{
if (!strncmp(str, "ID:", 3))
@@ -977,7 +1001,7 @@ static void print_def(struct arg_def *def, int usage)
printf("%s", def->str);
else if (val_enum == constnum_VAL)
printf("ll%u", (unsigned long long)def->num);
printf("%llu", (unsigned long long)def->num);
else {
if (sep) printf("|");
@@ -1132,14 +1156,46 @@ static char *flags_to_str(int flags)
memset(buf_flags, 0, sizeof(buf_flags));
if (flags & ARG_DEF_FLAG_MAY_REPEAT)
if (flags & ARG_DEF_FLAG_MAY_REPEAT) {
if (buf_flags[0])
strcat(buf_flags, " | ");
strcat(buf_flags, "ARG_DEF_FLAG_MAY_REPEAT");
if (flags & ARG_DEF_FLAG_NEW)
}
if (flags & ARG_DEF_FLAG_NEW) {
if (buf_flags[0])
strcat(buf_flags, " | ");
strcat(buf_flags, "ARG_DEF_FLAG_NEW");
}
return buf_flags;
}
static void add_flags(struct command *cmd, char *line)
{
if (strstr(line, "SECONDARY_SYNTAX"))
cmd->cmd_flags |= CMD_FLAG_SECONDARY_SYNTAX;
}
static char *cmd_flags_to_str(uint32_t flags)
{
static char buf_cmd_flags[32];
memset(buf_cmd_flags, 0, sizeof(buf_cmd_flags));
if (flags & CMD_FLAG_SECONDARY_SYNTAX) {
if (buf_cmd_flags[0])
strcat(buf_cmd_flags, " | ");
strcat(buf_cmd_flags, "CMD_FLAG_SECONDARY_SYNTAX");
}
if (flags & CMD_FLAG_ONE_REQUIRED_OPT) {
if (buf_cmd_flags[0])
strcat(buf_cmd_flags, " | ");
strcat(buf_cmd_flags, "CMD_FLAG_ONE_REQUIRED_OPT");
}
return buf_cmd_flags;
}
void print_command_count(void)
{
struct command *cmd;
@@ -1173,35 +1229,156 @@ void print_command_count(void)
printf("};\n");
}
static int is_common_opt(int opt)
static int is_lvm_all_opt(int opt)
{
int oo;
for (oo = 0; oo < common_options.oo_count; oo++) {
if (common_options.optional_opt_args[oo].opt == opt)
for (oo = 0; oo < lvm_all.oo_count; oo++) {
if (lvm_all.optional_opt_args[oo].opt == opt)
return 1;
}
return 0;
}
/*
* For certain commands (esp commands like lvcreate with many variants), common
* options should not be printed for every variation, but once for all. The
* list of commands this applies to is fixed for now but could be encoded in
* command-lines.in.
*
* The common options are defined in OO_USAGE_COMMON. Those options
* are skipped when creating the usage strings for each variation of
* these commands. Instead they are set in the usage_common string.
*/
void print_usage(struct command *cmd, int skip_required)
static void factor_common_options(void)
{
int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
int i, sep, ro, rp, oo, op;
int cn, opt_enum, ci, oo, found;
struct command *cmd;
if (skip_required)
goto oo_count;
for (cn = 0; cn < MAX_CMD_NAMES; cn++) {
if (!cmd_names[cn].name)
break;
for (ci = 0; ci < cmd_count; ci++) {
cmd = &cmd_array[ci];
if (strcmp(cmd->name, cmd_names[cn].name))
continue;
cmd_names[cn].variants++;
}
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
for (ci = 0; ci < cmd_count; ci++) {
cmd = &cmd_array[ci];
if (strcmp(cmd->name, cmd_names[cn].name))
continue;
found = 0;
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt == opt_enum) {
found = 1;
break;
}
}
if (!found)
goto next_opt;
}
/* all commands starting with this name use this option */
cmd_names[cn].common_options[opt_enum] = 1;
next_opt:
;
}
}
/*
for (cn = 0; cn < MAX_CMD_NAMES; cn++) {
if (!cmd_names[cn].name)
break;
printf("%s (%d)\n", cmd_names[cn].name, cmd_names[cn].variants);
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (cmd_names[cn].common_options[opt_enum])
printf(" %s\n", opt_names[opt_enum].long_opt);
}
}
*/
}
void print_usage_common(struct command *cmd)
{
struct cmd_name *cname;
int i, sep, ro, rp, oo, op, opt_enum;
if (!(cname = find_command_name(cmd->name)))
return;
sep = 0;
/*
* when there's more than one variant, options that
* are common to all commands with a common name.
*/
if (cname->variants < 2)
goto all;
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->common_options[opt_enum])
continue;
if (is_lvm_all_opt(opt_enum))
continue;
if (!sep) {
printf("\n");
printf("\" [");
} else {
printf(",");
}
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt != opt_enum)
continue;
printf(" %s", opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
break;
}
}
all:
/* options that are common to all lvm commands */
for (oo = 0; oo < lvm_all.oo_count; oo++) {
opt_enum = lvm_all.optional_opt_args[oo].opt;
if (!sep) {
printf("\n");
printf("\" [");
} else {
printf(",");
}
printf(" %s", opt_names[opt_enum].long_opt);
if (lvm_all.optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&lvm_all.optional_opt_args[oo].def, 1);
}
sep = 1;
}
printf(" ]\"");
printf(";\n");
}
void print_usage(struct command *cmd)
{
struct cmd_name *cname;
int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
int i, sep, ro, rp, oo, op, opt_enum;
if (!(cname = find_command_name(cmd->name)))
return;
printf("\"%s", cmd->name);
@@ -1240,30 +1417,40 @@ void print_usage(struct command *cmd, int skip_required)
sep = 0;
if (cmd->oo_count) {
printf("\n");
printf("\" [");
for (oo = 0; oo < cmd->oo_count; oo++) {
/* skip common opts which are in the usage_common string */
if ((cmd != &common_options) && is_common_opt(cmd->optional_opt_args[oo].opt))
opt_enum = cmd->optional_opt_args[oo].opt;
/*
* Skip common opts which are in the usage_common string.
* The common opts are those in lvm_all and in
* cname->common_options.
*/
if (is_lvm_all_opt(opt_enum))
continue;
if (!sep) {
printf("\n");
printf("\" [");
}
if ((cname->variants > 1) && cname->common_options[opt_enum])
continue;
if (sep)
printf(",");
printf(" %s", opt_names[cmd->optional_opt_args[oo].opt].long_opt);
printf(" %s", opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
}
}
if (sep)
if (sep)
printf(",");
printf(" COMMON_OPTIONS");
printf(" ]\"");
}
op_count:
if (!cmd->op_count)
@@ -1352,7 +1539,7 @@ static void print_def_man(struct arg_def *def, int usage)
else if (val_enum == constnum_VAL) {
printf("\\fB");
printf("ll%u", (unsigned long long)def->num);
printf("%llu", (unsigned long long)def->num);
printf("\\fP");
}
@@ -1393,13 +1580,14 @@ static void print_def_man(struct arg_def *def, int usage)
printf(" ...");
}
void print_cmd_man(struct command *cmd, int skip_required)
void print_man_usage(struct command *cmd)
{
struct cmd_name *cname;
int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0;
int i, sep, ro, rp, oo, op;
int i, sep, ro, rp, oo, op, opt_enum;
if (skip_required)
goto oo_count;
if (!(cname = find_command_name(cmd->name)))
return;
printf("\\fB%s\\fP", cmd->name);
@@ -1424,6 +1612,7 @@ void print_cmd_man(struct command *cmd, int skip_required)
sep = 0;
/* print required options with a short opt */
for (ro = 0; ro < cmd->ro_count; ro++) {
if (!opt_names[cmd->required_opt_args[ro].opt].short_opt)
continue;
@@ -1451,6 +1640,7 @@ void print_cmd_man(struct command *cmd, int skip_required)
sep = 1;
}
/* print required options without a short opt */
for (ro = 0; ro < cmd->ro_count; ro++) {
if (opt_names[cmd->required_opt_args[ro].opt].short_opt)
continue;
@@ -1476,6 +1666,7 @@ void print_cmd_man(struct command *cmd, int skip_required)
printf(".RE\n");
}
/* print required positional args on a new line after the onereq set */
if (cmd->rp_count) {
printf(".RS 4\n");
for (rp = 0; rp < cmd->rp_count; rp++) {
@@ -1498,7 +1689,7 @@ void print_cmd_man(struct command *cmd, int skip_required)
/*
* all are required options, print as:
* -a|--a, -b|--b
* -a|--aaa <val> -b|--bbb <val>
*/
if (cmd->ro_count) {
@@ -1518,6 +1709,7 @@ void print_cmd_man(struct command *cmd, int skip_required)
}
}
/* print required positional args on the same line as the required options */
if (cmd->rp_count) {
for (rp = 0; rp < cmd->rp_count; rp++) {
if (cmd->required_pos_args[rp].def.val_bits) {
@@ -1539,22 +1731,29 @@ void print_cmd_man(struct command *cmd, int skip_required)
sep = 0;
printf(".br\n");
if (cmd->oo_count) {
printf(".RS 4\n");
printf("[");
/* print optional options with short opts */
for (oo = 0; oo < cmd->oo_count; oo++) {
/* skip common opts which are in the usage_common string */
if ((cmd != &common_options) && is_common_opt(cmd->optional_opt_args[oo].opt))
opt_enum = cmd->optional_opt_args[oo].opt;
if (!opt_names[opt_enum].short_opt)
continue;
if (!opt_names[cmd->optional_opt_args[oo].opt].short_opt)
/*
* Skip common opts which are in the usage_common string.
* The common opts are those in lvm_all and in
* cname->common_options.
*/
if (is_lvm_all_opt(opt_enum))
continue;
if (!sep) {
printf(".RS 4\n");
printf("[");
}
if ((cname->variants > 1) && cname->common_options[opt_enum])
continue;
if (sep) {
printf(",");
@@ -1563,8 +1762,8 @@ void print_cmd_man(struct command *cmd, int skip_required)
}
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[cmd->optional_opt_args[oo].opt].short_opt,
opt_names[cmd->optional_opt_args[oo].opt].long_opt);
opt_names[opt_enum].short_opt,
opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
@@ -1573,18 +1772,25 @@ void print_cmd_man(struct command *cmd, int skip_required)
sep = 1;
}
/* print optional options without short opts */
for (oo = 0; oo < cmd->oo_count; oo++) {
/* skip common opts which are in the usage_common string */
if ((cmd != &common_options) && is_common_opt(cmd->optional_opt_args[oo].opt))
opt_enum = cmd->optional_opt_args[oo].opt;
if (opt_names[opt_enum].short_opt)
continue;
if (opt_names[cmd->optional_opt_args[oo].opt].short_opt)
/*
* Skip common opts which are in the usage_common string.
* The common opts are those in lvm_all and in
* cname->common_options.
*/
if (is_lvm_all_opt(opt_enum))
continue;
if (!sep) {
printf(".RS 4\n");
printf("[");
}
if ((cname->variants > 1) && cname->common_options[opt_enum])
continue;
if (sep) {
printf(",");
@@ -1595,7 +1801,7 @@ void print_cmd_man(struct command *cmd, int skip_required)
/* space alignment without short opt */
printf(" ");
printf(" \\fB%s\\fP", opt_names[cmd->optional_opt_args[oo].opt].long_opt);
printf(" \\fB%s\\fP", opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
@@ -1603,9 +1809,15 @@ void print_cmd_man(struct command *cmd, int skip_required)
}
sep = 1;
}
}
if (sep) {
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
/* space alignment without short opt */
printf(" ");
}
printf(" COMMON_OPTIONS");
printf(" ]\n");
printf(".RE\n");
printf(".br\n");
@@ -1634,6 +1846,151 @@ void print_cmd_man(struct command *cmd, int skip_required)
printf("\n");
}
void print_man_usage_common(struct command *cmd)
{
struct cmd_name *cname;
int i, sep, ro, rp, oo, op, opt_enum;
if (!(cname = find_command_name(cmd->name)))
return;
sep = 0;
printf(".RS 4\n");
printf("[");
/*
* when there's more than one variant, options that
* are common to all commands with a common name.
*/
if (cname->variants < 2)
goto all;
/* print those with short opts */
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->common_options[opt_enum])
continue;
if (!opt_names[opt_enum].short_opt)
continue;
if (is_lvm_all_opt(opt_enum))
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt != opt_enum)
continue;
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def_man(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
break;
}
}
/* print those without short opts */
for (opt_enum = 0; opt_enum < ARG_COUNT; opt_enum++) {
if (!cname->common_options[opt_enum])
continue;
if (opt_names[opt_enum].short_opt)
continue;
if (is_lvm_all_opt(opt_enum))
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
for (oo = 0; oo < cmd->oo_count; oo++) {
if (cmd->optional_opt_args[oo].opt != opt_enum)
continue;
/* space alignment without short opt */
printf(" ");
printf(" \\fB%s\\fP", opt_names[opt_enum].long_opt);
if (cmd->optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def_man(&cmd->optional_opt_args[oo].def, 1);
}
sep = 1;
break;
}
}
all:
/* options that are common to all lvm commands */
/* those with short opts */
for (oo = 0; oo < lvm_all.oo_count; oo++) {
opt_enum = lvm_all.optional_opt_args[oo].opt;
if (!opt_names[opt_enum].short_opt)
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
printf(" \\fB-%c\\fP|\\fB%s\\fP",
opt_names[opt_enum].short_opt,
opt_names[opt_enum].long_opt);
if (lvm_all.optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&lvm_all.optional_opt_args[oo].def, 1);
}
sep = 1;
}
/* those without short opts */
for (oo = 0; oo < lvm_all.oo_count; oo++) {
opt_enum = lvm_all.optional_opt_args[oo].opt;
if (opt_names[opt_enum].short_opt)
continue;
if (sep) {
printf(",");
printf("\n.br\n");
printf(" ");
}
/* space alignment without short opt */
printf(" ");
printf(" \\fB%s\\fP", opt_names[opt_enum].long_opt);
if (lvm_all.optional_opt_args[oo].def.val_bits) {
printf(" ");
print_def(&lvm_all.optional_opt_args[oo].def, 1);
}
sep = 1;
}
printf(" ]\"");
printf(";\n");
}
#define DESC_LINE 256
void print_desc_man(const char *desc)
@@ -1674,16 +2031,16 @@ void print_desc_man(const char *desc)
}
}
void print_command_man(void)
void print_man_command(void)
{
struct command *cmd;
const char *last_cmd_name = NULL;
const char *desc;
int i, j, ro, rp, oo, op;
include_optional_opt_args(&common_options, "OO_USAGE_COMMON");
include_optional_opt_args(&lvm_all, "OO_USAGE_COMMON");
printf(".TH LVM_ALL 8\n");
printf(".TH LVM_COMMANDS 8\n");
for (i = 0; i < cmd_count; i++) {
@@ -1711,12 +2068,12 @@ void print_command_man(void)
printf(".P\n");
}
print_cmd_man(cmd, 0);
print_man_usage(cmd);
if ((i == (cmd_count - 1)) || strcmp(cmd->name, cmd_array[i+1].name)) {
printf("Common options:\n");
printf(".\n");
print_cmd_man(&common_options, 1);
print_man_usage_common(cmd);
}
printf("\n");
@@ -1729,7 +2086,7 @@ void print_command_struct(int only_usage)
struct command *cmd;
int i, j, ro, rp, oo, op;
include_optional_opt_args(&common_options, "OO_USAGE_COMMON");
include_optional_opt_args(&lvm_all, "OO_USAGE_COMMON");
printf("/* Do not edit. This file is generated by scripts/create-commands */\n");
printf("/* using command definitions from scripts/command-lines.in */\n");
@@ -1739,8 +2096,8 @@ void print_command_struct(int only_usage)
cmd = &cmd_array[i];
if (only_usage) {
print_usage(cmd, 0);
print_usage(&common_options, 1);
print_usage(cmd);
print_usage_common(cmd);
printf("\n");
continue;
}
@@ -1754,16 +2111,18 @@ void print_command_struct(int only_usage)
printf("commands[%d].oo_count = %d;\n", i, cmd->oo_count);
printf("commands[%d].op_count = %d;\n", i, cmd->op_count);
if (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT)
printf("commands[%d].cmd_flags = CMD_FLAG_ONE_REQUIRED_OPT;\n", i);
if (cmd->cmd_flags)
printf("commands[%d].cmd_flags = %s;\n", i, cmd_flags_to_str(cmd->cmd_flags));
else
printf("commands[%d].cmd_flags = 0;\n", i, cmd_flags_to_str(cmd->cmd_flags));
printf("commands[%d].desc = \"%s\";\n", i, cmd->desc ?: "");
printf("commands[%d].usage = ", i);
print_usage(cmd, 0);
print_usage(cmd);
if (cmd->oo_count) {
printf("commands[%d].usage_common = ", i);
print_usage(&common_options, 1);
print_usage_common(cmd);
} else {
printf("commands[%d].usage_common = \"NULL\";\n", i);
}
@@ -1911,12 +2270,8 @@ static void print_ambiguous(void)
continue;
if (cmd->ro_count != dup->ro_count)
continue;
if (cmd->oo_count != dup->oo_count)
continue;
if (cmd->rp_count != dup->rp_count)
continue;
if (cmd->op_count != dup->op_count)
continue;
for (ro = 0; ro < cmd->ro_count; ro++) {
if (!opt_arg_matches(&cmd->required_opt_args[ro],
@@ -1936,8 +2291,8 @@ static void print_ambiguous(void)
}
printf("Ambiguous commands %d and %d:\n", i, j);
print_usage(cmd, 0);
print_usage(dup, 0);
print_usage(cmd);
print_usage(dup);
printf("\n");
dups[found].i = i;
@@ -1977,11 +2332,11 @@ static void print_help(int argc, char *argv[])
{
printf("%s --output struct|count|usage|expanded <filename>\n", argv[0]);
printf("\n");
printf("struct: print C structures.\n");
printf("struct: print C structures for command-lines.h\n");
printf("count: print defines and enums for command-lines-count.h\n");
printf("ambiguous: print commands differing only by LV types\n");
printf("usage: print usage format.\n");
printf("expanded: print expanded input format.\n");
printf("count: print #define COMMAND_COUNT <Number>\n");
printf("ambiguous: print commands differing only by LV types\n");
}
int main(int argc, char *argv[])
@@ -2086,15 +2441,22 @@ int main(int argc, char *argv[])
if (is_desc_line(line_argv[0])) {
char *desc = strdup(line_orig);
if (cmd->desc) {
cmd->desc = realloc((char *)cmd->desc, strlen(cmd->desc) + strlen(desc) + 2);
strcat((char *)cmd->desc, " ");
strcat((char *)cmd->desc, desc);
int newlen = strlen(cmd->desc) + strlen(desc) + 2;
char *newdesc = malloc(newlen);
memset(newdesc, 0, newlen);
snprintf(newdesc, newlen, "%s %s", cmd->desc, desc);
cmd->desc = newdesc;
free(desc);
} else
cmd->desc = desc;
continue;
}
if (is_flags_line(line_argv[0])) {
add_flags(cmd, line_orig);
continue;
}
if (is_id_line(line_argv[0])) {
cmd->command_line_id = strdup(line_argv[1]);
continue;
@@ -2147,10 +2509,14 @@ int main(int argc, char *argv[])
fclose(file);
factor_common_options();
if (!outputformat)
print_command_struct(1);
else if (!strcmp(outputformat, "struct"))
else if (!strcmp(outputformat, "struct")) {
print_command_struct(0);
print_ambiguous();
}
else if (!strcmp(outputformat, "count"))
print_command_count();
else if (!strcmp(outputformat, "usage"))
@@ -2160,8 +2526,10 @@ int main(int argc, char *argv[])
else if (!strcmp(outputformat, "ambiguous"))
print_ambiguous();
else if (!strcmp(outputformat, "man"))
print_command_man();
print_man_command();
else
print_help(argc, argv);
return 0;
}

View File

@@ -30,12 +30,12 @@ void *cmdlib_lvm2_init(unsigned static_compile)
{
struct cmd_context *cmd;
lvm_register_commands();
init_is_static(static_compile);
if (!(cmd = init_lvm(1, 1)))
return NULL;
lvm_register_commands();
return (void *) cmd;
}

View File

@@ -733,23 +733,134 @@ int readahead_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_va
/*
* Non-zero, positive integer, "all", or "unmanaged"
*/
int metadatacopies_arg(struct cmd_context *cmd, struct arg_values *av)
int vgmetadatacopies_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strncmp(cmd->name, "vg", 2)) {
if (!strcasecmp(av->value, "all")) {
av->ui_value = VGMETADATACOPIES_ALL;
return 1;
}
if (!strcasecmp(av->value, "all")) {
av->ui_value = VGMETADATACOPIES_ALL;
return 1;
}
if (!strcasecmp(av->value, "unmanaged")) {
av->ui_value = VGMETADATACOPIES_UNMANAGED;
return 1;
}
if (!strcasecmp(av->value, "unmanaged")) {
av->ui_value = VGMETADATACOPIES_UNMANAGED;
return 1;
}
return int_arg(cmd, av);
}
int pvmetadatacopies_arg(struct cmd_context *cmd, struct arg_values *av)
{
int num;
if (!int_arg(cmd, av))
return 0;
num = av->i_value;
if ((num != 0) && (num != 1) && (num != 2))
return 0;
return 1;
}
int metadatacopies_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strncmp(cmd->name, "pv", 2))
return pvmetadatacopies_arg(cmd, av);
if (!strncmp(cmd->name, "vg", 2))
return vgmetadatacopies_arg(cmd, av);
return 0;
}
/*
* FIXME: there's been a confusing mixup among:
* resizeable, resizable, allocatable, allocation.
*
* resizeable and allocatable are the preferred,
* standard option names.
*
* The dispreferred "resizable" is always translated
* to the preferred resizeable.
*
* But, the dispreferred "allocation" name seems
* to translate to either or both resizeable
* and allocatable, it's not clear which.
*/
static int _opt_standard_to_synonym(const char *cmd_name, int opt)
{
switch (opt) {
case mirrorlog_ARG:
return corelog_ARG;
case resizeable_ARG:
return resizable_ARG;
case allocatable_ARG:
return allocation_ARG;
case activate_ARG:
return available_ARG;
case rebuild_ARG:
return raidrebuild_ARG;
case syncaction_ARG:
return raidsyncaction_ARG;
case writemostly_ARG:
return raidwritemostly_ARG;
case minrecoveryrate_ARG:
return raidminrecoveryrate_ARG;
case maxrecoveryrate_ARG:
return raidmaxrecoveryrate_ARG;
case writebehind_ARG:
return raidwritebehind_ARG;
case virtualsize_ARG:
return virtualoriginsize_ARG;
case pvmetadatacopies_ARG:
if (!strncmp(cmd_name, "pv", 2))
return metadatacopies_ARG;
return 0;
case vgmetadatacopies_ARG:
if (!strncmp(cmd_name, "vg", 2))
return metadatacopies_ARG;
return 0;
}
return 0;
}
static int _opt_synonym_to_standard(const char *cmd_name, int opt)
{
switch (opt) {
case corelog_ARG:
return mirrorlog_ARG;
case resizable_ARG:
return resizeable_ARG;
case allocation_ARG:
return allocatable_ARG;
case available_ARG:
return activate_ARG;
case raidrebuild_ARG:
return rebuild_ARG;
case raidsyncaction_ARG:
return syncaction_ARG;
case raidwritemostly_ARG:
return writemostly_ARG;
case raidminrecoveryrate_ARG:
return minrecoveryrate_ARG;
case raidmaxrecoveryrate_ARG:
return maxrecoveryrate_ARG;
case raidwritebehind_ARG:
return writebehind_ARG;
case virtualoriginsize_ARG:
return virtualsize_ARG;
case metadatacopies_ARG:
if (!strncmp(cmd_name, "pv", 2))
return pvmetadatacopies_ARG;
if (!strncmp(cmd_name, "vg", 2))
return vgmetadatacopies_ARG;
return 0;
}
return 0;
}
static void _add_getopt_arg(int arg_enum, char **optstrp, struct option **longoptsp);
/*
* The valid args for a command name in general is a union of
* required_opt_args and optional_opt_args for all commands[]
@@ -761,6 +872,7 @@ static void _set_valid_args_for_command_name(int ci)
int all_args[ARG_COUNT] = { 0 };
int num_args = 0;
int opt_enum; /* foo_ARG from args.h */
int opt_syn;
int i, ro, oo;
/*
@@ -774,6 +886,7 @@ static void _set_valid_args_for_command_name(int ci)
for (ro = 0; ro < commands[i].ro_count; ro++) {
opt_enum = commands[i].required_opt_args[ro].opt;
all_args[opt_enum] = 1;
}
for (oo = 0; oo < commands[i].oo_count; oo++) {
opt_enum = commands[i].optional_opt_args[oo].opt;
@@ -783,16 +896,36 @@ static void _set_valid_args_for_command_name(int ci)
for (i = 0; i < ARG_COUNT; i++) {
if (all_args[i]) {
command_names[ci].valid_args[num_args] = _cmdline.arg_props[i].arg_enum;
opt_enum = _cmdline.arg_props[i].arg_enum;
command_names[ci].valid_args[num_args] = opt_enum;
num_args++;
/* Automatically recognize --extents in addition to --size. */
if (_cmdline.arg_props[i].arg_enum == size_ARG) {
if (opt_enum == size_ARG) {
command_names[ci].valid_args[num_args] = extents_ARG;
num_args++;
}
/* Recognize synonyms */
if ((opt_syn = _opt_standard_to_synonym(command_names[ci].name, opt_enum))) {
command_names[ci].valid_args[num_args] = opt_syn;
num_args++;
}
/*
* "--allocation" is a weird option that seems to be
* a synonym for either allocatable or resizeable,
* each which already have their own other synonyms,
* so just add allocation whenever either is seen.
*/
if ((opt_enum == allocatable_ARG) || (opt_enum == resizeable_ARG)) {
command_names[ci].valid_args[num_args] = allocation_ARG;
num_args++;
}
}
}
command_names[ci].num_args = num_args;
}
@@ -861,49 +994,25 @@ void lvm_register_commands(void)
_set_valid_args_for_command_name(i);
}
static int _opt_equivalent_is_set(struct cmd_context *cmd, int opt)
/*
* Also see merge_synonym(). The command definitions
* are written using just one variation of the option
* name (opt below). This function checks if the user
* entered a synonym (arg_is_set).
*/
static int _opt_synonym_is_set(struct cmd_context *cmd, int opt_std)
{
if ((opt == mirrorlog_ARG) && arg_is_set(cmd, corelog_ARG))
return 1;
int opt_syn = _opt_standard_to_synonym(cmd->name, opt_std);
if ((opt == resizeable_ARG) && arg_is_set(cmd, resizable_ARG))
return 1;
if ((opt == allocatable_ARG) && arg_is_set(cmd, allocation_ARG))
return 1;
if ((opt == resizeable_ARG) && arg_is_set(cmd, allocation_ARG))
return 1;
if ((opt == activate_ARG) && arg_is_set(cmd, available_ARG))
return 1;
if ((opt == rebuild_ARG) && arg_is_set(cmd, raidrebuild_ARG))
return 1;
if ((opt == syncaction_ARG) && arg_is_set(cmd, raidsyncaction_ARG))
return 1;
if ((opt == writemostly_ARG) && arg_is_set(cmd, raidwritemostly_ARG))
return 1;
if ((opt == minrecoveryrate_ARG) && arg_is_set(cmd, raidminrecoveryrate_ARG))
return 1;
if ((opt == maxrecoveryrate_ARG) && arg_is_set(cmd, raidmaxrecoveryrate_ARG))
return 1;
if ((opt == writebehind_ARG) && arg_is_set(cmd, raidwritebehind_ARG))
return 1;
return 0;
return opt_syn && arg_is_set(cmd, opt_syn);
}
static int _command_required_opt_matches(struct cmd_context *cmd, int ci, int ro)
{
int opt_enum = commands[ci].required_opt_args[ro].opt;
if (arg_is_set(cmd, opt_enum))
if (arg_is_set(cmd, opt_enum) || _opt_synonym_is_set(cmd, opt_enum))
goto check_val;
/*
@@ -918,9 +1027,6 @@ static int _command_required_opt_matches(struct cmd_context *cmd, int ci, int ro
goto check_val;
}
if (_opt_equivalent_is_set(cmd, opt_enum))
goto check_val;
return 0;
/*
@@ -952,12 +1058,13 @@ check_val:
static int _command_required_pos_matches(struct cmd_context *cmd, int ci, int rp, char **argv)
{
const char *name;
/*
* rp is the index in required_pos_args[] of the required positional arg.
* The pos values begin with 1, so the first positional arg has
* pos 1, rp 0.
*/
if (argv[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. */
@@ -972,6 +1079,36 @@ static int _command_required_pos_matches(struct cmd_context *cmd, int ci, int rp
arg_is_set(cmd, select_ARG))
return 1;
/*
* For an lvcreate command with VG as the first required positional arg,
* the VG position is allowed to be empty if --name VG/LV is used, or if the
* LVM_VG_NAME env var is set.
*
* --thinpool VG/LV and --cachepool VG/LV can also function like --name
* to provide the VG name in place of the positional arg.
*/
if (!strcmp(cmd->name, "lvcreate") &&
(rp == 0) &&
val_bit_is_set(commands[ci].required_pos_args[rp].def.val_bits, vg_VAL) &&
(arg_is_set(cmd, name_ARG) || arg_is_set(cmd, thinpool_ARG) || arg_is_set(cmd, cachepool_ARG))) {
if ((name = arg_str_value(cmd, name_ARG, NULL))) {
if (strstr(name, "/") || getenv("LVM_VG_NAME"))
return 1;
}
/* FIXME: does LVM_VG_NAME also work with --thinpool/--cachepool ? */
if ((name = arg_str_value(cmd, thinpool_ARG, NULL))) {
if (strstr(name, "/"))
return 1;
}
if ((name = arg_str_value(cmd, cachepool_ARG, NULL))) {
if (strstr(name, "/"))
return 1;
}
}
return 0;
}
@@ -1136,17 +1273,14 @@ static void _print_description(int ci)
int bi = 0;
for (di = 0; di < strlen(desc); di++) {
if (desc[di] == '\0')
break;
if (desc[di] == '\n')
continue;
if (!strncmp(&desc[di], "DESC:", 5)) {
if (bi) {
buf[bi] = '\0';
log_print("%s", buf);
memset(buf, 0, sizeof(buf));
bi = 0;
}
/* skip DESC: */
di += 5;
continue;
}
@@ -1160,8 +1294,10 @@ static void _print_description(int ci)
break;
}
if (bi)
if (bi) {
buf[bi] = '\0';
log_print("%s", buf);
}
}
/*
@@ -1186,14 +1322,22 @@ static void _print_description(int ci)
* cmd->argv[] in that pos can be NULL if arg_is_set(select_ARG)
*/
/* The max number of unused options we keep track of to warn about */
#define MAX_UNUSED_COUNT 8
static struct command *_find_command(struct cmd_context *cmd, const char *path, int *argc, char **argv)
{
const char *name;
int match_count, match_count_ro, match_count_rp, mismatch_count;
int best_i = 0, best_count = 0;
int closest_i = 0, closest_count_ro = 0;
int match_required, match_ro, match_rp, match_type, match_unused, mismatch_required;
int best_i = 0, best_required = 0, best_type = 0, best_unused = 0;
int close_i = 0, close_ro = 0, close_type;
int temp_unused_options[MAX_UNUSED_COUNT];
int temp_unused_count;
int best_unused_options[MAX_UNUSED_COUNT] = { 0 };
int best_unused_count = 0;
int ro, rp;
int i, j;
int opt_enum, opt_i;
int accepted, count;
name = last_path_component(path);
@@ -1206,27 +1350,34 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
if (arg_is_set(cmd, help_ARG) || arg_is_set(cmd, help2_ARG) || arg_is_set(cmd, version_ARG))
return &commands[i];
match_count = 0; /* total parameters that match */
match_count_ro = 0; /* required opt_args that match */
match_count_rp = 0; /* required pos_args that match */
mismatch_count = 0; /* total parameters that do not match */
match_required = 0; /* required parameters that match */
match_ro = 0; /* required opt_args that match */
match_rp = 0; /* required pos_args that match */
match_type = 0; /* type arg matches */
match_unused = 0; /* options set that are not accepted by command */
mismatch_required = 0; /* required parameters that do not match */
temp_unused_count = 0;
memset(&temp_unused_options, 0, sizeof(temp_unused_options));
/* if the command name alone is enough, then that's a match */
if (!commands[i].ro_count && !commands[i].rp_count)
match_count = 1;
match_required = 1;
/* match required_opt_args */
for (ro = 0; ro < commands[i].ro_count; ro++) {
if (_command_required_opt_matches(cmd, i, ro)) {
/* log_warn("match %d ro opt %d", i, commands[i].required_opt_args[ro].opt); */
match_count++;
match_count_ro++;
match_required++;
match_ro++;
if (commands[i].required_opt_args[ro].opt == type_ARG)
match_type = 1;
} else {
/* cmd is missing a required opt arg */
/* log_warn("mismatch %d ro opt %d", i, commands[i].required_opt_args[ro].opt); */
mismatch_count++;
mismatch_required++;
}
}
@@ -1235,12 +1386,12 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
* if one required_opt_arg did match.
*/
if (commands[i].cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) {
if (match_count_ro) {
if (match_ro) {
/* one or more of the required_opt_args is used */
mismatch_count = 0;
mismatch_required = 0;
} else {
/* not even one of the required_opt_args is used */
mismatch_count = 1;
mismatch_required = 1;
}
}
@@ -1249,134 +1400,139 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
for (rp = 0; rp < commands[i].rp_count; rp++) {
if (_command_required_pos_matches(cmd, i, rp, argv)) {
/* log_warn("match %d rp %d", i, commands[i].required_pos_args[rp].pos); */
match_count++;
match_count_rp++;
match_required++;
match_rp++;
} else {
/* cmd is missing a required pos arg */
/* log_warn("mismatch %d rp %d", i, commands[i].required_pos_args[rp].pos); */
mismatch_count++;
mismatch_required++;
}
}
/* if cmd is missing any required opt/pos args, it can't be this command. */
if (mismatch_count) {
if (mismatch_required) {
/* save "closest" command that doesn't match */
if (match_count_ro && (match_count_ro > closest_count_ro)) {
closest_i = i;
closest_count_ro = match_count_ro;
if ((match_type && !close_type) ||
((match_type == close_type) && (match_ro > close_ro))) {
close_i = i;
close_ro = match_ro;
close_type = match_type;
}
continue;
}
if (!match_count)
if (!match_required)
continue;
/* Count the command name as a match if all the required opt/pos args match. */
if ((commands[i].ro_count || commands[i].rp_count) &&
(match_count_ro || match_count_rp))
match_count++;
if ((commands[i].ro_count || commands[i].rp_count) && (match_ro || match_rp))
match_required++;
/* log_warn("command %d has match_count %d match_ro %d match_rp %d",
i, match_count, match_count_ro, match_count_rp); */
/* log_warn("command %d has match_required %d match_ro %d match_rp %d",
i, match_required, match_ro, match_rp); */
/* Count how many options cmd has set that are not accepted by commands[i]. */
/* FIXME: also count unused positional args? */
for (opt_i = 0; opt_i < ARG_COUNT; opt_i++) {
if (!arg_is_set(cmd, opt_i))
continue;
if (!(opt_enum = _opt_synonym_to_standard(cmd->name, opt_i)))
opt_enum = opt_i;
/* extents are not used in command definitions */
if (opt_enum == extents_ARG)
continue;
accepted = 0;
/* NB in some cases required_opt_args are optional */
for (j = 0; j < commands[i].ro_count; j++) {
if (commands[i].required_opt_args[j].opt == opt_enum) {
accepted = 1;
break;
}
}
if (accepted)
continue;
for (j = 0; j < commands[i].oo_count; j++) {
if (commands[i].optional_opt_args[j].opt == opt_enum) {
accepted = 1;
break;
}
}
if (!accepted) {
match_unused++;
if (temp_unused_count < MAX_UNUSED_COUNT)
temp_unused_options[temp_unused_count++] = opt_enum;
}
}
/*
* Choose the best match, which in general is the command with
* the most matching required_{opt,pos}.
*
* A match is better if:
* . more required opt/pos args match
* . type arg matches when other doesn't
* . those being equal, less unused options
*/
if (!best_count || (match_count > best_count)) {
/* log_warn("best %d has match_count %d match_ro %d match_rp %d",
i, match_count, match_count_ro, match_count_rp); */
if (!best_required || (match_required > best_required) || (match_type > best_type) ||
((match_required == best_required) && (match_type == best_type) && (match_unused < best_unused))) {
/* log_warn("best %d has match_required %d match_ro %d match_rp %d",
i, match_required, match_ro, match_rp); */
best_i = i;
best_count = match_count;
best_required = match_required;
best_type = match_type;
best_unused = match_unused;
best_unused_count = temp_unused_count;
memcpy(&best_unused_options, &temp_unused_options, sizeof(best_unused_options));
}
}
if (!best_count) {
if (!best_required) {
/* cmd did not have all the required opt/pos args of any command */
log_error("Failed to find a matching command definition.");
if (closest_count_ro) {
if (close_ro) {
log_warn("Closest command usage is:");
_print_usage(_cmdline.commands[closest_i].usage, 1);
_print_usage(_cmdline.commands[close_i].usage, 1);
}
return NULL;
}
/*
* FIXME: should there be a config setting to fail the command if an
* unused option or pos arg is set? Or a user prompt to continue or
* not without the ignored args? There are ad hoc checks in various
* commands to fail sometimes if an unused option or pos arg is set.
* Does this mean a per-command flag is needed to determine if that
* command ignores or fails with unused args? e.g. "pvscan vg" would
* fail based on the vg arg, but now it's just ignored.
* If the user passed an option that is not accepted by the matched
* command, then fail.
*
* FIXME: it might be nice to have a config setting that would turn
* these into warnings, and just ignore the unused options.
*/
/*
* Warn about options that are set but are not used by the command.
*/
for (i = 0; i < ARG_COUNT; i++) {
if (!arg_is_set(cmd, i))
continue;
accepted = 0;
/* NB in some cases required_opt_args are optional */
for (j = 0; j < commands[best_i].ro_count; j++) {
if (commands[best_i].required_opt_args[j].opt == i) {
accepted = 1;
break;
}
}
if (accepted)
continue;
for (j = 0; j < commands[best_i].oo_count; j++) {
if (commands[best_i].optional_opt_args[j].opt == i) {
accepted = 1;
break;
}
}
/*
* --type is one option that when set but not accepted by the
* command, will not be ignored to make a match. Perhaps there
* are others like this, and perhaps this is a property that
* should be encoded in args.h?
*/
if (!accepted && (i == type_ARG)) {
log_error("Failed to find a matching command definition with --type.");
return NULL;
}
/* --extents is a special case which is accepted in place of --size */
if (!accepted && (i != extents_ARG)) {
log_error("Invalid option for the specified command (%s %d): %s.",
commands[best_i].command_line_id, best_i, arg_long_option_name(i));
return NULL;
#if 0
log_warn("Ignoring option which is not used by the specified command: %s.",
arg_long_option_name(i));
/* clear so it can't be used when processing. */
cmd->opt_arg_values[i].count = 0;
cmd->opt_arg_values[i].value = NULL;
#endif
if (best_unused_count) {
for (i = 0; i < best_unused_count; i++) {
log_error("Invalid option for command (%s %d): %s.",
commands[best_i].command_line_id, best_i,
arg_long_option_name(best_unused_options[i]));
}
return NULL;
}
/*
* Warn about positional args that are set but are not used by the command.
* If the user provided a positional arg that is not accepted by
* the mached command, then fail.
*
* If the last required_pos_arg or the last optional_pos_arg may repeat,
* then there won't be unused positional args.
*
* Otherwise, warn about positional args that exist beyond the number of
* required + optional pos_args.
* FIXME: same question as above, should there be a config setting
* to just warn/ignore about unused positional args?
*/
count = commands[best_i].rp_count;
@@ -1392,10 +1548,15 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
break;
if (count >= (commands[best_i].rp_count + commands[best_i].op_count)) {
log_warn("Ignoring positional argument which is not used by this command: %s.", argv[count]);
/* clear so it can't be used when processing. */
log_error("Invalid positional argument for command (%s %d): %s.",
commands[best_i].command_line_id, best_i, argv[count]);
/* FIXME: to warn/ignore, clear so it can't be used when processing. */
/*
argv[count] = NULL;
(*argc)--;
*/
return NULL;
}
}
out:
@@ -1426,6 +1587,9 @@ static int _usage(const char *name, int help_count)
if (strcmp(_cmdline.commands[i].name, name))
continue;
if ((_cmdline.commands[i].cmd_flags & CMD_FLAG_SECONDARY_SYNTAX) && (help_count < 3))
continue;
if (strlen(_cmdline.commands[i].desc))
_print_description(i);
@@ -1455,11 +1619,18 @@ static int _usage(const char *name, int help_count)
log_print(". --size Number can be replaced with --extents NumberExtents.");
log_print(". When --name is omitted from lvcreate, a new LV name is");
log_print(" generated with the \"lvol\" prefix and a unique numeral suffix.");
log_print(". The required VG parameter in lvcreate may be omitted when");
log_print(" the VG name is included in another option, e.g. --name VG/LV.");
log_print(". For required options listed in parentheses, e.g. (--A, --B),");
log_print(" any one is required, after which the others are optional.");
log_print(". The _new suffix indicates the VG or LV must not yet exist.");
log_print(". LV followed by _<type> indicates that an LV of the given type");
log_print(" is required. (raid represents any raid<N> type.)");
log_print(". The default output unit is specified by letter, followed by |unit");
log_print(" which represents other possible units: hHbBsSkKmMgGtTpPeE.");
log_print(". Output units are 1024 SI base, regardless of unit capitalization.");
log_print(". Use --help --help --help to print secondary command syntax");
log_print(" formats that are recognized, e.g. for compatibility.");
log_print(". See man pages for short option equivalents of long option names,");
log_print(" and for more detailed descriptions of variable parameters.");
}

View File

@@ -801,10 +801,7 @@ int vgcreate_params_set_from_args(struct cmd_context *cmd,
return 0;
}
if (arg_is_set(cmd, metadatacopies_ARG))
vp_new->vgmetadatacopies = arg_int_value(cmd, metadatacopies_ARG,
DEFAULT_VGMETADATACOPIES);
else if (arg_is_set(cmd, vgmetadatacopies_ARG))
if (arg_is_set(cmd, vgmetadatacopies_ARG))
vp_new->vgmetadatacopies = arg_int_value(cmd, vgmetadatacopies_ARG,
DEFAULT_VGMETADATACOPIES);
else
@@ -2555,10 +2552,14 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
* the LV is skipped and doesn't cause the command to fail.
*/
if (str_list_match_item(&found_arg_lvnames, lv->name)) {
log_error("Operation not permitted on LV %s with type %s.", display_lvname(lv), seg->segtype->name);
log_error("Operation not permitted (%s %d) on LV %s with type %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv), seg->segtype->name);
ret_max = ECMD_FAILED;
} else {
log_warn("Operation not permitted on LV %s with type %s.", display_lvname(lv), seg->segtype->name);
log_warn("Operation not permitted (%s %d) on LV %s with type %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv), seg->segtype->name);
}
continue;
}
@@ -4000,11 +4001,6 @@ int pvcreate_params_from_args(struct cmd_context *cmd, struct pvcreate_params *p
if (pp->pva.pvmetadatacopies < 0)
pp->pva.pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL);
if (pp->pva.pvmetadatacopies > 2) {
log_error("Metadatacopies may only be 0, 1 or 2");
return 0;
}
pp->pva.ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, pp->pva.ba_size);
return 1;

View File

@@ -159,6 +159,8 @@ int segtype_arg(struct cmd_context *cmd, struct arg_values *av);
int alloc_arg(struct cmd_context *cmd, struct arg_values *av);
int locktype_arg(struct cmd_context *cmd, struct arg_values *av);
int readahead_arg(struct cmd_context *cmd, struct arg_values *av);
int vgmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int pvmetadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
/* we use the enums to access the switches */

View File

@@ -116,18 +116,19 @@ val(permission_VAL, permission_arg, "Permission", "rw|r")
val(metadatatype_VAL, metadatatype_arg, "MetadataType", "lvm2|lvm1")
val(units_VAL, string_arg, "Units", "hHbBsSkKmMgGtTpPeE")
val(segtype_VAL, segtype_arg, "SegType", "linear|striped|snapshot|mirror|raid*|thin|cache|thin-pool|cache-pool")
/* FIXME: cling_by_tags is left out of help text because it makes the line wrap */
val(alloc_VAL, alloc_arg, "Alloc", "contiguous|cling|normal|anywhere|inherit")
val(alloc_VAL, alloc_arg, "Alloc", "contiguous|cling|cling_by_tags|normal|anywhere|inherit")
val(locktype_VAL, locktype_arg, "LockType", "sanlock|dlm|none")
val(readahead_VAL, readahead_arg, "Readahead", "auto|none|NumberSectors")
val(metadatacopies_VAL, metadatacopies_arg, "MetadataCopies", "all|unmanaged|Number")
val(vgmetadatacopies_VAL, vgmetadatacopies_arg, "MetadataCopiesVG", "all|unmanaged|Number")
val(pvmetadatacopies_VAL, pvmetadatacopies_arg, "MetadataCopiesPV", "0|1|2")
val(metadatacopies_VAL, metadatacopies_arg, "unused", "unused")
/* this should always be last */
val(VAL_COUNT, NULL, NULL, NULL)
/*
* I suspect many of the following are good candidates for a custom VAL enum
* for the benefit of custom parsing, or custom usage, or both:
* FIXME: I suspect many of the following are good candidates for a custom VAL
* enum for the benefit of custom parsing, or custom usage, or both:
*
* configreport_ARG, configtype_ARG, polloperation_ARG, raidrebuild_ARG,
* raidsyncaction_ARG, raidwritemostly_ARG, reportformat_ARG, syncaction_ARG,

View File

@@ -482,6 +482,9 @@ static int _vgchange_metadata_copies(struct cmd_context *cmd,
{
uint32_t mda_copies = arg_uint_value(cmd, vgmetadatacopies_ARG, DEFAULT_VGMETADATACOPIES);
log_warn("vgchange_metadata_copies new %u vg_mda_copies %u D %u",
mda_copies, vg_mda_copies(vg), DEFAULT_VGMETADATACOPIES);
if (mda_copies == vg_mda_copies(vg)) {
if (vg_mda_copies(vg) == VGMETADATACOPIES_UNMANAGED)
log_warn("Number of metadata copies for VG %s is already unmanaged.",

View File

@@ -157,24 +157,12 @@ int vgconvert(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
if (arg_is_set(cmd, metadatacopies_ARG)) {
log_error("Invalid option --metadatacopies, "
"use --pvmetadatacopies instead.");
return EINVALID_CMD_LINE;
}
if (!(cmd->fmt->features & FMT_MDAS) &&
(arg_is_set(cmd, pvmetadatacopies_ARG) ||
arg_is_set(cmd, metadatasize_ARG))) {
arg_is_set(cmd, pvmetadatacopies_ARG)) {
log_error("Metadata parameters only apply to text format");
return EINVALID_CMD_LINE;
}
if (arg_is_set(cmd, pvmetadatacopies_ARG) &&
arg_int_value(cmd, pvmetadatacopies_ARG, -1) > 2) {
log_error("Metadatacopies may only be 0, 1 or 2");
return EINVALID_CMD_LINE;
}
if (!(cmd->fmt->features & FMT_BAS) &&
arg_is_set(cmd, bootloaderareasize_ARG)) {
log_error("Bootloader area parameters only apply to text format");

View File

@@ -136,12 +136,6 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
if (arg_is_set(cmd, metadatacopies_ARG)) {
log_error("Invalid option --metadatacopies, "
"use --pvmetadatacopies instead.");
return EINVALID_CMD_LINE;
}
vg_name = skip_dev_dir(cmd, argv[0], NULL);
argc--;
argv++;