1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-09-20 05:44:20 +03:00

Compare commits

..

1 Commits

Author SHA1 Message Date
David Teigland
348e372f86 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-11-10 13:41:25 -06:00
19 changed files with 2569 additions and 9952 deletions

View File

@@ -94,6 +94,13 @@ struct cmd_context {
struct arg_values *opt_arg_values;
struct dm_list arg_value_groups;
/*
* Position args remaining after command name
* and --options are removed from original argc/argv.
*/
int position_argc;
char **position_argv;
/*
* Format handlers.
*/

View File

@@ -140,6 +140,13 @@ Makefile: Makefile.in
*) echo "Creating $@" ; $(SED) -e "s+#VERSION#+$(LVM_VERSION)+;s+#DEFAULT_SYS_DIR#+$(DEFAULT_SYS_DIR)+;s+#DEFAULT_ARCHIVE_DIR#+$(DEFAULT_ARCHIVE_DIR)+;s+#DEFAULT_BACKUP_DIR#+$(DEFAULT_BACKUP_DIR)+;s+#DEFAULT_PROFILE_DIR#+$(DEFAULT_PROFILE_DIR)+;s+#DEFAULT_CACHE_DIR#+$(DEFAULT_CACHE_DIR)+;s+#DEFAULT_LOCK_DIR#+$(DEFAULT_LOCK_DIR)+;s+#CLVMD_PATH#+@CLVMD_PATH@+;s+#LVM_PATH#+@LVM_PATH@+;s+#DEFAULT_RUN_DIR#+@DEFAULT_RUN_DIR@+;s+#DEFAULT_PID_DIR#+@DEFAULT_PID_DIR@+;s+#SYSTEMD_GENERATOR_DIR#+$(SYSTEMD_GENERATOR_DIR)+;s+#DEFAULT_MANGLING#+$(DEFAULT_MANGLING)+;" $< > $@ ;; \
esac
ccmd: ../tools/create-commands.c
$(CC) ../tools/create-commands.c -o ccmd
generate: ccmd
./ccmd --output man -s 0 -p 1 -c lvcreate ../tools/command-lines.in > lvcreate.8.a
cat lvcreate.8.a lvcreate.8.b > lvcreate.8.in
install_man5: $(MAN5)
$(INSTALL) -d $(MAN5DIR)
$(INSTALL_DATA) $(MAN5) $(MAN5DIR)/

View File

@@ -41,9 +41,9 @@ invalid lvcreate -l 1 --type cache $vg
invalid lvcreate -l 1 -i 2 --type cache-pool $vg
# Fails as it needs to see VG content
fail lvcreate -l 1 --type cache --cachepool $vg/pool1
invalid lvcreate -l 1 --type cache --cachepool pool2 $vg
fail lvcreate -l 1 --type cache --cachepool pool2 $vg
fail lvcreate -l 1 --cache $vg/pool3
invalid lvcreate -l 1 -H --cachepool pool4 $vg
fail lvcreate -l 1 -H --cachepool pool4 $vg
fail lvcreate -l 1 -H --name $lv2 $vg/pool5
fail lvcreate -l 1 -H --name $lv3 --cachepool $vg/pool6
fail lvcreate -l 1 -H --name $vg/$lv4 --cachepool pool7
@@ -52,7 +52,7 @@ fail lvcreate -l 1 -H --name $vg/$lv4 --cachepool pool7
# So we require cache pool to exist and need to fail when it's missing.
#
# --cachepool gives implicit --cache
invalid lvcreate -l 1 --cachepool pool8 $vg
fail lvcreate -l 1 --cachepool pool8 $vg
# no size specified
invalid lvcreate --cachepool pool $vg 2>&1 | tee err

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

@@ -17,6 +17,8 @@
* Put all long args that don't have a corresponding short option first.
*/
/* *INDENT-OFF* */
arg(ARG_UNUSED, '-', "", 0, 0, 0) /* place holder for unused 0 value */
arg(abort_ARG, '\0', "abort", 0, 0, 0)
arg(activationmode_ARG, '\0', "activationmode", activationmode_VAL, 0, 0)
arg(addtag_ARG, '\0', "addtag", tag_VAL, ARG_GROUPABLE, 0)
@@ -31,8 +33,8 @@ arg(cachemode_ARG, '\0', "cachemode", cachemode_VAL, 0, 0)
arg(cachepool_ARG, '\0', "cachepool", lv_VAL, 0, 0)
arg(commandprofile_ARG, '\0', "commandprofile", string_VAL, 0, 0)
arg(config_ARG, '\0', "config", string_VAL, 0, 0)
arg(configreport_ARG, '\0', "configreport", string_VAL, ARG_GROUPABLE, 1)
arg(configtype_ARG, '\0', "typeconfig", string_VAL, 0, 0)
arg(configreport_ARG, '\0', "configreport", configreport_VAL, ARG_GROUPABLE, 1)
arg(configtype_ARG, '\0', "typeconfig", configtype_VAL, 0, 0)
arg(corelog_ARG, '\0', "corelog", 0, 0, 0)
arg(dataalignment_ARG, '\0', "dataalignment", sizekb_VAL, 0, 0)
arg(dataalignmentoffset_ARG, '\0', "dataalignmentoffset", sizekb_VAL, 0, 0)
@@ -81,26 +83,26 @@ arg(noudevsync_ARG, '\0', "noudevsync", 0, 0, 0)
arg(originname_ARG, '\0', "originname", lv_VAL, 0, 0)
arg(physicalvolumesize_ARG, '\0', "setphysicalvolumesize", sizemb_VAL, 0, 0)
arg(poll_ARG, '\0', "poll", bool_VAL, 0, 0)
arg(polloperation_ARG, '\0', "polloperation", string_VAL, 0, 0)
arg(polloperation_ARG, '\0', "polloperation", polloperation_VAL, 0, 0)
arg(pooldatasize_ARG, '\0', "pooldatasize", sizemb_VAL, 0, 0)
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(raidrebuild_ARG, '\0', "raidrebuild", string_VAL, ARG_GROUPABLE, 0)
arg(pvmetadatacopies_ARG, '\0', "pvmetadatacopies", pvmetadatacopies_VAL, 0, 0)
arg(raidrebuild_ARG, '\0', "raidrebuild", pv_VAL, ARG_GROUPABLE, 0)
arg(raidmaxrecoveryrate_ARG, '\0', "raidmaxrecoveryrate", sizekb_VAL, 0, 0)
arg(raidminrecoveryrate_ARG, '\0', "raidminrecoveryrate", sizekb_VAL, 0, 0)
arg(raidsyncaction_ARG, '\0', "raidsyncaction", string_VAL, 0, 0)
arg(raidsyncaction_ARG, '\0', "raidsyncaction", syncaction_VAL, 0, 0)
arg(raidwritebehind_ARG, '\0', "raidwritebehind", number_VAL, 0, 0)
arg(raidwritemostly_ARG, '\0', "raidwritemostly", string_VAL, ARG_GROUPABLE, 0)
arg(raidwritemostly_ARG, '\0', "raidwritemostly", writemostly_VAL, ARG_GROUPABLE, 0)
arg(readonly_ARG, '\0', "readonly", 0, 0, 0)
arg(refresh_ARG, '\0', "refresh", 0, 0, 0)
arg(removemissing_ARG, '\0', "removemissing", 0, 0, 0)
arg(rebuild_ARG, '\0', "rebuild", pv_VAL, ARG_GROUPABLE, 0)
arg(repair_ARG, '\0', "repair", 0, 0, 0)
arg(replace_ARG, '\0', "replace", pv_VAL, ARG_GROUPABLE, 0)
arg(reportformat_ARG, '\0', "reportformat", string_VAL, 0, 0)
arg(reportformat_ARG, '\0', "reportformat", reportformat_VAL, 0, 0)
arg(restorefile_ARG, '\0', "restorefile", string_VAL, 0, 0)
arg(restoremissing_ARG, '\0', "restoremissing", 0, 0, 0)
arg(resync_ARG, '\0', "resync", 0, 0, 0)
@@ -116,7 +118,7 @@ arg(splitsnapshot_ARG, '\0', "splitsnapshot", 0, 0, 0)
arg(showdeprecated_ARG, '\0', "showdeprecated", 0, 0, 0)
arg(showunsupported_ARG, '\0', "showunsupported", 0, 0, 0)
arg(stripes_long_ARG, '\0', "stripes", number_VAL, 0, 0)
arg(syncaction_ARG, '\0', "syncaction", string_VAL, 0, 0) /* FIXME Use custom VAL */
arg(syncaction_ARG, '\0', "syncaction", syncaction_VAL, 0, 0)
arg(sysinit_ARG, '\0', "sysinit", 0, 0, 0)
arg(systemid_ARG, '\0', "systemid", string_VAL, 0, 0)
arg(thinpool_ARG, '\0', "thinpool", lv_VAL, 0, 0)
@@ -133,14 +135,14 @@ 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)
arg(withspaces_ARG, '\0', "withspaces", 0, 0, 0)
arg(withversions_ARG, '\0', "withversions", 0, 0, 0)
arg(writebehind_ARG, '\0', "writebehind", number_VAL, 0, 0)
arg(writemostly_ARG, '\0', "writemostly", string_VAL, ARG_GROUPABLE, 0)
arg(writemostly_ARG, '\0', "writemostly", writemostly_VAL, ARG_GROUPABLE, 0)
/* Allow some variations */
arg(allocation_ARG, '\0', "allocation", bool_VAL, 0, 0)

View File

@@ -1,129 +0,0 @@
/* Do not edit. This file is generated by scripts/create-commands */
/* using command definitions from scripts/command-lines.in */
#define COMMAND_COUNT 152
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_general_to_raid_CMD,
lvconvert_to_mirrored_or_change_image_count_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_striped_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_CMD,
vgsplit_by_lv_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,29 +51,8 @@ struct command_name {
*/
/* arg_def flags */
#define ARG_DEF_FLAG_NEW 1 << 0
#define ARG_DEF_FLAG_MAY_REPEAT 1 << 1
/* arg_def lv_types */
enum {
ARG_DEF_LV_ANY = 0,
ARG_DEF_LV_LINEAR = 1 << 0,
ARG_DEF_LV_STRIPED = 1 << 1,
ARG_DEF_LV_SNAPSHOT = 1 << 2,
ARG_DEF_LV_MIRROR = 1 << 3,
ARG_DEF_LV_RAID = 1 << 4,
ARG_DEF_LV_RAID0 = 1 << 5,
ARG_DEF_LV_RAID1 = 1 << 6,
ARG_DEF_LV_RAID4 = 1 << 7,
ARG_DEF_LV_RAID5 = 1 << 8,
ARG_DEF_LV_RAID6 = 1 << 9,
ARG_DEF_LV_RAID10 = 1 << 10,
ARG_DEF_LV_THIN = 1 << 11,
ARG_DEF_LV_THINPOOL = 1 << 12,
ARG_DEF_LV_CACHE = 1 << 13,
ARG_DEF_LV_CACHEPOOL = 1 << 14,
ARG_DEF_LV_LAST = 1 << 15,
};
#define ARG_DEF_FLAG_NEW 1 << 0
#define ARG_DEF_FLAG_MAY_REPEAT 1 << 1
static inline int val_bit_is_set(uint64_t val_bits, int val_enum)
{
@@ -82,16 +61,36 @@ static inline int val_bit_is_set(uint64_t val_bits, int val_enum)
static inline uint64_t val_enum_to_bit(int val_enum)
{
return 1 << val_enum;
return (1ULL << val_enum);
}
static inline int lvp_bit_is_set(uint64_t lvp_bits, int lvp_enum)
{
return (lvp_bits & (1 << lvp_enum)) ? 1 : 0;
}
static inline uint64_t lvp_enum_to_bit(int lvp_enum)
{
return (1ULL << lvp_enum);
}
static inline int lvt_bit_is_set(uint64_t lvt_bits, int lvt_enum)
{
return (lvt_bits & (1 << lvt_enum)) ? 1 : 0;
}
static inline uint64_t lvt_enum_to_bit(int lvt_enum)
{
return (1ULL << lvt_enum);
}
/* Description a value that follows an option or exists in a position. */
struct arg_def {
uint64_t val_bits; /* bits of x_VAL, can be multiple for pos_arg */
uint64_t lvt_bits; /* lvt_enum_to_bit(x_LVT) for lv_VAL, can be multiple */
uint64_t num; /* a literal number for conststr_VAL */
const char *str; /* a literal string for constnum_VAL */
uint32_t lv_types; /* ARG_DEF_LV_, for lv_VAL, can be multiple */
uint32_t flags; /* ARG_DEF_FLAG_ */
};
@@ -109,6 +108,37 @@ struct pos_arg {
struct arg_def def; /* defines accepted values */
};
/*
* When all values before the require|invalid match a given command,
* then all the values after are verified to be true|false.
*
* Rules that include only options can be verified before the VG
* is read. Otherwise, the rules are verified in process_each
* after the VG is read.
*
* RULE: --opt|LV_type|lv_is_prop|all require|invalid --opt|LV_type|lv_is_prop
* RULE: --opt1 invalid --opt2
* RULE: --opt1 require --opt2
* RULE: --opt1 LV_type1 lv_is_prop1 require --opt2
* RULE: --opt1 LV_type1 require lv_is_prop1
* RULE: LV_type1 require lv_is_prop1
*/
#define RULE_INVALID 1
#define RULE_REQUIRE 2
struct cmd_rule {
int opt; /* apply rule when this option is set (foo_ARG) */
uint64_t lvt_bits; /* apply rule when LV has one of these types (lvt_enum_to_bit) */
uint64_t lvp_bits; /* apply rule when LV has all these properties (lvp_enum_to_bit) */
uint32_t rule; /* RULE_INVALID, RULE_REQUIRE: check values must [not] be true */
int check_opt; /* this option must [not] be set */
uint64_t check_lvt_bits; /* LV must [not] have this type */
uint64_t check_lvp_bits; /* LV must [not] have these properties */
};
/*
* CMD_RO_ARGS needs to accomodate a list of options,
* of which one is required after which the rest are
@@ -118,12 +148,14 @@ struct pos_arg {
#define CMD_OO_ARGS 150 /* optional opt args */
#define CMD_RP_ARGS 8 /* required positional args */
#define CMD_OP_ARGS 8 /* optional positional args */
#define CMD_MAX_RULES 16 /* max number of rules per command def */
/*
* 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 {
@@ -157,6 +189,8 @@ struct command {
/* optional positional args */
struct pos_arg optional_pos_args[CMD_OP_ARGS];
struct cmd_rule rules[CMD_MAX_RULES];
int ro_count;
int oo_count;
int rp_count;
@@ -164,6 +198,8 @@ struct command {
/* used for processing current position */
int pos_count;
int rule_count;
};
#endif

File diff suppressed because it is too large Load Diff

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

@@ -79,6 +79,25 @@ struct command_name command_names[MAX_COMMAND_NAMES] = {
#undef xx
};
/*
* Table of LV properties
*/
static struct lv_props _lv_props[LVP_COUNT + 1] = {
#define lvp(a, b, c) {a, b, c},
#include "lv_props.h"
#undef lvp
};
/*
* Table of LV types
*/
static struct lv_types _lv_types[LVT_COUNT + 1] = {
#define lvt(a, b, c) {a, b, c},
#include "lv_types.h"
#undef lvt
};
/*
* Table of valid command lines
*/
@@ -96,9 +115,34 @@ struct command_function command_functions[COMMAND_ID_COUNT] = {
{ lvmconfig_general_CMD, lvmconfig },
};
#if 0
{ lvchange_properties_CMD, lvchange_properties_cmd },
{ lvchange_activate_CMD, lvchange_activate_cmd },
{ lvchange_refresh_CMD, lvchange_refresh_cmd },
/* all raid-related type conversions */
{ lvconvert_raid_types_CMD, lvconvert_raid_types_fn },
/* raid-related utilities (move into lvconvert_raid_types?) */
{ lvconvert_split_mirror_images_CMD, lvconvert_split_mirror_images_fn },
{ lvconvert_change_mirrorlog_CMD, lvconvert_change_mirrorlog_fn },
/* utilities for creating/maintaining thin and cache objects. */
{ lvconvert_to_thin_with_external_CMD, lvconvert_to_thin_with_external_fn },
{ lvconvert_to_cache_vol_CMD, lvconvert_to_cache_vol_fn },
{ lvconvert_to_thinpool_CMD, lvconvert_to_thinpool_fn },
{ lvconvert_to_cachepool_CMD, lvconvert_to_cachepool_fn },
{ lvconvert_split_and_keep_cachepool_CMD, lvconvert_split_and_keep_cachepool_fn },
{ lvconvert_split_and_delete_cachepool_CMD, lvconvert_split_and_delete_cachepool_fn },
{ lvconvert_swap_pool_metadata_CMD, lvconvert_swap_pool_metadata_fn },
/* utilities related to snapshots and repair. */
{ lvconvert_merge_CMD, lvconvert_merge_fn },
{ lvconvert_combine_split_snapshot_CMD, lvconvert_combine_split_snapshot_fn },
{ lvconvert_repair_pvs_or_thinpool_CMD, lvconvert_repair_pvs_or_thinpool_fn },
{ lvconvert_replace_pv_CMD, lvconvert_replace_pv_fn },
{ lvconvert_split_cow_snapshot_CMD, lvconvert_split_cow_snapshot_fn },
{ lvconvert_poll_start_CMD, lvconvert_poll_start_fn },
#endif
/* Command line args */
@@ -733,23 +777,105 @@ 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;
}
int polloperation_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strcmp(av->value, "pvmove") ||
!strcmp(av->value, "convert") ||
!strcmp(av->value, "merge") ||
!strcmp(av->value, "merge_thin"))
return 1;
return 0;
}
int writemostly_arg(struct cmd_context *cmd, struct arg_values *av)
{
/* Could we verify that a PV arg looks like /dev/foo ? */
return 1;
}
int syncaction_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strcmp(av->value, "check") ||
!strcmp(av->value, "repair"))
return 1;
return 0;
}
int reportformat_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strcmp(av->value, "basic") ||
!strcmp(av->value, "json"))
return 1;
return 0;
}
int configreport_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strcmp(av->value, "log") ||
!strcmp(av->value, "vg") ||
!strcmp(av->value, "lv") ||
!strcmp(av->value, "pv") ||
!strcmp(av->value, "pvseg") ||
!strcmp(av->value, "seg"))
return 1;
return 0;
}
int configtype_arg(struct cmd_context *cmd, struct arg_values *av)
{
if (!strcmp(av->value, "current") ||
!strcmp(av->value, "default") ||
!strcmp(av->value, "diff") ||
!strcmp(av->value, "full") ||
!strcmp(av->value, "list") ||
!strcmp(av->value, "missing") ||
!strcmp(av->value, "new") ||
!strcmp(av->value, "profilable") ||
!strcmp(av->value, "profilable-command") ||
!strcmp(av->value, "profilable-metadata"))
return 1;
return 0;
}
/*
* FIXME: there's been a confusing mixup among:
* resizeable, resizable, allocatable, allocation.
@@ -790,6 +916,14 @@ static int _opt_standard_to_synonym(const char *cmd_name, int opt)
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;
}
@@ -819,6 +953,12 @@ static int _opt_synonym_to_standard(const char *cmd_name, int opt)
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;
}
@@ -958,6 +1098,20 @@ void lvm_register_commands(void)
_set_valid_args_for_command_name(i);
}
struct lv_props *get_lv_prop(int lvp_enum)
{
if (!lvp_enum)
return NULL;
return &_lv_props[lvp_enum];
}
struct lv_types *get_lv_type(int lvt_enum)
{
if (!lvt_enum)
return NULL;
return &_lv_types[lvt_enum];
}
/*
* Also see merge_synonym(). The command definitions
* are written using just one variation of the option
@@ -1022,12 +1176,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. */
@@ -1042,6 +1197,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;
}
@@ -1206,17 +1391,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;
}
@@ -1230,8 +1412,10 @@ static void _print_description(int ci)
break;
}
if (bi)
if (bi) {
buf[bi] = '\0';
log_print("%s", buf);
}
}
/*
@@ -1253,19 +1437,27 @@ static void _print_description(int ci)
* set to match.
*
* required_pos_args[0].types & select_VAL means
* cmd->argv[] in that pos can be NULL if arg_is_set(select_ARG)
* 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, match_type;
int best_i = 0, best_count = 0, best_type;
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;
int check_is_set;
name = last_path_component(path);
@@ -1277,31 +1469,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_type = 0;
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++;
}
}
@@ -1310,12 +1505,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;
}
}
@@ -1324,134 +1519,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) || (match_type > best_type)) {
/* 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;
}
/*
* Warn about options that are set but are not used by the command.
* 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.
*/
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;
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 == opt_enum) {
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 == opt_enum) {
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 && (opt_enum == type_ARG)) {
log_error("Failed to find a matching command definition with --type.");
return NULL;
}
/*
* FIXME: should there be a config setting to just warn and ignore an
* option that is not used/accepted by the command def? Same for an
* an unused positional arg?
*/
/* --extents is a special case which is accepted in place of --size */
if (!accepted && (opt_enum != 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(opt_enum));
return NULL;
#if 0
log_warn("Ignoring option which is not used by the specified command: %s.",
arg_long_option_name(opt_enum));
/* clear so it can't be used when processing. */
cmd->opt_arg_values[opt_enum].count = 0;
cmd->opt_arg_values[opt_enum].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;
@@ -1467,12 +1667,53 @@ 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;
}
}
/*
* Check any rules related to option combinations. If the only
* qualification for this rule to apply is that rule->opt is set (it's
* not also related to a specific LV type or property), and the rule
* check is related to another option, then we can do the option
* validation here.
*/
for (i = 0; i < commands[best_i].rule_count; i++) {
struct cmd_rule *rule;
rule = &commands[best_i].rules[i];
if (rule->opt && rule->check_opt && !rule->lvt_bits && !rule->lvp_bits &&
arg_is_set(cmd, rule->opt)) {
check_is_set = arg_is_set(cmd, rule->check_opt);
if (check_is_set && (rule->rule == RULE_INVALID)) {
log_error("Invalid option combination for command (%s %d): %s and %s",
commands[best_i].command_line_id, best_i,
arg_long_option_name(rule->opt),
arg_long_option_name(rule->check_opt));
return NULL;
}
if (!check_is_set && (rule->rule == RULE_REQUIRE)) {
log_error("Invalid option usage for command (%s %d): %s requires %s",
commands[best_i].command_line_id, best_i,
arg_long_option_name(rule->opt),
arg_long_option_name(rule->check_opt));
return NULL;
}
}
}
out:
log_debug("command line id: %s %d", commands[best_i].command_line_id, best_i);
@@ -1501,6 +1742,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);
@@ -1529,12 +1773,19 @@ static int _usage(const char *name, int help_count)
log_print(" be omitted if the --select option is used.");
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(" generated with the \"lvol\" prefix and a unique numeric 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.");
}
@@ -2361,6 +2612,12 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
if (!(cmd->command = _find_command(cmd, cmd->name, &argc, argv)))
return EINVALID_CMD_LINE;
/*
* Remaining position args after command name and --options are removed.
*/
cmd->position_argc = argc;
cmd->position_argv = argv;
set_cmd_name(cmd->name);
if (arg_is_set(cmd, backgroundfork_ARG)) {

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
@@ -2329,6 +2326,407 @@ static struct lv_segment _historical_lv_segment = {
.origin_list = DM_LIST_HEAD_INIT(_historical_lv_segment.origin_list),
};
static void lvp_bits_to_str(uint64_t bits, char *buf, int len)
{
struct lv_props *prop;
int lvp_enum;
int pos = 0;
int ret;
for (lvp_enum = 0; lvp_enum < LVP_COUNT; lvp_enum++) {
if (!(prop = get_lv_prop(lvp_enum)))
continue;
if (lvp_bit_is_set(bits, lvp_enum)) {
ret = snprintf(buf + pos, len - pos, "%s ", prop->name);
if (ret >= len - pos)
break;
pos += ret;
}
}
buf[len - 1] = '\0';
}
static const char *lvt_bits_to_str(uint64_t bits)
{
struct lv_types *type;
int lvt_enum;
for (lvt_enum = 0; lvt_enum < LVT_COUNT; lvt_enum++) {
if (!(type = get_lv_type(lvt_enum)))
continue;
if (lvt_bit_is_set(bits, lvt_enum))
return type->name;
}
return "unknown";
}
/*
* This is the lv_prop function pointer used for lv_is_foo() #defines.
* Alternatively, lv_is_foo() could all be turned into functions.
*/
static int _lv_is_prop(struct cmd_context *cmd, struct logical_volume *lv, int lvp_enum)
{
switch (lvp_enum) {
case is_locked_LVP:
return lv_is_locked(lv);
case is_partial_LVP:
return lv_is_partial(lv);
case is_virtual_LVP:
return lv_is_virtual(lv);
case is_merging_LVP:
return lv_is_merging(lv);
case is_merging_origin_LVP:
return lv_is_merging_origin(lv);
case is_converting_LVP:
return lv_is_converting(lv);
case is_external_origin_LVP:
return lv_is_external_origin(lv);
case is_virtual_origin_LVP:
return lv_is_virtual_origin(lv);
case is_not_synced_LVP:
return lv_is_not_synced(lv);
case is_pending_delete_LVP:
return lv_is_pending_delete(lv);
case is_error_when_full_LVP:
return lv_is_error_when_full(lv);
case is_pvmove_LVP:
return lv_is_pvmove(lv);
case is_removed_LVP:
return lv_is_removed(lv);
case is_thinpool_data_LVP:
return lv_is_thin_pool_data(lv);
case is_thinpool_metadata_LVP:
return lv_is_thin_pool_metadata(lv);
case is_cachepool_data_LVP:
return lv_is_cache_pool_data(lv);
case is_cachepool_metadata_LVP:
return lv_is_cache_pool_metadata(lv);
case is_mirror_image_LVP:
return lv_is_mirror_image(lv);
case is_mirror_log_LVP:
return lv_is_mirror_log(lv);
case is_raid_image_LVP:
return lv_is_raid_image(lv);
case is_raid_metadata_LVP:
return lv_is_raid_metadata(lv);
case is_origin_LVP:
return lv_is_origin(lv);
case is_thin_origin_LVP:
return lv_is_thin_origin(lv, NULL);
case is_cache_origin_LVP:
return lv_is_cache_origin(lv);
case is_merging_cow_LVP:
return lv_is_merging_cow(lv);
case is_cow_covering_origin_LVP:
return lv_is_cow_covering_origin(lv);
case is_visible_LVP:
return lv_is_visible(lv);
case is_historical_LVP:
return lv_is_historical(lv);
case is_raid_with_tracking_LVP:
return lv_is_raid_with_tracking(lv);
default:
log_error(INTERNAL_ERROR "unknown lv property value lvp_enum %d", lvp_enum);
}
return 0;
}
/*
* Check if an LV matches a given LV type enum.
*/
static int _lv_is_type(struct cmd_context *cmd, struct logical_volume *lv, int lvt_enum)
{
struct lv_segment *seg = first_seg(lv);
switch (lvt_enum) {
case striped_LVT:
return seg_is_striped(seg);
case linear_LVT:
return seg_is_linear(seg);
case snapshot_LVT:
return lv_is_cow(lv);
case thin_LVT:
return lv_is_thin_volume(lv);
case thinpool_LVT:
return lv_is_thin_pool(lv);
case cache_LVT:
return lv_is_cache(lv);
case cachepool_LVT:
return lv_is_cache_pool(lv);
case mirror_LVT:
return lv_is_mirror(lv);
case raid_LVT:
return lv_is_raid(lv);
case raid0_LVT:
return seg_is_raid0(seg);
case raid1_LVT:
return seg_is_raid1(seg);
case raid4_LVT:
return seg_is_raid4(seg);
#if 0
case raid5_LVT:
return seg_is_raid5(seg);
case raid6_LVT:
return seg_is_raid6(seg);
#endif
case raid10_LVT:
return seg_is_raid10(seg);
default:
log_error(INTERNAL_ERROR "unknown lv type value lvt_enum %d", lvt_enum);
}
return 0;
}
static int _get_lvt_enum(struct logical_volume *lv)
{
struct lv_segment *seg = first_seg(lv);
if (seg_is_striped(seg))
return striped_LVT;
if (seg_is_linear(seg))
return linear_LVT;
if (lv_is_cow(lv))
return snapshot_LVT;
if (lv_is_thin_volume(lv))
return thin_LVT;
if (lv_is_thin_pool(lv))
return thinpool_LVT;
if (lv_is_cache(lv))
return cache_LVT;
if (lv_is_cache_pool(lv))
return cachepool_LVT;
if (lv_is_mirror(lv))
return mirror_LVT;
if (lv_is_raid(lv))
return raid_LVT;
if (seg_is_raid0(seg))
return raid0_LVT;
if (seg_is_raid1(seg))
return raid1_LVT;
if (seg_is_raid4(seg))
return raid4_LVT;
#if 0
if (seg_is_raid5(seg))
return raid5_LVT;
if (seg_is_raid6(seg))
return raid6_LVT;
#endif
if (seg_is_raid10(seg))
return raid10_LVT;
log_error(INTERNAL_ERROR "unknown lv type for %s", display_lvname(lv));
return 0;
}
/* Call lv_is_<type> for each <type>_LVT bit set in lvt_bits. */
static int _lv_types_match(struct cmd_context *cmd, struct logical_volume *lv, uint64_t lvt_bits)
{
struct lv_types *type;
int lvt_enum;
int ret = 1;
for (lvt_enum = 1; lvt_enum < LVT_COUNT; lvt_enum++) {
if (!lvt_bit_is_set(lvt_bits, lvt_enum))
continue;
if (!(type = get_lv_type(lvt_enum)))
continue;
/*
* All types are currently handled by _lv_is_type()
* because lv_is_type() are #defines and not exposed
* in tools.h
*/
if (!type->fn)
ret = _lv_is_type(cmd, lv, lvt_enum);
else
ret = type->fn(cmd, lv);
}
return ret;
}
/* Call lv_is_<prop> for each <prop>_LVP bit set in lvp_bits. */
static int _lv_props_match(struct cmd_context *cmd, struct logical_volume *lv, uint64_t lvp_bits,
uint64_t *match_bits, uint64_t *unmatch_bits)
{
struct lv_props *prop;
int lvp_enum;
int ret = 1;
if (match_bits)
*match_bits = 0;
if (unmatch_bits)
*unmatch_bits = 0;
for (lvp_enum = 1; lvp_enum < LVP_COUNT; lvp_enum++) {
if (!lvp_bit_is_set(lvp_bits, lvp_enum))
continue;
if (!(prop = get_lv_prop(lvp_enum)))
continue;
if (!prop->fn)
ret = _lv_is_prop(cmd, lv, lvp_enum);
else
ret = prop->fn(cmd, lv);
if (match_bits && ret)
*match_bits |= lvp_enum_to_bit(lvp_enum);
if (unmatch_bits && !ret)
*unmatch_bits |= lvp_enum_to_bit(lvp_enum);
}
return ret;
}
/*
* If the command definition specifies one required positional
* LV (possibly repeatable), and specifies accepted LV types,
* then verify that the LV being processed matches one of those
* types.
*
* process_each_lv() can only be used for commands that have
* one positional LV arg (optionally repeating, where each is
* processed independently.) It cannot work for commands that
* have different required LVs in designated positions, like
* 'lvrename LV1 LV2', where each LV is not processed
* independently. That means that this LV type check only
* needs to check the lv_type of the first positional arg.
*/
static int _check_lv_types(struct cmd_context *cmd, struct logical_volume *lv)
{
int ret = 1;
if ((cmd->command->rp_count == 1) &&
val_bit_is_set(cmd->command->required_pos_args[0].def.val_bits, lv_VAL) &&
cmd->command->required_pos_args[0].def.lvt_bits) {
ret = _lv_types_match(cmd, lv, cmd->command->required_pos_args[0].def.lvt_bits);
if (!ret) {
int lvt_enum = _get_lvt_enum(lv);
struct lv_types *type = get_lv_type(lvt_enum);
log_warn("Operation on LV %s which has invalid type %s.",
display_lvname(lv), type ? type->name : "unknown");
}
}
return ret;
}
/* Check if LV passes each rule specified in command definition. */
static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
{
char buf[64];
struct cmd_rule *rule;
struct lv_types *lvtype = NULL;
int lvt_enum;
int opt_is_set, lv_types_match, lv_props_match;
uint64_t match_bits, unmatch_bits;
int ret = 1;
int i;
lvt_enum = _get_lvt_enum(lv);
if (lvt_enum)
lvtype = get_lv_type(lvt_enum);
for (i = 0; i < cmd->command->rule_count; i++) {
rule = &cmd->command->rules[i];
/*
* For the rule to apply, any option, LV type or LV property
* that is specified before the not|and rule needs to match the
* command/LV.
*
* If all qualifications are zero (!opt && !lvt_bits && !lvp_bits),
* then there are no qualifications and the rule always applies.
*/
if (rule->opt && !arg_is_set(cmd, rule->opt))
continue;
if (rule->lvt_bits && !_lv_types_match(cmd, lv, rule->lvt_bits))
continue;
if (rule->lvp_bits && !_lv_props_match(cmd, lv, rule->lvp_bits, NULL, NULL))
continue;
/*
* Check the option, LV types, LV properties specified after
* the not|and rule.
*/
if (rule->check_opt)
opt_is_set = arg_is_set(cmd, rule->check_opt);
if (rule->check_lvt_bits)
lv_types_match = _lv_types_match(cmd, lv, rule->check_lvt_bits);
if (rule->check_lvp_bits)
lv_props_match = _lv_props_match(cmd, lv, rule->check_lvp_bits, &match_bits, &unmatch_bits);
/*
* Evaluate if the check results pass based on the rule.
*/
if (rule->check_opt && (rule->rule == RULE_INVALID) && opt_is_set) {
log_warn("Command option %s invalid with option %s.",
arg_long_option_name(rule->opt), arg_long_option_name(rule->check_opt));
ret = 0;
}
if (rule->check_opt && (rule->rule == RULE_REQUIRE) && !opt_is_set) {
log_warn("Command option %s requires option %s.",
arg_long_option_name(rule->opt), arg_long_option_name(rule->check_opt));
ret = 0;
}
if (rule->check_lvt_bits && (rule->rule == RULE_INVALID) && lv_types_match) {
log_warn("Command on LV %s with invalid type: %s.",
display_lvname(lv), lvtype ? lvtype->name : "unknown");
ret = 0;
}
if (rule->check_lvt_bits && (rule->rule == RULE_REQUIRE) && !lv_types_match) {
log_warn("Command on LV %s type %s requires type: %s.",
display_lvname(lv), lvtype ? lvtype->name : "unknown",
lvt_bits_to_str(rule->check_lvt_bits));
ret = 0;
}
if (rule->check_lvp_bits && (rule->rule == RULE_INVALID) && lv_props_match) {
memset(buf, 0, sizeof(buf));
lvp_bits_to_str(match_bits, buf, sizeof(buf));
log_warn("Command on LV %s with invalid properties: %s.",
display_lvname(lv), buf);
ret = 0;
}
if (rule->check_lvp_bits && (rule->rule == RULE_REQUIRE) && !lv_props_match) {
memset(buf, 0, sizeof(buf));
lvp_bits_to_str(unmatch_bits, buf, sizeof(buf));
log_warn("Command on LV %s requires properties: %s.",
display_lvname(lv), buf);
ret = 0;
}
}
return ret;
}
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct dm_list *arg_lvnames, const struct dm_list *tags_in,
int stop_on_error,
@@ -2353,9 +2751,6 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct dm_list found_arg_lvnames;
struct glv_list *glvl, *tglvl;
int do_report_ret_code = 1;
uint32_t lv_types;
struct logical_volume *lv;
struct lv_segment *seg;
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_LV);
@@ -2507,65 +2902,28 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
continue;
/*
* If the command definition specifies one required positional
* LV (possibly repeatable), and specifies accepted LV types,
* then verify that the LV being processed matches one of those
* types.
*
* process_each_lv() can only be used for commands that have
* one positional LV arg (optionally repeating, where each is
* processed independently.) It cannot work for commands that
* have different required LVs in designated positions, like
* 'lvrename LV1 LV2', where each LV is not processed
* independently. That means that this LV type check only
* needs to check the lv_type of the first positional arg.
*
* There is one command that violates this rule by stealing
* the first positional LV arg before calling process_each_lv:
* lvconvert --type snapshot LV_linear_striped_raid LV_snapshot
* This code cannot validate that case. process_each_lv() sees
* a single LV name arg, but it's in pos 2. Could we work around
* this by looking at the final positional arg rather than always
* looking at pos 1?
*
* This only validates types for required LV positional args
* (currently there are no command specifications that include
* specific LV types in optional positional args.)
* The command definition may include restrictions on the
* types and properties of LVs that can be processed.
*/
if ((cmd->command->rp_count == 1) &&
val_bit_is_set(cmd->command->required_pos_args[0].def.val_bits, lv_VAL) &&
cmd->command->required_pos_args[0].def.lv_types) {
if (!_check_lv_types(cmd, lvl->lv)) {
log_warn("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
if (str_list_match_item(&found_arg_lvnames, lvl->lv->name))
ret_max = ECMD_FAILED;
/* FIXME: include this result in report log? */
continue;
}
lv_types = cmd->command->required_pos_args[0].def.lv_types;
lv = lvl->lv;
seg = first_seg(lv);
if ((lv_is_cow(lv) && !(lv_types & ARG_DEF_LV_SNAPSHOT)) ||
(lv_is_thin_volume(lv) && !(lv_types & ARG_DEF_LV_THIN)) ||
(lv_is_thin_pool(lv) && !(lv_types & ARG_DEF_LV_THINPOOL)) ||
(lv_is_cache(lv) && !(lv_types & ARG_DEF_LV_CACHE)) ||
(lv_is_cache_pool(lv) && !(lv_types & ARG_DEF_LV_CACHEPOOL)) ||
(lv_is_mirror(lv) && !(lv_types & ARG_DEF_LV_MIRROR)) ||
(lv_is_raid(lv) && !(lv_types & (ARG_DEF_LV_RAID | ARG_DEF_LV_RAID0 | ARG_DEF_LV_RAID1 | ARG_DEF_LV_RAID4 | ARG_DEF_LV_RAID5 | ARG_DEF_LV_RAID6 | ARG_DEF_LV_RAID10))) ||
(segtype_is_striped(seg->segtype) && !(lv_types & ARG_DEF_LV_STRIPED)) ||
(segtype_is_linear(seg->segtype) && !(lv_types & ARG_DEF_LV_LINEAR))) {
/*
* If a named LV arg cannot be processed it's an error, otherwise
* 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 (%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 (%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;
}
if (!_check_lv_rules(cmd, lvl->lv)) {
log_warn("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
if (str_list_match_item(&found_arg_lvnames, lvl->lv->name))
ret_max = ECMD_FAILED;
/* FIXME: include this result in report log? */
continue;
}
log_very_verbose("Processing LV %s in VG %s.", lvl->lv->name, vg->name);
@@ -4004,11 +4362,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

@@ -50,14 +50,14 @@
#define CMD_LEN 256
#define MAX_ARGS 64
/* define the enums for the values accepted by command line --options */
/* define the enums for the values accepted by command line --options, foo_VAL */
enum {
#define val(a, b, c, d) a ,
#include "vals.h"
#undef val
};
/* define the enums for the command line --options */
/* define the enums for the command line --options, foo_ARG */
enum {
#define arg(a, b, c, d, e, f) a ,
#include "args.h"
@@ -69,6 +69,20 @@ enum {
#include "commands.h"
#undef xx
/* define enums for LV properties, foo_LVP */
enum {
#define lvp(a, b, c) a ,
#include "lv_props.h"
#undef lvp
};
/* define enums for LV types, foo_LVT */
enum {
#define lvt(a, b, c) a ,
#include "lv_types.h"
#undef lvt
};
#include "command.h"
#define ARG_COUNTABLE 0x00000001 /* E.g. -vvvv */
@@ -111,6 +125,21 @@ struct val_props {
const char *usage;
};
/* a global table of possible LV properties */
struct lv_props {
int lvp_enum; /* is_foo_LVP from lv_props.h */
const char *name; /* "lv_is_foo" used in command-lines.in */
int (*fn) (struct cmd_context *cmd, struct logical_volume *lv); /* lv_is_foo() */
};
/* a global table of possible LV types */
/* (as exposed externally in command line interface, not exactly as internal segtype is used) */
struct lv_types {
int lvt_enum; /* is_foo_LVT from lv_types.h */
const char *name; /* "foo" used in command-lines.in, i.e. LV_foo */
int (*fn) (struct cmd_context *cmd, struct logical_volume *lv); /* lv_is_foo() */
};
#define CACHE_VGMETADATA 0x00000001
#define PERMITTED_READ_ONLY 0x00000002
/* Process all VGs if none specified on the command line. */
@@ -159,7 +188,15 @@ 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);
int polloperation_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int writemostly_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int syncaction_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int reportformat_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int configreport_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
int configtype_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
/* we use the enums to access the switches */
unsigned arg_count(const struct cmd_context *cmd, int a);
@@ -201,4 +238,7 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg);
struct lv_props *get_lv_prop(int lvp_enum);
struct lv_types *get_lv_type(int lvt_enum);
#endif

View File

@@ -57,11 +57,13 @@
* words is not defined or stored in a consistent way,
* but is just whatever the parsing function happens to look
* for, so adding a new accepted value for the val type is
* often just making the parsing function recognize a new
* word. This new word should then also be added to the
* usage string for the val type here. It would be nice
* if the accepted values could be defined in a more
* consistent way, perhaps in struct val_props.
* generally making the parsing function recognize a new
* word, and making the implementation code also recognize
* that word to do something different. This new word should
* then also be added to the usage string for the val type here.
* It would be nice if the accepted values could be defined in a
* more consistent way, and perhaps in a single place, perhaps in
* struct val_props.
*
* The usage text for an option is not always the full
* set of words accepted for an option, but may be a
@@ -92,9 +94,9 @@
* should avoid suggesting the upper case letters.
*/
val(none_VAL, NULL, "None", "") /* unused, for enum value 0 */
val(conststr_VAL, NULL, "ConstString", "") /* used only for command defs */
val(constnum_VAL, NULL, "ConstNumber", "") /* used only for command defs */
val(none_VAL, NULL, "None", "ERR") /* unused, for enum value 0 */
val(conststr_VAL, NULL, "ConstString", "ERR") /* used only for command defs */
val(constnum_VAL, NULL, "ConstNumber", "ERR") /* used only for command defs */
val(bool_VAL, yes_no_arg, "Bool", "y|n")
val(number_VAL, int_arg, "Number", NULL)
val(string_VAL, string_arg, "String", NULL)
@@ -111,26 +113,24 @@ val(mirrorlog_VAL, mirrorlog_arg, "MirrorLog", "core|disk")
val(sizekb_VAL, size_kb_arg, "SizeKB", "Number[k|unit]")
val(sizemb_VAL, size_mb_arg, "SizeMB", "Number[m|unit]")
val(numsigned_VAL, int_arg_with_sign, "SNumber", "[+|-]Number")
val(numsignedper_VAL, int_arg_with_sign_and_percent, "SNumberP", "[+|-]Number[%{VG|PVS|FREE}]")
val(numsignedper_VAL, int_arg_with_sign_and_percent, "SNumberP", "[+|-]Number[%VG|%PVS|%FREE]")
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")
val(polloperation_VAL, polloperation_arg, "PollOp", "pvmove|convert|merge|merge_thin")
val(writemostly_VAL, writemostly_arg, "WriteMostlyPV", "PV[:t|n|y]")
val(syncaction_VAL, syncaction_arg, "SyncAction", "check|repair")
val(reportformat_VAL, reportformat_arg, "ReportFmt", "basic|json")
val(configreport_VAL, configreport_arg, "ConfigReport", "log|vg|lv|pv|pvseg|seg")
val(configtype_VAL, configtype_arg, "ConfigType", "current|default|diff|full|list|missing|new|profilable|profilable-command|profilable-metadata")
/* 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:
*
* configreport_ARG, configtype_ARG, polloperation_ARG, raidrebuild_ARG,
* raidsyncaction_ARG, raidwritemostly_ARG, reportformat_ARG, syncaction_ARG,
* cachepolicy_ARG, cachesettings_ARG, writemostly_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++;