diff options
author | David Teigland <teigland@redhat.com> | 2016-08-12 15:52:18 -0500 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2016-10-13 16:59:53 -0500 |
commit | 761e4ef9ab90a1303602db8aabae6b86366c3e2f (patch) | |
tree | 6729385e69bedfb5f6c496027c29d3ef69d09694 /scripts | |
parent | 5c55c4ac18f60822da6bf61dde9dec3467a9e79a (diff) | |
download | lvm2-dev-dct-cmd-defs20.tar.gz |
commands: new method for defining commandsdev-dct-cmd-defs20
. Define a prototype for every lvm command.
. Verify every user command matches one.
. 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, --test, --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,
--test
ID: lvresize_by_pv
DESC: Resize an LV by a specified PV.
lvresize --poolmetadatasize SizeMB LV_thinpool
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --stripes Number, --stripesize SizeKB,
--test
OP: PV ...
ID: lvresize_pool_metadata_by_size
DESC: Resize the metadata SubLV of a pool LV.
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" text is also
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[78].name = "lvresize";
commands[78].command_line_id = "lvresize_by_size";
commands[78].command_line_enum = lvresize_by_size_CMD;
commands[78].fn = lvresize;
commands[78].ro_count = 1;
commands[78].rp_count = 1;
commands[78].oo_count = 22;
commands[78].op_count = 1;
commands[78].desc = "DESC: Resize an LV by a specified size.";
commands[78].usage = "lvresize --size Number[m|unit] LV"
" [ --alloc contiguous|cling|normal|anywhere|inherit,
--autobackup y|n, --nofsck, --nosync, --reportformat String,
--resizefs, --stripes Number, --stripesize Number[k|unit],
--poolmetadatasize Number[m|unit] ]"
" [ PV ... ]";
commands[78].usage_common =
" [ --commandprofile String, --config String, --debug,
--driverloaded y|n, --help, --profile String, --quiet,
--verbose, --version, --yes, --force, --test, --noudevsync ]";
commands[78].required_opt_args[0].opt = size_ARG;
commands[78].required_opt_args[0].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[78].required_pos_args[0].pos = 1;
commands[78].required_pos_args[0].def.val_bits = val_enum_to_bit(lv_VAL);
commands[78].optional_opt_args[0].opt = commandprofile_ARG;
commands[78].optional_opt_args[0].def.val_bits = val_enum_to_bit(string_VAL);
commands[78].optional_opt_args[1].opt = config_ARG;
commands[78].optional_opt_args[1].def.val_bits = val_enum_to_bit(string_VAL);
commands[78].optional_opt_args[2].opt = debug_ARG;
commands[78].optional_opt_args[3].opt = driverloaded_ARG;
commands[78].optional_opt_args[3].def.val_bits = val_enum_to_bit(bool_VAL);
commands[78].optional_opt_args[4].opt = help_ARG;
commands[78].optional_opt_args[5].opt = profile_ARG;
commands[78].optional_opt_args[5].def.val_bits = val_enum_to_bit(string_VAL);
commands[78].optional_opt_args[6].opt = quiet_ARG;
commands[78].optional_opt_args[7].opt = verbose_ARG;
commands[78].optional_opt_args[8].opt = version_ARG;
commands[78].optional_opt_args[9].opt = yes_ARG;
commands[78].optional_opt_args[10].opt = alloc_ARG;
commands[78].optional_opt_args[10].def.val_bits = val_enum_to_bit(alloc_VAL);
commands[78].optional_opt_args[11].opt = autobackup_ARG;
commands[78].optional_opt_args[11].def.val_bits = val_enum_to_bit(bool_VAL);
commands[78].optional_opt_args[12].opt = force_ARG;
commands[78].optional_opt_args[13].opt = nofsck_ARG;
commands[78].optional_opt_args[14].opt = nosync_ARG;
commands[78].optional_opt_args[15].opt = noudevsync_ARG;
commands[78].optional_opt_args[16].opt = reportformat_ARG;
commands[78].optional_opt_args[16].def.val_bits = val_enum_to_bit(string_VAL);
commands[78].optional_opt_args[17].opt = resizefs_ARG;
commands[78].optional_opt_args[18].opt = stripes_ARG;
commands[78].optional_opt_args[18].def.val_bits = val_enum_to_bit(number_VAL);
commands[78].optional_opt_args[19].opt = stripesize_ARG;
commands[78].optional_opt_args[19].def.val_bits = val_enum_to_bit(sizekb_VAL);
commands[78].optional_opt_args[20].opt = test_ARG;
commands[78].optional_opt_args[21].opt = poolmetadatasize_ARG;
commands[78].optional_opt_args[21].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[78].optional_pos_args[0].pos = 2;
commands[78].optional_pos_args[0].def.val_bits = val_enum_to_bit(pv_VAL);
commands[78].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, e.g.:
$ lvresize --help
lvresize - Resize a logical volume
Resize an LV by a specified size.
lvresize --size Number[m|unit] LV
[ --alloc contiguous|cling|normal|anywhere|inherit,
--autobackup y|n,
--nofsck,
--nosync,
--reportformat String,
--resizefs,
--stripes Number,
--stripesize Number[k|unit],
--poolmetadatasize Number[m|unit] ]
[ PV ... ]
Resize an LV by a specified PV.
lvresize LV PV ...
[ --alloc contiguous|cling|normal|anywhere|inherit,
--autobackup y|n,
--nofsck,
--nosync,
--reportformat String,
--resizefs,
--stripes Number,
--stripesize Number[k|unit] ]
Resize the metadata SubLV of a pool LV.
lvresize --poolmetadatasize Number[m|unit] LV_thinpool
[ --alloc contiguous|cling|normal|anywhere|inherit,
--autobackup y|n,
--nofsck,
--nosync,
--reportformat String,
--stripes Number,
--stripesize Number[k|unit] ]
[ PV ... ]
Common options:
[ --commandprofile String,
--config String,
--debug,
--driverloaded y|n,
--help,
--profile String,
--quiet,
--verbose,
--version,
--yes,
--force,
--test,
--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
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 implementation.
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.
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/command-lines.in | 1278 | ||||
-rw-r--r-- | scripts/create-commands.c | 2167 |
2 files changed, 3445 insertions, 0 deletions
diff --git a/scripts/command-lines.in b/scripts/command-lines.in new file mode 100644 index 000000000..fb3dbaa70 --- /dev/null +++ b/scripts/command-lines.in @@ -0,0 +1,1278 @@ +# +# When this file is changed, tools/command-lines.h +# and tools/command-lines-count.h must be regenerated +# with: +# +# scripts/create-commands --output count scripts/command-lines.in > tools/command-lines-count.h +# scripts/create-commands --output struct scripts/command-lines.in > tools/command-lines.h +# + +# +# Syntax +# +# A new command has a unique combination of: +# command name, required option args and required +# positional args. +# +# To define a new command, begin a single line with a +# command name, followed by required options/args, +# (e.g. --foo, or --foo val), followed by required +# positional args, (e.g. VG) +# +# After the single line of required elements are lines +# of optional elements: +# OO: <optional --option args> +# OP: <optional positional args> +# +# command_name required_opt_arg ... required_pos_arg ... +# OO: optional_opt_arg, ... +# OP: optional_pos_arg ... +# +# required_opt_arg/optional_opt_arg must begin with the +# long form option name, e.g. --foo. If the option name +# takes a value, then the type of value is specified, +# e.g. --foo String. +# +# Possible option names are listed in args.h +# +# Use --foo_long to specify that only the long form of +# --foo is accepted by the command. (This is uncommon.) +# +# Possible option arg types are shown in tools/vals.h, +# e.g. Bool, String, VG, SizeMB. +# +# --option args outside the list of types in vals.h are treated +# as literal (non-variable) strings or numbers. +# +# positional args can be multiple types separated by |, e.g. VG|LV|Tag +# +# If a positional arg is repeatable, it is followed by ..., e.g. VG|LV|Tag ... +# +# LV can have a suffix indicating the LV type, e.g. LV_linear, LV_thinpool. +# LV_raid represents any raidN. LV_type1_type2_type3 when the LV is +# limited to multiple specific types. +# +# Note that two commands whose required paramters differ only by +# the LV types they accept are ambiguous. That is, they cannot be +# distinguished by just looking at the command, but require reading +# the VG to find the LV type. So, command definitions that differ +# only in accepted LV types are not allowed. It would be best to +# distinguish them by using different option names. +# There are FIXME's below for some of these cases. +# +# VG, LV can have the suffix _new, indicating the named VG or LV +# does not yet exist. +# +# If Select is included in pos_arg, it means that the pos_arg +# may be empty if the --select option is used. +# +# --size and --extents are interchangable, but only --size is used +# in these definitions to keep them simpler. --extents is +# automatically included and recognized as an alternative to --size. +# +# Some options have multiple names, but only one form of the name +# is used in these definitions. Synonyms will be recognized when +# matching a command to a command definition. +# +# used in definitions below (equivalent but not used in definitions) +# mirrorlog core (corelog) +# resizeable (resizable) +# allocatable (allocation) +# resizeable (allocation) +# activate (available) +# rebuild (raidrebuild) +# syncaction (raidsyncaction) +# writemostly (raidwritemostly) +# minrecoveryrate (raidminrecoveryrate) +# maxrecoveryrate (raidmaxrecoveryrate) +# writebehind (raidwritebehind) +# +# +# "---" is like a comment line, used to separate text for readability +# +# ID: A unique string identifying the command. Two commands that do +# the same thing, but are alternate syntaxes can share the same ID, +# in which case the implementation would have to sort out which +# args to look at for the required parameters. Or, the two commands +# could use differnet IDs, in which case the implementation would +# know where to look for each parameter. +# +# DESC: A description of the command. +# + +# +# For efficiency, sets of options can be defined and reused +# in multiple command definitions. +# +# To define a common set of options: +# OO_NAME: --foo, --bar String +# +# To use this set of options, include it on the OO: line, e.g. +# OO: --example, OO_NAME +# +# which is expaneded to +# OO: --example, --foo, --bar String +# +# Including OO_NAME after a command name on the required line +# means that any one of the options is required and the rest +# are optional. The usage syntax for this case is printed as: +# command (--foo A, --bar B) +# + +# +# OO_ALL is included in every command automatically. +# FIXME: add --force and --test to OO_ALL so that all commands will +# accept them even if they are not used? +# +OO_ALL: --commandprofile String, --config String, --debug, +--driverloaded Bool, --help, --profile String, --quiet, +--verbose, --version, --yes + +# +# This list only applies to printing the usage text. +# These common options are displayed once at the end of +# a given command's usage. This is done to avoid excessive +# repetition of common options, which may obscure the more +# interesting and relevant parts of a common prototype. +# This definition is *only* used when generating the command +# usage strings, and is the basis for the division between +# the "usage" and "usage_common" strings. This OO defn does +# not relate to which optional opts are accepted by commands, +# which is defined by the OO line. +# +OO_USAGE_COMMON: OO_ALL, --force, --test, --noudevsync + +# +# options for pvs, lvs, vgs, fullreport +# +OO_REPORT: --aligned, --all, --binary, --configreport String, --foreign, +--ignorelockingfailure, --ignoreskippedcluster, --logonly, +--nameprefixes, --noheadings, --nolocking, --nosuffix, +--options String, --partial, --readonly, --reportformat String, --rows, +--select String, --separator String, --shared, --sort String, +--trustcache, --unbuffered, --units Units, --unquoted + +# +# options for config, dumpconfig, lvmconfig +# +OO_CONFIG: --atversion String, --typeconfig String, --file String, --ignoreadvanced, +--ignoreunsupported, --ignorelocal, --list, --mergedconfig, --metadataprofile String, +--sinceversion String, --showdeprecated, --showunsupported, --validate, --withsummary, +--withcomments, --withspaces, --unconfigured, --withversions + +--- + +# None of these can function as a required option for lvchange. + +OO_LVCHANGE: --autobackup Bool, --force, --ignorelockingfailure, +--ignoremonitoring, --ignoreskippedcluster, --noudevsync, +--reportformat String, --sysinit, --test, --select String + +# Any of these can function as a required option for lvchange. +# profile is also part of OO_ALL, but is repeated in OO_LVCHANGE_META +# because it can function as a required opt. + +OO_LVCHANGE_META: --addtag Tag, --deltag Tag, +--alloc Alloc, --contiguous Bool, +--detachprofile, --metadataprofile String, --profile String, +--permission Permission, --readahead Readahead, --setactivationskip Bool, +--errorwhenfull Bool, --discards Discards, --zero Bool, +--cachemode CacheMode, --cachepolicy String, --cachesettings String, +--minrecoveryrate SizeKB, --maxrecoveryrate SizeKB, +--writebehind Number, --writemostly PV + +lvchange OO_LVCHANGE_META VG|LV|Tag|Select ... +OO: OO_LVCHANGE +ID: lvchange_properties +DESC: Change a general LV property. + +lvchange --resync VG|LV|Tag|Select ... +OO: OO_LVCHANGE +ID: lvchange_resync + +lvchange --syncaction String VG|LV|Tag|Select ... +OO: OO_LVCHANGE +ID: lvchange_syncaction + +lvchange --rebuild PV VG|LV|Tag|Select ... +OO: OO_LVCHANGE +ID: lvchange_rebuild + +lvchange --activate Active VG|LV|Tag|Select ... +OO: --activationmode ActivationMode, --partial, --ignoreactivationskip, OO_LVCHANGE_META, OO_LVCHANGE +ID: lvchange_activate +DESC: Activate or deactivate an LV. + +lvchange --refresh VG|LV|Tag|Select ... +OO: OO_LVCHANGE +ID: lvchange_refresh + +lvchange --monitor Bool VG|LV|Tag|Select ... +OO: --poll Bool, OO_LVCHANGE +ID: lvchange_monitor +DESC: Monitor or unmonitor an LV. + +lvchange --poll Bool VG|LV|Tag|Select ... +OO: --monitor Bool, OO_LVCHANGE +ID: lvchange_poll + +lvchange --persistent Bool VG|LV|Tag|Select ... +OO: --minor Number, --major Number, OO_LVCHANGE +ID: lvchange_persistent + +--- + +OO_LVCONVERT_RAID: --mirrors SNumber, --stripes_long Number, +--stripesize SizeKB, --regionsize SizeMB + +OO_LVCONVERT_POOL: --poolmetadata LV, --poolmetadatasize SizeMB, +--poolmetadataspare Bool, --readahead Readahead, --chunksize SizeKB + +OO_LVCONVERT: --alloc Alloc, --background, --force, --noudevsync, +--test, --usepolicies + +--- + +# FIXME: lvconvert --merge is an extremely ambiguous command. +# It can do very different operations, but which one depends +# on knowing the LV type. So, the command doesn't know what +# it's actually doing until quite late, when processing a +# single LV. +# +# Use different option names for different merge operations +# so that we can have different command definitions, +# different behaviors, different optional options, etc: +# +# lvconvert --merge-mirror LV_linear_striped_raid ... +# DESC: Merge LV that was previously split from a mirror. +# +# lvconvert --merge-thin LV_thin +# DESC: Merge thin LV into its origin LV. +# +# lvconvert --merge-snapshot LV_snapshot +# DESC: Merge COW snapshot LV into its origin. +# +# Then we could add VG|Tag to --merge-mirror arg pos 1, because +# "lvconvert --merge VG|Tag" is a terrible command. It will do +# different operations on each LV it finds, depending on the +# current LV type. + +lvconvert --merge LV_linear_striped_raid_thin_snapshot|VG|Tag ... +OO: --background, --interval Number +ID: lvconvert_merge +DESC: Merge LV that was previously split from a mirror. +DESC: Merge thin LV into its origin LV. +DESC: Merge COW snapshot LV into its origin. + +--- + +# FIXME: by using two different positional args, this is the +# single violation of the standard method of using process_each_lv(). +# Before calling process_each, it steals the first positional arg +# and adjusts argv/argc so it's not seen by process_each. + +lvconvert --type snapshot LV_linear_striped_raid LV_snapshot +OO: --chunksize SizeKB, --zero Bool, OO_LVCONVERT +ID: lvconvert_combine_split_snapshot +DESC: Combine LV with a previously split snapshot LV. + +--- + +lvconvert --type thin --thinpool LV LV_linear_striped_raid +OO: --thin, --originname LV_new, OO_LVCONVERT_POOL, OO_LVCONVERT +ID: lvconvert_to_thin_with_external +DESC: Convert LV to type thin with an external origin. + +# alternate form of lvconvert --type thin +lvconvert --thin --thinpool LV LV_linear_striped_raid +OO: --type thin, --originname LV_new, OO_LVCONVERT_POOL, OO_LVCONVERT +ID: lvconvert_to_thin_with_external +DESC: Convert LV to type thin with an external origin +DESC: (variant, infers --type thin). + +--- + +# FIXME: I don't think --zero applies when creating cache LV, +# but it's used in a test. Should the test be fixed and +# --zero removed here? + +lvconvert --type cache --cachepool LV LV_linear_striped_raid_thinpool +OO: --cache, --cachemode CacheMode, --cachepolicy String, +--cachesettings String, --zero Bool, OO_LVCONVERT_POOL, OO_LVCONVERT +ID: lvconvert_to_cache_vol +DESC: Convert LV to type cache. + +# alternate form of lvconvert --type cache +lvconvert --cache --cachepool LV LV_linear_striped_raid_thinpool +OO: --type cache, --cachemode CacheMode, --cachepolicy String, +--cachesettings String, --zero Bool, OO_LVCONVERT_POOL, OO_LVCONVERT +ID: lvconvert_to_cache_vol +DESC: Convert LV to type cache (variant, infers --type cache). + +--- + +lvconvert --type thin-pool LV_linear_striped_raid_cache +OO: --discards Discards, --zero Bool, OO_LVCONVERT_POOL, OO_LVCONVERT +ID: lvconvert_to_thinpool +DESC: Convert LV to type thin-pool. + +# alternate form of lvconvert --type thin-pool +# deprecated because of non-standard syntax (missing positional arg) +lvconvert --thinpool LV +OO: OO_LVCONVERT_POOL, OO_LVCONVERT +ID: lvconvert_to_thinpool +DESC: Convert LV to type thin-pool (variant, use --type thin-pool). + +--- + +# FIXME: I don't think that --cachemode, --cachepolicy, --cachesettings +# are meant to be used when creating a cache pool, but they are used +# in one test. Should that test be fixed and these options removed? + +lvconvert --type cache-pool LV_linear_striped_raid +OO: OO_LVCONVERT_POOL, OO_LVCONVERT, +--cachemode CacheMode, --cachepolicy String, --cachesettings String +ID: lvconvert_to_cachepool +DESC: Convert LV to type cache-pool. + +# alternate form of lvconvert --type cache-pool +# deprecated because of non-standard syntax (missing positional arg) +lvconvert --cachepool LV_linear_striped_raid +OO: --type cache-pool, OO_LVCONVERT_POOL, OO_LVCONVERT, +--cachemode CacheMode, --cachepolicy String, --cachesettings String +ID: lvconvert_to_cachepool +DESC: Convert LV to type cache-pool (variant, use --type cache-pool). + +--- + +lvconvert --type mirror --mirrors SNumber LV_linear_striped +OO: OO_LVCONVERT_RAID, OO_LVCONVERT +OP: PV ... +ID: lvconvert_to_mirror +DESC: Convert LV to type mirror, adding mirror images. + +# alternate form of lvconvert --type raid1|mirror +lvconvert --mirrors SNumber LV_linear_striped +OO: --type raid1, --type mirror, OO_LVCONVERT_RAID, OO_LVCONVERT +OP: PV ... +ID: lvconvert_to_mirror_or_raid1 +DESC: Convert LV to type raid1 or mirror +DESC: (variant, infers --type raid1|mirror). + +--- + +lvconvert --type mirror LV_raid1 +OO: OO_LVCONVERT_RAID, OO_LVCONVERT +ID: lvconvert_raid1_to_mirror +DESC: Convert LV to type mirror, keeping mirror images. + +lvconvert --type raid1 LV_mirror +OO: OO_LVCONVERT_RAID, OO_LVCONVERT +ID: lvconvert_mirror_to_raid1 +DESC: Convert LV to type raid1, keeping mirror images. + +--- + +# FIXME: by using specific raid levels, e.g. raid1, we could +# specify other required options, e.g. --mirrors. This may +# help the second fixme... +# +# FIXME: there are two different operations here, and it would +# be nice to split them into to unambiguous command lines: +# +# 1. lvconvert --type raid LV_linear_striped +# DESC: Convert LV to type raid. +# +# 2. lvconvert --type raid LV_raid +# DESC: Change LV raid type. + +lvconvert --type raid LV_linear_striped_raid +OO: OO_LVCONVERT_RAID, OO_LVCONVERT +OP: PV ... +ID: lvconvert_general_to_raid +DESC: Convert LV to type raid. +DESC: Change LV raid type. + +--- + +lvconvert --mirrors SNumber LV_raid_mirror +OO: OO_LVCONVERT +OP: PV ... +ID: lvconvert_change_mirror_images +DESC: Change the number of mirror images in the LV. + +--- + +lvconvert --type striped LV_raid +OO: OO_LVCONVERT_RAID, OO_LVCONVERT +OP: PV ... +ID: lvconvert_raid_to_striped +DESC: Convert LV to type striped. + +--- + +lvconvert --type linear LV_raid_mirror +OO: OO_LVCONVERT +ID: lvconvert_raid_or_mirror_to_linear +DESC: Convert LV to type linear. + +--- + +lvconvert --splitmirrors Number --name LV_new LV_raid1_mirror_cache +OO: OO_LVCONVERT +ID: lvconvert_split_mirror_images_to_new +DESC: Split images from a raid1 or mirror LV and use them to create a new LV. + +lvconvert --splitmirrors Number --trackchanges LV_raid1_cache +OO: OO_LVCONVERT +ID: lvconvert_split_mirror_images_and_track +DESC: Split images from a raid1 LV and track changes to origin. + +--- + +# FIXME: use specific option names to distinguish these two +# very different commands, e.g. +# +# lvconvert --repair-pvs LV_raid_mirror +# DESC: Replace failed PVs in a raid or mirror LV. +# +# lvconvert --repair-thinpool LV_thinpool +# DESC: Repair a thin pool. +# +# lvm may want to do different things, or allow different options +# depending on which operation is being run, but as it stands, it +# cannot do anything operation-specific until after the VG is read +# and the LV type is known. + +lvconvert --repair LV_raid_mirror_thinpool +OO: OO_LVCONVERT +ID: lvconvert_repair_pvs_or_thinpool +DESC: Replace failed PVs in a raid or mirror LV. +DESC: Repair a thin pool. + +--- + +lvconvert --replace PV LV_raid +OO: OO_LVCONVERT +OP: PV ... +ID: lvconvert_replace_pv +DESC: Replace specific PV(s) in a raid* LV with another PV. + +--- + +lvconvert --mirrorlog MirrorLog LV_mirror +OO: OO_LVCONVERT +ID: lvconvert_change_mirrorlog +DESC: Change the type of log used by LV. + +--- + +lvconvert --splitcache LV_cachepool_cache_thinpool +OO: OO_LVCONVERT +ID: lvconvert_split_and_keep_cachepool +DESC: Separate and preserve a cache pool from a cache LV. + +--- + +lvconvert --uncache LV_cache_thinpool +OO: OO_LVCONVERT +ID: lvconvert_split_and_delete_cachepool +DESC: Separate and remove a cache pool from a cache LV. + +--- + +lvconvert --splitsnapshot LV_snapshot +OO: OO_LVCONVERT +ID: lvconvert_split_cow_snapshot +DESC: Separate a COW snapshot from its origin LV. + +--- + +# FIXME: add a new option defining this operation, e.g. --poll-mirror +# The purpose of this command is not entirely clear. + +lvconvert LV_mirror +ID: lvconvert_poll_mirror +DESC: Poll LV to collapse resync layers. + +--- + +# FIXME: add a new option defining this operation, e.g. --swapmetadata + +lvconvert --poolmetadata LV LV_thinpool_cachepool +ID: lvconvert_swap_pool_metadata +DESC: Swap metadata LV in a thin pool or cache pool (temporary command). + +--- + +# --extents is not specified; it's an automatic alternative for --size + +OO_LVCREATE: --addtag Tag, --alloc Alloc, --autobackup Bool, --activate Active, +--contiguous Bool, --ignoreactivationskip, --ignoremonitoring, --major Number, +--metadataprofile String, --minor Number, --monitor Bool, --name String, --nosync, +--noudevsync, --permission Permission, --persistent Bool, --readahead Readahead, +--reportformat String, --setactivationskip Bool, --test, --wipesignatures Bool, +--zero Bool + +OO_LVCREATE_CACHE: --cachemode CacheMode, --cachepolicy String, --cachesettings String + +OO_LVCREATE_POOL: --poolmetadatasize SizeMB, --poolmetadataspare Bool, --chunksize SizeKB + +OO_LVCREATE_THIN: --discards Discards, --errorwhenfull Bool + +OO_LVCREATE_RAID: --mirrors SNumber, --stripes Number, --stripesize SizeKB, +--regionsize SizeMB, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB + +--- + +lvcreate --type error --size SizeMB VG +OO: OO_LVCREATE +ID: lvcreate_error_vol +DESC: Create an LV that returns errors when used. + +--- + +lvcreate --type zero --size SizeMB VG +OO: OO_LVCREATE +ID: lvcreate_zero_vol +DESC: Create an LV that returns zeros when read. + +--- + +lvcreate --type linear --size SizeMB VG +OO: OO_LVCREATE +OP: PV ... +ID: lvcreate_linear +DESC: Create a linear LV. + +lvcreate --size SizeMB VG +OO: --type linear, OO_LVCREATE +OP: PV ... +ID: lvcreate_linear +DESC: Create a linear LV (default --type linear). +DESC: When --name is omitted, the name is generated. + +--- + +lvcreate --type striped --size SizeMB VG +OO: --stripes Number, --stripesize SizeKB, OO_LVCREATE +OP: PV ... +ID: lvcreate_striped +DESC: Create a striped LV. + +lvcreate --stripes Number --size SizeMB VG +OO: --type striped, --stripesize SizeKB, OO_LVCREATE +OP: PV ... +ID: lvcreate_striped +DESC: Create a striped LV (infers --type striped). + +--- + +lvcreate --type mirror --size SizeMB VG +OO: --mirrors SNumber, --mirrorlog MirrorLog, --corelog, --regionsize SizeMB, OO_LVCREATE +OP: PV ... +ID: lvcreate_mirror +DESC: Create a mirror LV. + +# alternate form of lvcreate --type raid1|mirror +lvcreate --mirrors SNumber --size SizeMB VG +OO: --type raid1, --type mirror, --mirrorlog MirrorLog, --corelog, OO_LVCREATE_RAID, OO_LVCREATE +OP: PV ... +ID: lvcreate_mirror +DESC: Create a raid1 or mirror LV (variant, infers --type raid1|mirror). + +--- + +lvcreate --type raid --size SizeMB VG +OO: OO_LVCREATE_RAID, OO_LVCREATE +OP: PV ... +ID: lvcreate_raid_any +DESC: Create a raid LV (a specific raid level must be used, e.g. raid1.) + +--- + +lvcreate --type snapshot --size SizeMB LV +OO: --snapshot, OO_LVCREATE +OP: PV ... +ID: lvcreate_cow_snapshot +DESC: Create a COW snapshot LV from an origin LV. + +# alternate form of lvcreate --type snapshot +lvcreate --snapshot --size SizeMB LV +OO: --type snapshot, OO_LVCREATE +OP: PV ... +ID: lvcreate_cow_snapshot +DESC: Create a COW snapshot LV from an origin LV +DESC: (infers --type snapshot). + +--- + +lvcreate --type snapshot --size SizeMB --virtualsize SizeMB VG +OO: --snapshot, --virtualoriginsize SizeMB, OO_LVCREATE +OP: PV ... +ID: lvcreate_cow_snapshot_with_virtual_origin +DESC: Create a sparse COW snapshot LV of a virtual origin LV. + +--- + +lvcreate --type thin-pool --size SizeMB VG +OO: OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thinpool +DESC: Create a thin pool. + +# alternate form of lvcreate --type thin-pool +lvcreate --thin --size SizeMB VG +OO: --type thin-pool, OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thinpool +DESC: Create a thin pool (variant, infers --type thin-pool). + +--- + +lvcreate --type cache-pool --size SizeMB VG +OO: OO_LVCREATE_POOL, OO_LVCREATE_CACHE, OO_LVCREATE +OP: PV ... +ID: lvcreate_cachepool +DESC: Create a cache pool. + +# alternate form of lvcreate --type cache-pool +lvcreate --cache --size SizeMB VG +OO: --type cache-pool, OO_LVCREATE_POOL, OO_LVCREATE_CACHE, OO_LVCREATE +OP: PV ... +ID: lvcreate_cachepool +DESC: Create a cache pool (variant, infers --type cache-pool). + +--- + +lvcreate --type thin --virtualsize SizeMB --thinpool LV_thinpool +OO: --thin, OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +ID: lvcreate_thin_vol +DESC: Create a thin LV in a thin pool. + +# alternate form of lvcreate --type thin +lvcreate --virtualsize SizeMB --thinpool LV_thinpool +OO: --type thin, --thin, OO_LVCREATE_THIN, OO_LVCREATE +ID: lvcreate_thin_vol +DESC: Create a thin LV in a thin pool (variant, infers --type thin). + +# alternate form of lvcreate --type thin +lvcreate --virtualsize SizeMB LV_thinpool +OO: --type thin, --thin, OO_LVCREATE_THIN, OO_LVCREATE +ID: lvcreate_thin_vol +DESC: Create a thin LV in the thin pool named in arg pos 1 +DESC: (variant, infers --type thin). + +--- + +lvcreate --type thin LV_thin +OO: OO_LVCREATE_THIN, OO_LVCREATE +ID: lvcreate_thin_snapshot +DESC: Create a thin LV that is a snapshot of an existing thin LV. + +# alternate form of lvcreate --type thin LV_thin +lvcreate --snapshot LV_thin +OO: --type thin, OO_LVCREATE_THIN, OO_LVCREATE +ID: lvcreate_thin_snapshot +DESC: Create a thin LV that is a snapshot of an existing thin LV +DESC: (infers --type thin). + +lvcreate --type thin --thinpool LV_thinpool LV +OO: OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +ID: lvcreate_thin_snapshot_of_external +DESC: Create a thin LV that is a snapshot of an external origin LV. + +# alternate form of lvcreate --type thin --thinpool LV_thinpool LV +lvcreate --snapshot --thinpool LV_thinpool LV +OO: --type thin, OO_LVCREATE_THIN, OO_LVCREATE +ID: lvcreate_thin_snapshot_of_external +DESC: Create a thin LV that is a snapshot of an external origin LV +DESC: (infers --type thin). + +--- + +lvcreate --type thin --virtualsize SizeMB --size SizeMB --thinpool LV_new +OO: OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thin_vol_with_thinpool +DESC: Create a thin LV, first creating a thin pool for it, +DESC: where the new thin pool is named by the --thinpool arg. + +# alternate form of lvcreate --type thin +lvcreate --thin --virtualsize SizeMB --size SizeMB --thinpool LV_new +OO: --type thin, OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thin_vol_with_thinpool +DESC: Create a thin LV, first creating a thin pool for it, +DESC: where the new thin pool is named by the --thinpool arg, +DESC: (variant, infers --type thin). + +lvcreate --type thin --virtualsize SizeMB --size SizeMB LV_new +OO: OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thin_vol_with_thinpool +DESC: Create a thin LV, first creating a thin pool for it, +DESC: where the new thin pool is named in arg pos 1. + +lvcreate --thin --virtualsize SizeMB --size SizeMB LV_new +OO: --type thin, OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thin_vol_with_thinpool +DESC: Create a thin LV, first creating a thin pool for it, +DESC: where the new thin pool is named in arg pos 1, +DESC: (variant, infers --type thin). + +lvcreate --type thin --virtualsize SizeMB --size SizeMB VG +OO: OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thin_vol_with_thinpool +DESC: Create a thin LV, first creating a thin pool for it. + +--- + +lvcreate --size SizeMB --virtualsize SizeMB VG +OO: --type thin, --type snapshot, --thin, --snapshot, +--virtualoriginsize SizeMB, OO_LVCREATE_POOL, OO_LVCREATE_THIN, OO_LVCREATE +OP: PV ... +ID: lvcreate_thin_vol_with_thinpool_or_sparse_snapshot +DESC: Create a thin LV, first creating a thin pool for it +DESC: (infers --type thin). +DESC: Create a sparse snapshot of a virtual origin LV +DESC: (infers --type snapshot). +DESC: Infers --type thin or --type snapshot according to +DESC: confing setting sparse_segtype_default. + +--- + +# FIXME: this should be done by lvconvert, and this command deprecated + +lvcreate --type cache --size SizeMB LV +OO: OO_LVCREATE_POOL, OO_LVCREATE_CACHE, OO_LVCREATE +OP: PV ... +ID: lvcreate_convert_to_cache_vol_with_cachepool +DESC: Convert the specified LV to type cache after creating a new +DESC: cache pool LV to use. + +--- + +lvcreate --type cache --size SizeMB --cachepool LV_cachepool +OO: --cache, OO_LVCREATE_POOL, OO_LVCREATE_CACHE, OO_LVCREATE +OP: PV ... +ID: lvcreate_cache_vol_with_new_origin +DESC: Create a cache LV, first creating a new origin LV, +DESC: then combining it with the existing cache pool in arg pos 1. + +lvcreate --size SizeMB --cachepool LV_cachepool +OO: --type cache, --cache, OO_LVCREATE_CACHE, OO_LVCREATE +OP: PV ... +ID: lvcreate_cache_vol_with_new_origin +DESC: Create a cache LV, first creating a new origin LV, +DESC: then combining it with the existing cache pool in arg pos 1. +DESC: (variant, infers --type cache). + +--- + +lvdisplay +OO: --aligned, --all, --binary, --colon, --columns, +--configreport String, --foreign, --history, --ignorelockingfailure, +--ignoreskippedcluster, --logonly, --maps, --noheadings, +--nosuffix, --options String, --sort String, --partial, --readonly, +--reportformat String, --segments, --select String, --separator String, +--shared, --unbuffered, --units Units +OP: VG|LV|Tag ... +ID: lvdisplay_general + +--- + +# --extents is not specified; it's an automatic alternative for --size + +lvextend --size SizeMB LV +OO: --alloc Alloc, --autobackup Bool, --force, --mirrors SNumber, +--nofsck, --nosync, --noudevsync, --reportformat String, --resizefs, +--stripes Number, --stripesize SizeKB, --test, --poolmetadatasize SizeMB +OP: PV ... +ID: lvextend_by_size + +lvextend LV PV ... +OO: --alloc Alloc, --autobackup Bool, --force, --mirrors SNumber, +--nofsck, --nosync, --noudevsync, +--reportformat String, --resizefs, --stripes Number, --stripesize SizeKB, +--test +ID: lvextend_by_pv + +lvextend --poolmetadatasize SizeMB LV_thinpool +OO: --alloc Alloc, --autobackup Bool, --force, --mirrors SNumber, +--nofsck, --nosync, --noudevsync, +--reportformat String, --stripes Number, --stripesize SizeKB, +--test +OP: PV ... +ID: lvextend_pool_metadata_by_size + +lvextend --usepolicies LV_thinpool_snapshot +OO: --alloc Alloc, --autobackup Bool, --force, --mirrors SNumber, +--nofsck, --nosync, --noudevsync, +--reportformat String, --resizefs, +--test +ID: lvextend_by_policy + +--- + +lvmconfig +OO: OO_CONFIG +ID: lvmconfig_general + +--- + +lvreduce --size SizeMB LV +OO: --autobackup Bool, --force, --nofsck, --noudevsync, +--reportformat String, --resizefs, --test +ID: lvreduce_general + +--- + +lvremove VG|LV|Tag|Select ... +OO: --autobackup Bool, --force, --nohistory, --noudevsync, +--reportformat String, --select String, --test +ID: lvremove_general + +--- + +lvrename VG LV LV_new +OO: --autobackup Bool, --noudevsync, --reportformat String, --test +ID: lvrename_vg_lv_lv + +lvrename LV LV_new +OO: --autobackup Bool, --noudevsync, --reportformat String, --test +ID: lvrename_lv_lv + +--- + +lvresize --size SizeMB LV +OO: --alloc Alloc, --autobackup Bool, --force, +--nofsck, --nosync, --noudevsync, --reportformat String, --resizefs, +--stripes Number, --stripesize SizeKB, --test, --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, +--test +ID: lvresize_by_pv +DESC: Resize an LV by a specified PV. + +lvresize --poolmetadatasize SizeMB LV_thinpool +OO: --alloc Alloc, --autobackup Bool, --force, +--nofsck, --nosync, --noudevsync, +--reportformat String, --stripes Number, --stripesize SizeKB, +--test +OP: PV ... +ID: lvresize_pool_metadata_by_size +DESC: Resize the metadata SubLV of a pool LV. + +--- + +lvs +OO: --history, --segments, OO_REPORT +OP: VG|LV|Tag ... +ID: lvs_general + +--- + +lvscan +OO: --all, --blockdevice, --ignorelockingfailure, --partial, +--readonly, --reportformat String, --cache_long +ID: lvscan_general + +--- + +# None of these can function as a required option for pvchange. +OO_PVCHANGE: --autobackup Bool, --force, --ignoreskippedcluster, +--reportformat String, --test, --uuid + +# Any of these can function as a required option for pvchange. +OO_PVCHANGE_META: --allocatable Bool, --addtag Tag, --deltag Tag, +--uuid, --metadataignore Bool + +pvchange OO_PVCHANGE_META --all +OO: OO_PVCHANGE +ID: pvchange_properties_all + +pvchange OO_PVCHANGE_META PV|Select ... +OO: --select String, OO_PVCHANGE +ID: pvchange_properties_some + +--- + +pvresize PV ... +OO: --setphysicalvolumesize SizeMB, --reportformat String, --test +ID: pvresize_general + +--- + +pvck PV ... +OO: --labelsector Number +ID: pvck_general + +--- + +# Use --uuidstr here which will be converted to uuidstr_ARG +# which is actually --uuid string on the command line. + +pvcreate PV ... +OO: --dataalignment SizeKB, --dataalignmentoffset SizeKB, --bootloaderareasize SizeMB, +--force, --test, --labelsector Number, --metadatatype MetadataType, +--pvmetadatacopies Number, --metadatacopies MetadataCopies, --metadatasize SizeMB, +--metadataignore Bool, --norestorefile, --setphysicalvolumesize SizeMB, +--reportformat String, --restorefile String, --uuidstr String, --zero Bool +ID: pvcreate_general + +--- + +pvdisplay +OO: --aligned, --all, --binary, --colon, --columns, --configreport String, +--foreign, --ignorelockingfailure, --ignoreskippedcluster, +--logonly, --maps, --noheadings, --nosuffix, --options String, +--readonly, --reportformat String, --select String, --separator String, --shared, +--short, --sort String, --unbuffered, --units Units +OP: PV|Tag ... +ID: pvdisplay_general + +--- + +pvmove PV +OO: --abort, --alloc Alloc, --atomic, --autobackup Bool, --background, +--interval Number, --name LV, --noudevsync, --reportformat String, --test +OP: PV ... +ID: pvmove_one + +pvmove +OO: --abort, --background, --test +ID: pvmove_any + +--- + +pvremove PV ... +OO: --force, --reportformat String, --test +ID: pvremove_general + +--- + +pvs +OO: --segments, OO_REPORT +OP: PV|Tag ... +ID: pvs_general + +--- + +pvscan +OO: --ignorelockingfailure, --reportformat String, --exported, --novolumegroup, +--short, --uuid +ID: pvscan_show + +pvscan --cache_long +OO: --ignorelockingfailure, --reportformat String, --background, +--activate Active, --major Number, --minor Number, +OP: PV|String ... +ID: pvscan_cache + +--- + +vgcfgbackup +OO: --file String, --foreign, --ignorelockingfailure, --partial, --readonly, +--reportformat String +OP: VG ... +ID: vgcfgbackup_general + +--- + +vgcfgrestore VG +OO: --file String, --force_long, --list, --metadatatype MetadataType, --test +ID: vgcfgrestore_by_vg + +vgcfgrestore --list --file String +ID: vgcfgrestore_by_file + +--- + +# None of these can function as a required option for vgchange. + +OO_VGCHANGE: --autobackup Bool, --ignoremonitoring, --ignoreskippedcluster, +--noudevsync, --reportformat String, --select String, --test, --force + +# Any of these can function as a required option for vgchange. +# profile is also part of OO_ALL, but is repeated in OO_VGCHANGE_META +# because it can function as a required opt. + +OO_VGCHANGE_META: --addtag Tag, --deltag Tag, +--logicalvolume Number, --maxphysicalvolumes Number, --alloc Alloc, --uuid, +--clustered Bool, --metadatacopies MetadataCopies, --vgmetadatacopies MetadataCopies, +--physicalextentsize SizeMB, --resizeable Bool, --systemid String, --locktype LockType, +--profile String, --detachprofile, --metadataprofile String, + +vgchange OO_VGCHANGE_META +OO: OO_VGCHANGE +OP: VG|Tag ... +ID: vgchange_properties + +vgchange --monitor Bool +OO: --sysinit, --ignorelockingfailure, --poll Bool, OO_VGCHANGE_META, OO_VGCHANGE +OP: VG|Tag ... +ID: vgchange_monitor + +vgchange --poll Bool +OO: --ignorelockingfailure, OO_VGCHANGE_META, OO_VGCHANGE +OP: VG|Tag ... +ID: vgchange_poll + +vgchange --activate Active +OO: --activationmode ActivationMode, --ignoreactivationskip, --partial, --sysinit, +--ignorelockingfailure, --monitor Bool, --poll Bool, OO_VGCHANGE_META, OO_VGCHANGE +OP: VG|Tag ... +ID: vgchange_activate + +vgchange --refresh +OO: --sysinit, --ignorelockingfailure, --monitor Bool, --poll Bool, OO_VGCHANGE_META, OO_VGCHANGE +OP: VG|Tag ... +ID: vgchange_refresh + +vgchange --lockstart +OO: --lockopt String, OO_VGCHANGE_META, OO_VGCHANGE +OP: VG|Tag ... +ID: vgchange_lockstart + +vgchange --lockstop +OO: --lockopt String, OO_VGCHANGE_META, OO_VGCHANGE +OP: VG|Tag ... +ID: vgchange_lockstop + +--- + +vgck +OO: --reportformat String +OP: VG|Tag ... +ID: vgck_general + +--- + +vgconvert VG ... +OO: --force, --test, --labelsector Number, --bootloaderareasize SizeMB, +--metadatatype MetadataType, --pvmetadatacopies Number, +--metadatasize SizeMB, --reportformat String +ID: vgconvert_general + +--- + +vgcreate VG_new PV ... +OO: --addtag Tag, --alloc Alloc, --autobackup Bool, --clustered Bool, --maxlogicalvolumes Number, +--maxphysicalvolumes Number, --metadataprofile String, --metadatatype MetadataType, +--physicalextentsize SizeMB, --test, --force, --zero Bool, --labelsector Number, +--metadatasize SizeMB, --pvmetadatacopies Number, --reportformat String, --metadatacopies MetadataCopies, +--vgmetadatacopies MetadataCopies, --dataalignment SizeKB, --dataalignmentoffset SizeKB, +--shared, --systemid String, --locktype LockType, --lockopt String +ID: vgcreate_general + +--- + +vgdisplay +OO: --activevolumegroups, --aligned, --binary, --colon, --columns, +--configreport String, --foreign, --ignorelockingfailure, +--ignoreskippedcluster, --logonly, --noheadings, --nosuffix, +--options String, --partial, --readonly, --reportformat String, --select String, +--shared, --short, --separator String, --sort String, --unbuffered, --units Units +OP: VG|Tag ... +ID: vgdisplay_general + +--- + +OO_VGEXPORT: --reportformat String, --test + +vgexport VG|Tag|Select ... +OO: --select String, OO_VGEXPORT +ID: vgexport_some + +vgexport --all +OO: OO_VGEXPORT +ID: vgexport_all + +--- + +vgextend VG PV ... +OO: --autobackup Bool, --test, +--force, --zero Bool, --labelsector Number, --metadatatype MetadataType, +--metadatasize SizeMB, --pvmetadatacopies Number, +--metadataignore Bool, --dataalignment SizeKB, --dataalignmentoffset SizeKB, +--reportformat String, --restoremissing +ID: vgextend_general + +--- + +OO_VGIMPORT: --force, --reportformat String, --test + +vgimport VG|Tag|Select ... +OO: --select String, OO_VGIMPORT +ID: vgimport_some + +vgimport --all +OO: OO_VGIMPORT +ID: vgimport_all + +--- + +vgimportclone PV ... +OO: --basevgname VG, --test, --import +ID: vgimportclone_general + +--- + +vgmerge VG VG +OO: --autobackup Bool, --list, --test +ID: vgmerge_general + +--- + +vgmknodes +OO: --ignorelockingfailure, --refresh, --reportformat String +OP: VG|LV|Tag ... +ID: vgmknodes_general + +--- + +OO_VGREDUCE: --autobackup Bool, --force, --reportformat String, --test + +vgreduce VG PV ... +OO: OO_VGREDUCE +ID: vgreduce_by_pv + +vgreduce --all VG +OO: OO_VGREDUCE +ID: vgreduce_all + +vgreduce --removemissing VG +OO: --mirrorsonly, OO_VGREDUCE +ID: vgreduce_missing + +--- + +vgremove VG|Tag|Select ... +OO: --force, --noudevsync, --reportformat String, --select String, --test +ID: vgremove_general + +--- + +vgrename VG VG_new +OO: --autobackup Bool, --force, --reportformat String, --test +ID: vgrename_by_name + +vgrename String VG_new +OO: --autobackup Bool, --force, --reportformat String, --test +ID: vgrename_by_uuid + +--- + +vgs +OO: OO_REPORT +OP: VG|Tag ... +ID: vgs_general + +--- + +vgscan +OO: --cache_long, --ignorelockingfailure, --mknodes, --notifydbus, +--partial, --reportformat String +ID: vgscan_general + +--- + +OO_VGSPLIT: --autobackup Bool, --test + +OO_VGSPLIT_NEW: --alloc Alloc, --clustered Bool, +--maxlogicalvolumes Number, --maxphysicalvolumes Number, +--metadatatype MetadataType, --vgmetadatacopies MetadataCopies + +vgsplit VG VG PV ... +OO: OO_VGSPLIT +ID: vgsplit_by_pv_to_existing + +vgsplit --name LV VG VG +OO: OO_VGSPLIT +ID: vgsplit_by_lv_to_existing + +vgsplit VG VG_new PV ... +OO: OO_VGSPLIT, OO_VGSPLIT_NEW +ID: vgsplit_by_pv_to_new + +vgsplit --name LV VG VG_new +OO: OO_VGSPLIT, OO_VGSPLIT_NEW +ID: vgsplit_by_lv_to_new + +--- + +# built-in and deprecated commands + +config +OO: OO_CONFIG +OP: String ... +ID: lvmconfig_general + +dumpconfig +OO: OO_CONFIG +OP: String ... +ID: lvmconfig_general + +devtypes +OO: --aligned, --binary, --nameprefixes, --noheadings, +--nosuffix, --options String, --reportformat String, --rows, +--select String, --separator String, --sort String, --unbuffered, --unquoted +ID: devtypes_general + +fullreport +OO: OO_REPORT +OP: VG ... +ID: fullreport_general + +lastlog +OO: --reportformat String, --select String +ID: lastlog_general + +lvpoll --polloperation String LV ... +OO: --abort, --autobackup Bool, --handlemissingpvs, --interval Number, --test +ID: lvpoll_general + +formats +ID: formats_general + +help +ID: help_general + +version +ID: version_general + +pvdata +ID: pvdata_general + +segtypes +ID: segtypes_general + +systemid +ID: systemid_general + +tags +ID: tags_general + +lvmchange +ID: lvmchange_general + +lvmdiskscan +OO: --lvmpartition, --readonly +ID: lvmdiskscan_general + +lvmsadc +ID: lvmsadc_general + +lvmsar +OO: --full, --stdin +ID: lvmsar_general + diff --git a/scripts/create-commands.c b/scripts/create-commands.c new file mode 100644 index 000000000..ae496b016 --- /dev/null +++ b/scripts/create-commands.c @@ -0,0 +1,2167 @@ +#include <asm/types.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <stdarg.h> +#include <limits.h> +#include <unistd.h> +#include <syslog.h> +#include <sched.h> +#include <dirent.h> +#include <ctype.h> +#include <getopt.h> + +/* needed to include args.h */ +#define ARG_COUNTABLE 0x00000001 +#define ARG_GROUPABLE 0x00000002 +struct cmd_context; +struct arg_values; + +int yes_no_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int activation_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int cachemode_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int discards_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int mirrorlog_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int size_kb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int size_mb_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int size_mb_arg_with_percent(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int int_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int int_arg_with_sign(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int int_arg_with_sign_and_percent(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int major_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int minor_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int string_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int tag_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int permission_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +int units_arg(struct cmd_context *cmd, struct arg_values *av) { return 0; } +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 metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av) { return 0; } + +/* also see arg_props */ +struct opt_name { + const char *name; + int opt_enum; /* enum from args.h */ + const char short_opt; + char _padding[7]; + const char *long_opt; + int val_enum; /* enum from vals.h */ + uint32_t unused1; + uint32_t unused2; +}; + +/* also see val_props */ +struct val_name { + const char *enum_name; + int val_enum; /* enum from vals.h */ + int (*fn) (struct cmd_context *cmd, struct arg_values *av); /* unused here */ + const char *name; + const char *usage; +}; + +/* create foo_VAL enums */ + +enum { +#define val(a, b, c, d) a , +#include "vals.h" +#undef val +}; + +/* create foo_ARG enums */ + +enum { +#define arg(a, b, c, d, e, f) a , +#include "args.h" +#undef arg +}; + +/* create table of value names, e.g. String, and corresponding enum from vals.h */ + +static struct val_name val_names[VAL_COUNT + 1] = { +#define val(a, b, c, d) { # a, a, b, c, d }, +#include "vals.h" +#undef val +}; + +/* create table of option names, e.g. --foo, and corresponding enum from args.h */ + +static struct opt_name opt_names[ARG_COUNT + 1] = { +#define arg(a, b, c, d, e, f) { # a, a, b, "", "--" c, d, e, f }, +#include "args.h" +#undef arg +}; + +#include "command.h" + +#define MAX_CMD_NAMES 128 +struct cmd_name { + const char *name; + const char *desc; +}; + +/* create table of command names, e.g. vgcreate */ + +static struct cmd_name cmd_names[MAX_CMD_NAMES] = { +#define xx(a, b, c) { # a , b } , +#include "commands.h" +#undef xx +}; + +#define MAX_LINE 1024 +#define MAX_LINE_ARGC 256 + +#define REQUIRED 1 +#define OPTIONAL 0 + +struct oo_line { + char *name; + char *line; +}; + +#define MAX_CMDS 256 +int cmd_count; +struct command cmd_array[MAX_CMDS]; + +struct command common_options; /* for printing common usage */ + +#define MAX_OO_LINES 256 +int oo_line_count; +struct oo_line oo_lines[MAX_OO_LINES]; + + +static void add_optional_opt_line(struct command *cmd, int argc, char *argv[]); + +/* + * modifies buf, replacing the sep characters with \0 + * argv pointers point to positions in buf + */ + +static char *split_line(char *buf, int *argc, char **argv, char sep) +{ + char *p = buf, *rp = NULL; + int i; + + argv[0] = p; + + for (i = 1; i < MAX_LINE_ARGC; i++) { + p = strchr(buf, sep); + if (!p) + break; + *p = '\0'; + + argv[i] = p + 1; + buf = p + 1; + } + *argc = i; + + /* we ended by hitting \0, return the point following that */ + if (!rp) + rp = strchr(buf, '\0') + 1; + + return rp; +} + +/* convert value string, e.g. Number, to foo_VAL enum */ + +static int val_str_to_num(char *str) +{ + char name[32] = { 0 }; + char *new; + int i; + + /* compare the name before any suffix like _new or _<lvtype> */ + + strncpy(name, str, 31); + if ((new = strstr(name, "_"))) + *new = '\0'; + + for (i = 0; i < VAL_COUNT; i++) { + if (!val_names[i].name) + break; + if (!strncmp(name, val_names[i].name, strlen(val_names[i].name))) + return val_names[i].val_enum; + } + + return 0; +} + +/* convert "--option" to foo_ARG enum */ + +static int opt_str_to_num(char *str) +{ + char long_name[32]; + char *p; + int i; + + /* + * --foo_long means there are two args entries + * for --foo, one with a short option and one + * without, and we want the one without the + * short option. + */ + if (strstr(str, "_long")) { + strcpy(long_name, str); + p = strstr(long_name, "_long"); + *p = '\0'; + + for (i = 0; i < ARG_COUNT; i++) { + if (!opt_names[i].long_opt) + continue; + /* skip anything with a short opt */ + if (opt_names[i].short_opt) + continue; + if (!strcmp(opt_names[i].long_opt, long_name)) + return opt_names[i].opt_enum; + } + + printf("Unknown opt str: %s %s\n", str, long_name); + exit(1); + } + + for (i = 0; i < ARG_COUNT; i++) { + if (!opt_names[i].long_opt) + continue; + /* These are only selected using --foo_long */ + if (strstr(opt_names[i].name, "_long_ARG")) + continue; + if (!strcmp(opt_names[i].long_opt, str)) + return opt_names[i].opt_enum; + } + + printf("Unknown opt str: \"%s\"\n", str); + exit(1); +} + +static char *val_bits_to_str(int val_bits) +{ + static char buf[128]; + int i; + int or = 0; + + memset(buf, 0, sizeof(buf)); + + for (i = 0; i < VAL_COUNT; i++) { + if (val_bits & val_enum_to_bit(i)) { + if (or) strcat(buf, " | "); + strcat(buf, "val_enum_to_bit("); + strcat(buf, val_names[i].enum_name); + strcat(buf, ")"); + or = 1; + } + } + + return buf; +} + +/* + * The _<lvtype> and _new suffixes are only used by the command definitions and + * are not exposed to lvm at large, which uses only the ARG_DEF values. + */ + +static uint32_t lv_str_to_types(char *str) +{ + char copy[128] = { 0 }; + char *argv[MAX_LINE_ARGC]; + int argc; + char *name; + uint32_t types = 0; + int i; + + strncpy(copy, str, 128); + + split_line(copy, &argc, argv, '_'); + + for (i = 0; i < argc; i++) { + name = argv[i]; + + if (!strcmp(name, "linear")) + types |= ARG_DEF_LV_LINEAR; + + if (!strcmp(name, "striped")) + types |= ARG_DEF_LV_STRIPED; + + if (!strcmp(name, "snapshot")) + types |= ARG_DEF_LV_SNAPSHOT; + + if (!strcmp(name, "mirror")) + types |= ARG_DEF_LV_MIRROR; + + if (!strcmp(name, "thin")) + types |= ARG_DEF_LV_THIN; + + if (!strcmp(name, "thinpool")) + types |= ARG_DEF_LV_THINPOOL; + + if (!strcmp(name, "cache")) + types |= ARG_DEF_LV_CACHE; + + if (!strcmp(name, "cachepool")) + types |= ARG_DEF_LV_CACHEPOOL; + + if (!strcmp(name, "raid0")) + types |= ARG_DEF_LV_RAID0; + + if (!strcmp(name, "raid1")) + types |= ARG_DEF_LV_RAID1; + + if (!strcmp(name, "raid4")) + types |= ARG_DEF_LV_RAID4; + + if (!strcmp(name, "raid5")) + types |= ARG_DEF_LV_RAID5; + + if (!strcmp(name, "raid6")) + types |= ARG_DEF_LV_RAID6; + + if (!strcmp(name, "raid10")) + types |= ARG_DEF_LV_RAID10; + + if (!strcmp(name, "raid")) + types |= ARG_DEF_LV_RAID; + } + + return types; +} + +static const char *lv_num_to_str(int num) +{ + switch (num) { + case ARG_DEF_LV_LINEAR: + return "linear"; + case ARG_DEF_LV_STRIPED: + return "striped"; + case ARG_DEF_LV_SNAPSHOT: + return "snapshot"; + case ARG_DEF_LV_MIRROR: + return "mirror"; + case ARG_DEF_LV_RAID: + return "raid"; + case ARG_DEF_LV_RAID0: + return "raid0"; + case ARG_DEF_LV_RAID1: + return "raid1"; + case ARG_DEF_LV_RAID4: + return "raid4"; + case ARG_DEF_LV_RAID5: + return "raid5"; + case ARG_DEF_LV_RAID6: + return "raid6"; + case ARG_DEF_LV_RAID10: + return "raid10"; + case ARG_DEF_LV_THIN: + return "thin"; + case ARG_DEF_LV_THINPOOL: + return "thinpool"; + case ARG_DEF_LV_CACHE: + return "cache"; + case ARG_DEF_LV_CACHEPOOL: + return "cachepool"; + default: + printf("lv_num_to_str: unknown LV num: %d\n", num); + exit(1); + } +} + +static char *lv_types_to_flags(int lv_types) +{ + static char buf_lv_types[128]; + int or = 0; + + memset(buf_lv_types, 0, sizeof(buf_lv_types)); + + if (lv_types & ARG_DEF_LV_LINEAR) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_LINEAR"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_STRIPED) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_STRIPED"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_SNAPSHOT) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_SNAPSHOT"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_MIRROR) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_MIRROR"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_RAID) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_RAID"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_RAID0) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_RAID0"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_RAID1) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_RAID1"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_RAID4) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_RAID4"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_RAID5) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_RAID5"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_RAID6) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_RAID6"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_RAID10) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_RAID10"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_THIN) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_THIN"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_THINPOOL) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_THINPOOL"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_CACHE) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_CACHE"); + or = 1; + } + + if (lv_types & ARG_DEF_LV_CACHEPOOL) { + if (or) strcat(buf_lv_types, " | "); + strcat(buf_lv_types, "ARG_DEF_LV_CACHEPOOL"); + or = 1; + } + + return buf_lv_types; +} + +static const char *is_command_name(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].name; + } + return NULL; +} + +static const char *cmd_name_desc(const char *name) +{ + int i; + + for (i = 0; i < MAX_CMD_NAMES; i++) { + if (!cmd_names[i].name) + break; + if (!strcmp(cmd_names[i].name, name)) + return cmd_names[i].desc; + } + return NULL; +} + +static int is_opt_name(char *str) +{ + if (!strncmp(str, "--", 2)) + return 1; + + if ((str[0] == '-') && (str[1] != '-')) { + printf("Options must be specified in long form: %s\n", str); + exit(1); + } + + return 0; +} + +/* + * "Select" as a pos name means that the position + * can be empty if the --select option is used. + */ + +static int is_pos_name(char *str) +{ + if (!strncmp(str, "VG", 2)) + return 1; + if (!strncmp(str, "LV", 2)) + return 1; + if (!strncmp(str, "PV", 2)) + return 1; + if (!strncmp(str, "Tag", 3)) + return 1; + if (!strncmp(str, "String", 6)) + return 1; + if (!strncmp(str, "Select", 6)) + return 1; + return 0; +} + +static int is_oo_definition(char *str) +{ + if (!strncmp(str, "OO_", 3)) + return 1; + return 0; +} + +static int is_oo_line(char *str) +{ + if (!strncmp(str, "OO:", 3)) + return 1; + return 0; +} + +static int is_op_line(char *str) +{ + if (!strncmp(str, "OP:", 3)) + return 1; + return 0; +} + +static int is_desc_line(char *str) +{ + if (!strncmp(str, "DESC:", 5)) + return 1; + return 0; +} + +static int is_id_line(char *str) +{ + if (!strncmp(str, "ID:", 3)) + return 1; + return 0; +} + +/* + * parse str for anything that can appear in a position, + * like VG, VG|LV, VG|LV_linear|LV_striped, etc + */ + +static void set_pos_def(struct command *cmd, char *str, struct arg_def *def) +{ + char *argv[MAX_LINE_ARGC]; + int argc; + char *name; + int val_enum; + int i; + + split_line(str, &argc, argv, '|'); + + for (i = 0; i < argc; i++) { + name = argv[i]; + + val_enum = val_str_to_num(name); + + if (!val_enum) { + printf("Unknown pos arg: %s\n", name); + exit(1); + } + + def->val_bits |= val_enum_to_bit(val_enum); + + if ((val_enum == lv_VAL) && strstr(name, "_")) + def->lv_types = lv_str_to_types(name); + + if (strstr(name, "_new")) + def->flags |= ARG_DEF_FLAG_NEW; + } +} + +/* + * parse str for anything that can follow --option + */ + +static void set_opt_def(struct command *cmd, char *str, struct arg_def *def) +{ + char *argv[MAX_LINE_ARGC]; + int argc; + char *name; + int val_enum; + int i, j; + + split_line(str, &argc, argv, '|'); + + for (i = 0; i < argc; i++) { + name = argv[i]; + + val_enum = val_str_to_num(name); + + if (!val_enum) { + /* a literal number or string */ + + if (isdigit(name[0])) + val_enum = constnum_VAL; + + else if (isalpha(name[0])) + val_enum = conststr_VAL; + + else { + printf("Unknown opt arg: %s\n", name); + exit(0); + } + } + + + def->val_bits |= val_enum_to_bit(val_enum); + + if (val_enum == constnum_VAL) + def->num = (uint64_t)atoi(name); + + if (val_enum == conststr_VAL) + def->str = strdup(name); + + if (val_enum == lv_VAL) { + if (strstr(name, "_")) + def->lv_types = lv_str_to_types(name); + } + + if ((val_enum == vg_VAL) || (val_enum == lv_VAL) || (val_enum == pv_VAL)) { + if (strstr(name, "_new")) + def->flags |= ARG_DEF_FLAG_NEW; + } + } +} + + +/* + * OO_FOO: --opt1 ... + * + * oo->name = "OO_FOO"; + * oo->line = "--opt1 ..."; + */ + +static void add_oo_definition_line(const char *name, const char *line) +{ + struct oo_line *oo; + char *colon; + char *start; + + oo = &oo_lines[oo_line_count++]; + oo->name = strdup(name); + + if ((colon = strstr(oo->name, ":"))) + *colon = '\0'; + else { + printf("invalid OO definition\n"); + exit(1); + } + + start = strstr(line, ":") + 2; + oo->line = strdup(start); +} + +/* when OO_FOO: continues on multiple lines */ + +static void append_oo_definition_line(const char *new_line) +{ + struct oo_line *oo; + char *old_line; + char *line; + int len; + + oo = &oo_lines[oo_line_count-1]; + + old_line = oo->line; + + /* +2 = 1 space between old and new + 1 terminating \0 */ + len = strlen(old_line) + strlen(new_line) + 2; + line = malloc(len); + memset(line, 0, len); + + strcat(line, old_line); + strcat(line, " "); + strcat(line, new_line); + + free(oo->line); + oo->line = line; +} + +char *get_oo_line(char *str) +{ + char *name; + char *end; + char str2[64]; + int i; + + strcpy(str2, str); + if ((end = strstr(str2, ":"))) + *end = '\0'; + if ((end = strstr(str2, ","))) + *end = '\0'; + + for (i = 0; i < oo_line_count; i++) { + name = oo_lines[i].name; + if (!strcmp(name, str2)) + return oo_lines[i].line; + } + return NULL; +} + +/* add optional_opt_args entries when OO_FOO appears on OO: line */ + +static void include_optional_opt_args(struct command *cmd, char *str) +{ + char *oo_line; + char *line; + char *line_argv[MAX_LINE_ARGC]; + int line_argc; + + if (!(oo_line = get_oo_line(str))) { + printf("No OO line found for %s\n", str); + exit(1); + } + + if (!(line = strdup(oo_line))) + exit(1); + + split_line(line, &line_argc, line_argv, ' '); + add_optional_opt_line(cmd, line_argc, line_argv); + free(line); +} + +static void add_opt_arg(struct command *cmd, char *str, int *takes_arg, int required) +{ + char *comma; + int opt; + + /* opt_arg.opt set here */ + /* opt_arg.def will be set in update_prev_opt_arg() if needed */ + + if ((comma = strstr(str, ","))) + *comma = '\0'; + + /* + * Work around nasty hack where --uuid is used for both uuid_ARG + * and uuidstr_ARG. The input uses --uuidstr, where an actual + * command uses --uuid string. + */ + if (!strcmp(str, "--uuidstr")) { + opt = uuidstr_ARG; + goto skip; + } + + opt = opt_str_to_num(str); +skip: + if (required) + cmd->required_opt_args[cmd->ro_count++].opt = opt; + else + cmd->optional_opt_args[cmd->oo_count++].opt = opt; + + *takes_arg = opt_names[opt].val_enum ? 1 : 0; +} + +static void update_prev_opt_arg(struct command *cmd, char *str, int required) +{ + struct arg_def def = { 0 }; + char *comma; + + if (str[0] == '-') { + printf("Option %s must be followed by an arg.\n", str); + exit(1); + } + + /* opt_arg.def set here */ + /* opt_arg.opt was previously set in add_opt_arg() when --foo was read */ + + if ((comma = strstr(str, ","))) + *comma = '\0'; + + set_opt_def(cmd, str, &def); + + if (required) + cmd->required_opt_args[cmd->ro_count-1].def = def; + else + cmd->optional_opt_args[cmd->oo_count-1].def = def; +} + +static void add_pos_arg(struct command *cmd, char *str, int required) +{ + struct arg_def def = { 0 }; + + /* pos_arg.pos and pos_arg.def are set here */ + + set_pos_def(cmd, str, &def); + + if (required) { + cmd->required_pos_args[cmd->rp_count].pos = cmd->pos_count++; + cmd->required_pos_args[cmd->rp_count].def = def; + cmd->rp_count++; + } else { + cmd->optional_pos_args[cmd->op_count].pos = cmd->pos_count++;; + cmd->optional_pos_args[cmd->op_count].def = def; + cmd->op_count++; + } +} + +/* process something that follows a pos arg, which is not a new pos arg */ + +static void update_prev_pos_arg(struct command *cmd, char *str, int required) +{ + struct arg_def *def; + + /* a previous pos_arg.def is modified here */ + + if (required) + def = &cmd->required_pos_args[cmd->rp_count-1].def; + else + def = &cmd->optional_pos_args[cmd->op_count-1].def; + + if (!strcmp(str, "...")) + def->flags |= ARG_DEF_FLAG_MAY_REPEAT; + else { + printf("Unknown pos arg: %s\n", str); + exit(1); + } +} + +/* process what follows OO:, which are optional opt args */ + +static void add_optional_opt_line(struct command *cmd, int argc, char *argv[]) +{ + int takes_arg; + int i; + + for (i = 0; i < argc; i++) { + if (!i && !strncmp(argv[i], "OO:", 3)) + continue; + if (is_opt_name(argv[i])) + add_opt_arg(cmd, argv[i], &takes_arg, OPTIONAL); + else if (!strncmp(argv[i], "OO_", 3)) + include_optional_opt_args(cmd, argv[i]); + else if (takes_arg) + update_prev_opt_arg(cmd, argv[i], OPTIONAL); + else + printf("Can't parse argc %d argv %s prev %s\n", + i, argv[i], argv[i-1]); + } +} + +/* process what follows OP:, which are optional pos args */ + +static void add_optional_pos_line(struct command *cmd, int argc, char *argv[]) +{ + int i; + + for (i = 0; i < argc; i++) { + if (!i && !strncmp(argv[i], "OP:", 3)) + continue; + if (is_pos_name(argv[i])) + add_pos_arg(cmd, argv[i], OPTIONAL); + else + update_prev_pos_arg(cmd, argv[i], OPTIONAL); + } +} + +/* add required opt args from OO_FOO definition */ + +static void add_required_opt_line(struct command *cmd, int argc, char *argv[]) +{ + int takes_arg; + int i; + + for (i = 0; i < argc; i++) { + if (is_opt_name(argv[i])) + add_opt_arg(cmd, argv[i], &takes_arg, REQUIRED); + else if (takes_arg) + update_prev_opt_arg(cmd, argv[i], REQUIRED); + else + printf("Can't parse argc %d argv %s prev %s\n", + i, argv[i], argv[i-1]); + } +} + +/* add to required_opt_args when OO_FOO appears on required line */ + +static void include_required_opt_args(struct command *cmd, char *str) +{ + char *oo_line; + char *line; + char *line_argv[MAX_LINE_ARGC]; + int line_argc; + + if (!(oo_line = get_oo_line(str))) { + printf("No OO line found for %s\n", str); + exit(1); + } + + if (!(line = strdup(oo_line))) + exit(1); + + split_line(line, &line_argc, line_argv, ' '); + add_required_opt_line(cmd, line_argc, line_argv); + free(line); +} + +/* process what follows command_name, which are required opt/pos args */ + +static void add_required_line(struct command *cmd, int argc, char *argv[]) +{ + int i; + int takes_arg; + int prev_was_opt = 0, prev_was_pos = 0; + + /* argv[0] is command name */ + + for (i = 1; i < argc; i++) { + if (is_opt_name(argv[i])) { + add_opt_arg(cmd, argv[i], &takes_arg, REQUIRED); + prev_was_opt = 1; + prev_was_pos = 0; + } else if (prev_was_opt && takes_arg) { + update_prev_opt_arg(cmd, argv[i], REQUIRED); + prev_was_opt = 0; + prev_was_pos = 0; + } else if (is_pos_name(argv[i])) { + add_pos_arg(cmd, argv[i], REQUIRED); + prev_was_opt = 0; + prev_was_pos = 1; + } else if (!strncmp(argv[i], "OO_", 3)) { + cmd->cmd_flags |= CMD_FLAG_ONE_REQUIRED_OPT; + include_required_opt_args(cmd, argv[i]); + } else if (prev_was_pos) { + update_prev_pos_arg(cmd, argv[i], REQUIRED); + } else + printf("Can't parse argc %d argv %s prev %s\n", + i, argv[i], argv[i-1]); + + } +} + +static void print_def(struct arg_def *def, int usage) +{ + int val_enum; + int sep = 0; + int i; + + for (val_enum = 0; val_enum < VAL_COUNT; val_enum++) { + if (def->val_bits & val_enum_to_bit(val_enum)) { + + if (val_enum == conststr_VAL) + printf("%s", def->str); + + else if (val_enum == constnum_VAL) + printf("ll%u", (unsigned long long)def->num); + + else { + if (sep) printf("|"); + + if (!usage || !val_names[val_enum].usage) + printf("%s", val_names[val_enum].name); + else + printf("%s", val_names[val_enum].usage); + + sep = 1; + } + + if (val_enum == lv_VAL && def->lv_types) { + for (i = 0; i < 32; i++) { + if (def->lv_types & (1 << i)) + printf("_%s", lv_num_to_str(1 << i)); + } + } + + if ((val_enum == pv_VAL) || (val_enum == vg_VAL) || (val_enum == lv_VAL)) { + if (def->flags & ARG_DEF_FLAG_NEW) + printf("_new"); + } + } + } + + if (def->flags & ARG_DEF_FLAG_MAY_REPEAT) + printf(" ..."); +} + +void print_expanded(void) +{ + struct command *cmd; + int onereq; + int i, ro, rp, oo, op; + + for (i = 0; i < cmd_count; i++) { + cmd = &cmd_array[i]; + printf("%s", cmd->name); + + onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0; + + if (cmd->ro_count) { + if (onereq) + printf(" ("); + + for (ro = 0; ro < cmd->ro_count; ro++) { + if (ro && onereq) + printf(","); + printf(" %s", opt_names[cmd->required_opt_args[ro].opt].long_opt); + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + print_def(&cmd->required_opt_args[ro].def, 0); + } + } + if (onereq) + printf(" )"); + } + + if (cmd->rp_count) { + for (rp = 0; rp < cmd->rp_count; rp++) { + if (cmd->required_pos_args[rp].def.val_bits) { + printf(" "); + print_def(&cmd->required_pos_args[rp].def, 0); + } + } + } + + if (cmd->oo_count) { + printf("\n"); + printf("OO:"); + for (oo = 0; oo < cmd->oo_count; oo++) { + if (oo) + printf(","); + printf(" %s", opt_names[cmd->optional_opt_args[oo].opt].long_opt); + if (cmd->optional_opt_args[oo].def.val_bits) { + printf(" "); + print_def(&cmd->optional_opt_args[oo].def, 0); + } + } + } + + if (cmd->op_count) { + printf("\n"); + printf("OP:"); + for (op = 0; op < cmd->op_count; op++) { + if (cmd->optional_pos_args[op].def.val_bits) { + printf(" "); + print_def(&cmd->optional_pos_args[op].def, 0); + } + } + } + + printf("\n\n"); + } +} + +static int opt_arg_matches(struct opt_arg *oa1, struct opt_arg *oa2) +{ + if (oa1->opt != oa2->opt) + return 0; + + /* FIXME: some cases may need more specific val_bits checks */ + if (oa1->def.val_bits != oa2->def.val_bits) + return 0; + + if (oa1->def.str && oa2->def.str && strcmp(oa1->def.str, oa2->def.str)) + return 0; + + if (oa1->def.num != oa2->def.num) + return 0; + + /* + * Do NOT compare lv_types because we are checking if two + * command lines are ambiguous before the LV type is known. + */ + + return 1; +} + +static int pos_arg_matches(struct pos_arg *pa1, struct pos_arg *pa2) +{ + if (pa1->pos != pa2->pos) + return 0; + + /* FIXME: some cases may need more specific val_bits checks */ + if (pa1->def.val_bits != pa2->def.val_bits) + return 0; + + if (pa1->def.str && pa2->def.str && strcmp(pa1->def.str, pa2->def.str)) + return 0; + + if (pa1->def.num != pa2->def.num) + return 0; + + /* + * Do NOT compare lv_types because we are checking if two + * command lines are ambiguous before the LV type is known. + */ + + return 1; +} + +static const char *opt_to_enum_str(int opt) +{ + return opt_names[opt].name; +} + +static char *flags_to_str(int flags) +{ + static char buf_flags[32]; + + memset(buf_flags, 0, sizeof(buf_flags)); + + if (flags & ARG_DEF_FLAG_MAY_REPEAT) + strcat(buf_flags, "ARG_DEF_FLAG_MAY_REPEAT"); + if (flags & ARG_DEF_FLAG_NEW) + strcat(buf_flags, "ARG_DEF_FLAG_NEW"); + + return buf_flags; +} + +void print_command_count(void) +{ + struct command *cmd; + int i, j; + + printf("/* Do not edit. This file is generated by scripts/create-commands */\n"); + printf("/* using command definitions from scripts/command-lines.in */\n"); + printf("#define COMMAND_COUNT %d\n", cmd_count); + + printf("enum {\n"); + printf("\tno_CMD,\n"); /* enum value 0 is not used */ + + for (i = 0; i < cmd_count; i++) { + cmd = &cmd_array[i]; + + if (!cmd->command_line_id) { + printf("Missing ID: at %d\n", i); + exit(1); + } + + for (j = 0; j < i; j++) { + if (!strcmp(cmd->command_line_id, cmd_array[j].command_line_id)) + goto next; + } + + printf("\t%s_CMD,\n", cmd->command_line_id); + next: + ; + } + printf("\tCOMMAND_ID_COUNT,\n"); + printf("};\n"); +} + +static int is_common_opt(int opt) +{ + int oo; + + for (oo = 0; oo < common_options.oo_count; oo++) { + if (common_options.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) +{ + int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0; + int i, sep, ro, rp, oo, op; + + if (skip_required) + goto oo_count; + + printf("\"%s", cmd->name); + + if (cmd->ro_count) { + if (onereq) + printf(" ("); + for (ro = 0; ro < cmd->ro_count; ro++) { + if (ro && onereq) + printf(","); + printf(" %s", opt_names[cmd->required_opt_args[ro].opt].long_opt); + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + print_def(&cmd->required_opt_args[ro].def, 1); + } + } + if (onereq) + printf(" )"); + } + + if (cmd->rp_count) { + for (rp = 0; rp < cmd->rp_count; rp++) { + if (cmd->required_pos_args[rp].def.val_bits) { + printf(" "); + print_def(&cmd->required_pos_args[rp].def, 1); + } + } + } + + printf("\""); + + oo_count: + if (!cmd->oo_count) + goto op_count; + + sep = 0; + + if (cmd->oo_count) { + 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)) + continue; + + if (!sep) { + printf("\n"); + printf("\" ["); + } + + if (sep) + printf(","); + + printf(" %s", opt_names[cmd->optional_opt_args[oo].opt].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) + printf(" ]\""); + + op_count: + if (!cmd->op_count) + goto done; + + printf("\n"); + printf("\" ["); + + if (cmd->op_count) { + for (op = 0; op < cmd->op_count; op++) { + if (cmd->optional_pos_args[op].def.val_bits) { + printf(" "); + print_def(&cmd->optional_pos_args[op].def, 1); + } + } + } + + printf(" ]\""); + + done: + printf(";\n"); +} + +static void print_val_man(const char *str) +{ + char *line; + char *line_argv[MAX_LINE_ARGC]; + int line_argc; + int i; + + if (!strcmp(str, "Number") || + !strcmp(str, "String") || + !strncmp(str, "VG", 2) || + !strncmp(str, "LV", 2) || + !strncmp(str, "PV", 2) || + !strcmp(str, "Tag")) { + printf("\\fI%s\\fP", str); + return; + } + + if (strstr(str, "Number[") || strstr(str, "]Number")) { + for (i = 0; i < strlen(str); i++) { + if (str[i] == 'N') + printf("\\fI"); + if (str[i] == 'r') { + printf("%c", str[i]); + printf("\\fP"); + continue; + } + printf("%c", str[i]); + } + return; + } + + if (strstr(str, "|")) { + line = strdup(str); + split_line(line, &line_argc, line_argv, '|'); + for (i = 0; i < line_argc; i++) { + if (i) + printf("|"); + if (strstr(line_argv[i], "Number")) + printf("\\fI%s\\fP", line_argv[i]); + else + printf("\\fB%s\\fP", line_argv[i]); + } + return; + } + + printf("\\fB%s\\fP", str); +} + +static void print_def_man(struct arg_def *def, int usage) +{ + int val_enum; + int sep = 0; + int i; + + for (val_enum = 0; val_enum < VAL_COUNT; val_enum++) { + if (def->val_bits & val_enum_to_bit(val_enum)) { + + if (val_enum == conststr_VAL) { + printf("\\fB"); + printf("%s", def->str); + printf("\\fP"); + } + + else if (val_enum == constnum_VAL) { + printf("\\fB"); + printf("ll%u", (unsigned long long)def->num); + printf("\\fP"); + } + + else { + if (sep) printf("|"); + + if (!usage || !val_names[val_enum].usage) { + printf("\\fI"); + printf("%s", val_names[val_enum].name); + printf("\\fP"); + } else { + print_val_man(val_names[val_enum].usage); + } + + sep = 1; + } + + if (val_enum == lv_VAL && def->lv_types) { + printf("\\fI"); + for (i = 0; i < 32; i++) { + if (def->lv_types & (1 << i)) + printf("_%s", lv_num_to_str(1 << i)); + } + printf("\\fP"); + } + + if ((val_enum == pv_VAL) || (val_enum == vg_VAL) || (val_enum == lv_VAL)) { + if (def->flags & ARG_DEF_FLAG_NEW) { + printf("\\fI"); + printf("_new"); + printf("\\fP"); + } + } + } + } + + if (def->flags & ARG_DEF_FLAG_MAY_REPEAT) + printf(" ..."); +} + +void print_cmd_man(struct command *cmd, int skip_required) +{ + int onereq = (cmd->cmd_flags & CMD_FLAG_ONE_REQUIRED_OPT) ? 1 : 0; + int i, sep, ro, rp, oo, op; + + if (skip_required) + goto oo_count; + + printf("\\fB%s\\fP", cmd->name); + + if (!onereq) + goto ro_normal; + + /* + * one required option in a set, print as: + * ( -a|--a, + * -b|--b, + * --c, + * --d ) + * + * First loop through ro prints those with short opts, + * and the second loop prints those without short opts. + */ + + if (cmd->ro_count) { + printf("\n"); + printf(".RS 4\n"); + printf("("); + + sep = 0; + + for (ro = 0; ro < cmd->ro_count; ro++) { + if (!opt_names[cmd->required_opt_args[ro].opt].short_opt) + continue; + + if (sep) { + printf(","); + printf("\n.br\n"); + printf(" "); + } + + if (opt_names[cmd->required_opt_args[ro].opt].short_opt) { + printf(" \\fB-%c\\fP|\\fB%s\\fP", + opt_names[cmd->required_opt_args[ro].opt].short_opt, + opt_names[cmd->required_opt_args[ro].opt].long_opt); + } else { + printf(" "); + printf(" \\fB%s\\fP", opt_names[cmd->required_opt_args[ro].opt].long_opt); + } + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + print_def_man(&cmd->required_opt_args[ro].def, 1); + } + + sep = 1; + } + + for (ro = 0; ro < cmd->ro_count; ro++) { + if (opt_names[cmd->required_opt_args[ro].opt].short_opt) + continue; + + if (sep) { + printf(","); + printf("\n.br\n"); + printf(" "); + } + + printf(" "); + printf(" \\fB%s\\fP", opt_names[cmd->required_opt_args[ro].opt].long_opt); + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + print_def_man(&cmd->required_opt_args[ro].def, 1); + } + + sep = 1; + } + + printf(" )\n"); + printf(".RE\n"); + } + + if (cmd->rp_count) { + printf(".RS 4\n"); + for (rp = 0; rp < cmd->rp_count; rp++) { + if (cmd->required_pos_args[rp].def.val_bits) { + printf(" "); + print_def_man(&cmd->required_pos_args[rp].def, 1); + } + } + + printf("\n"); + printf(".RE\n"); + } else { + /* printf("\n"); */ + } + + printf(".br\n"); + goto oo_count; + + ro_normal: + + /* + * all are required options, print as: + * -a|--a, -b|--b + */ + + if (cmd->ro_count) { + for (ro = 0; ro < cmd->ro_count; ro++) { + if (opt_names[cmd->required_opt_args[ro].opt].short_opt) { + printf(" \\fB-%c\\fP|\\fB%s\\fP", + opt_names[cmd->required_opt_args[ro].opt].short_opt, + opt_names[cmd->required_opt_args[ro].opt].long_opt); + } else { + printf(" \\fB%s\\fP", opt_names[cmd->required_opt_args[ro].opt].long_opt); + } + + if (cmd->required_opt_args[ro].def.val_bits) { + printf(" "); + print_def_man(&cmd->required_opt_args[ro].def, 1); + } + } + } + + if (cmd->rp_count) { + for (rp = 0; rp < cmd->rp_count; rp++) { + if (cmd->required_pos_args[rp].def.val_bits) { + printf(" "); + print_def_man(&cmd->required_pos_args[rp].def, 1); + } + } + + printf("\n"); + } else { + printf("\n"); + } + + printf(".br\n"); + + oo_count: + if (!cmd->oo_count) + goto op_count; + + sep = 0; + + printf(".br\n"); + + if (cmd->oo_count) { + + 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)) + continue; + + if (!opt_names[cmd->optional_opt_args[oo].opt].short_opt) + continue; + + if (!sep) { + printf(".RS 4\n"); + printf("["); + } + + if (sep) { + printf(","); + printf("\n.br\n"); + printf(" "); + } + + 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); + + if (cmd->optional_opt_args[oo].def.val_bits) { + printf(" "); + print_def_man(&cmd->optional_opt_args[oo].def, 1); + } + sep = 1; + } + + 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)) + continue; + + if (opt_names[cmd->optional_opt_args[oo].opt].short_opt) + continue; + + if (!sep) { + printf(".RS 4\n"); + printf("["); + } + + if (sep) { + printf(","); + printf("\n.br\n"); + printf(" "); + } + + /* space alignment without short opt */ + printf(" "); + + printf(" \\fB%s\\fP", opt_names[cmd->optional_opt_args[oo].opt].long_opt); + + if (cmd->optional_opt_args[oo].def.val_bits) { + printf(" "); + print_def_man(&cmd->optional_opt_args[oo].def, 1); + } + sep = 1; + } + } + + if (sep) { + printf(" ]\n"); + printf(".RE\n"); + printf(".br\n"); + } + + op_count: + if (!cmd->op_count) + goto done; + + printf(".RS 4\n"); + printf("["); + + if (cmd->op_count) { + for (op = 0; op < cmd->op_count; op++) { + if (cmd->optional_pos_args[op].def.val_bits) { + printf(" "); + print_def_man(&cmd->optional_pos_args[op].def, 1); + } + } + } + + printf("]\n"); + printf(".RE\n"); + + done: + printf("\n"); +} + +#define DESC_LINE 256 + +void print_desc_man(const char *desc) +{ + char buf[DESC_LINE] = {0}; + int di = 0; + 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) { + printf("%s\n", buf); + printf(".br\n"); + memset(buf, 0, sizeof(buf)); + bi = 0; + } + di += 5; + continue; + } + + if (!bi && desc[di] == ' ') + continue; + + buf[bi++] = desc[di]; + + if (bi == (DESC_LINE - 1)) + break; + } + + if (bi) { + printf("%s\n", buf); + printf(".br\n"); + } +} + +void print_command_man(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"); + + printf(".TH LVM_ALL 8\n"); + + for (i = 0; i < cmd_count; i++) { + + cmd = &cmd_array[i]; + + if (!last_cmd_name || strcmp(last_cmd_name, cmd->name)) { + printf(".SH NAME\n"); + printf(".\n"); + if ((desc = cmd_name_desc(cmd->name))) + printf("%s - %s\n", cmd->name, desc); + else + printf("%s\n", cmd->name); + printf(".br\n"); + printf(".P\n"); + printf(".\n"); + printf(".SH SYNOPSIS\n"); + printf(".br\n"); + printf(".P\n"); + printf(".\n"); + last_cmd_name = cmd->name; + } + + if (cmd->desc) { + print_desc_man(cmd->desc); + printf(".P\n"); + } + + print_cmd_man(cmd, 0); + + 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); + } + + printf("\n"); + continue; + } +} + +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"); + + printf("/* Do not edit. This file is generated by scripts/create-commands */\n"); + printf("/* using command definitions from scripts/command-lines.in */\n"); + printf("\n"); + + for (i = 0; i < cmd_count; i++) { + cmd = &cmd_array[i]; + + if (only_usage) { + print_usage(cmd, 0); + print_usage(&common_options, 1); + printf("\n"); + continue; + } + + printf("commands[%d].name = \"%s\";\n", i, cmd->name); + printf("commands[%d].command_line_id = \"%s\";\n", i, cmd->command_line_id); + printf("commands[%d].command_line_enum = %s_CMD;\n", i, cmd->command_line_id); + printf("commands[%d].fn = %s;\n", i, cmd->name); + printf("commands[%d].ro_count = %d;\n", i, cmd->ro_count); + printf("commands[%d].rp_count = %d;\n", i, cmd->rp_count); + 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); + + printf("commands[%d].desc = \"%s\";\n", i, cmd->desc ?: ""); + printf("commands[%d].usage = ", i); + print_usage(cmd, 0); + + if (cmd->oo_count) { + printf("commands[%d].usage_common = ", i); + print_usage(&common_options, 1); + } else { + printf("commands[%d].usage_common = \"NULL\";\n", i); + } + + if (cmd->ro_count) { + for (ro = 0; ro < cmd->ro_count; ro++) { + printf("commands[%d].required_opt_args[%d].opt = %s;\n", + i, ro, opt_to_enum_str(cmd->required_opt_args[ro].opt)); + + if (!cmd->required_opt_args[ro].def.val_bits) + continue; + + printf("commands[%d].required_opt_args[%d].def.val_bits = %s;\n", + i, ro, val_bits_to_str(cmd->required_opt_args[ro].def.val_bits)); + + if (cmd->required_opt_args[ro].def.lv_types) + printf("commands[%d].required_opt_args[%d].def.lv_types = %s;\n", + i, ro, lv_types_to_flags(cmd->required_opt_args[ro].def.lv_types)); + + if (cmd->required_opt_args[ro].def.flags) + printf("commands[%d].required_opt_args[%d].def.flags = %s;\n", + i, ro, flags_to_str(cmd->required_opt_args[ro].def.flags)); + + if (val_bit_is_set(cmd->required_opt_args[ro].def.val_bits, constnum_VAL)) + printf("commands[%d].required_opt_args[%d].def.num = %d;\n", + i, ro, cmd->required_opt_args[ro].def.num); + + if (val_bit_is_set(cmd->required_opt_args[ro].def.val_bits, conststr_VAL)) + printf("commands[%d].required_opt_args[%d].def.str = \"%s\";\n", + i, ro, cmd->required_opt_args[ro].def.str ?: "NULL"); + } + } + + if (cmd->rp_count) { + for (rp = 0; rp < cmd->rp_count; rp++) { + printf("commands[%d].required_pos_args[%d].pos = %d;\n", + i, rp, cmd->required_pos_args[rp].pos); + + if (!cmd->required_pos_args[rp].def.val_bits) + continue; + + printf("commands[%d].required_pos_args[%d].def.val_bits = %s;\n", + i, rp, val_bits_to_str(cmd->required_pos_args[rp].def.val_bits)); + + if (cmd->required_pos_args[rp].def.lv_types) + printf("commands[%d].required_pos_args[%d].def.lv_types = %s;\n", + i, rp, lv_types_to_flags(cmd->required_pos_args[rp].def.lv_types)); + + if (cmd->required_pos_args[rp].def.flags) + printf("commands[%d].required_pos_args[%d].def.flags = %s;\n", + i, rp, flags_to_str(cmd->required_pos_args[rp].def.flags)); + + if (val_bit_is_set(cmd->required_pos_args[rp].def.val_bits, constnum_VAL)) + printf("commands[%d].required_pos_args[%d].def.num = %d;\n", + i, rp, cmd->required_pos_args[rp].def.num); + + if (val_bit_is_set(cmd->required_pos_args[rp].def.val_bits, conststr_VAL)) + printf("commands[%d].required_pos_args[%d].def.str = \"%s\";\n", + i, rp, cmd->required_pos_args[rp].def.str ?: "NULL"); + } + } + + if (cmd->oo_count) { + for (oo = 0; oo < cmd->oo_count; oo++) { + printf("commands[%d].optional_opt_args[%d].opt = %s;\n", + i, oo, opt_to_enum_str(cmd->optional_opt_args[oo].opt)); + + if (!cmd->optional_opt_args[oo].def.val_bits) + continue; + + printf("commands[%d].optional_opt_args[%d].def.val_bits = %s;\n", + i, oo, val_bits_to_str(cmd->optional_opt_args[oo].def.val_bits)); + + if (cmd->optional_opt_args[oo].def.lv_types) + printf("commands[%d].optional_opt_args[%d].def.lv_types = %s;\n", + i, oo, lv_types_to_flags(cmd->optional_opt_args[oo].def.lv_types)); + + if (cmd->optional_opt_args[oo].def.flags) + printf("commands[%d].optional_opt_args[%d].def.flags = %s;\n", + i, oo, flags_to_str(cmd->optional_opt_args[oo].def.flags)); + + if (val_bit_is_set(cmd->optional_opt_args[oo].def.val_bits, constnum_VAL)) + printf("commands[%d].optional_opt_args[%d].def.num = %d;\n", + i, oo, cmd->optional_opt_args[oo].def.num); + + if (val_bit_is_set(cmd->optional_opt_args[oo].def.val_bits, conststr_VAL)) + printf("commands[%d].optional_opt_args[%d].def.str = \"%s\";\n", + i, oo, cmd->optional_opt_args[oo].def.str ?: "NULL"); + } + } + + if (cmd->op_count) { + for (op = 0; op < cmd->op_count; op++) { + printf("commands[%d].optional_pos_args[%d].pos = %d;\n", + i, op, cmd->optional_pos_args[op].pos); + + if (!cmd->optional_pos_args[op].def.val_bits) + continue; + + printf("commands[%d].optional_pos_args[%d].def.val_bits = %s;\n", + i, op, val_bits_to_str(cmd->optional_pos_args[op].def.val_bits)); + + if (cmd->optional_pos_args[op].def.lv_types) + printf("commands[%d].optional_pos_args[%d].def.lv_types = %s;\n", + i, op, lv_types_to_flags(cmd->optional_pos_args[op].def.lv_types)); + + if (cmd->optional_pos_args[op].def.flags) + printf("commands[%d].optional_pos_args[%d].def.flags = %s;\n", + i, op, flags_to_str(cmd->optional_pos_args[op].def.flags)); + + if (val_bit_is_set(cmd->optional_pos_args[op].def.val_bits, constnum_VAL)) + printf("commands[%d].optional_pos_args[%d].def.num = %d;\n", + i, op, cmd->optional_pos_args[op].def.num); + + if (val_bit_is_set(cmd->optional_pos_args[op].def.val_bits, conststr_VAL)) + printf("commands[%d].optional_pos_args[%d].def.str = \"%s\";\n", + i, op, cmd->optional_pos_args[op].def.str ?: "NULL"); + } + } + + printf("\n"); + } +} + +struct cmd_pair { + int i, j; +}; + +static void print_ambiguous(void) +{ + struct command *cmd, *dup; + struct cmd_pair dups[64] = { 0 }; + int found = 0; + int i, j, f, ro, rp; + + for (i = 0; i < cmd_count; i++) { + cmd = &cmd_array[i]; + + for (j = 0; j < cmd_count; j++) { + dup = &cmd_array[j]; + + if (i == j) + continue; + if (strcmp(cmd->name, dup->name)) + 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], + &dup->required_opt_args[ro])) + goto next; + } + + for (rp = 0; rp < cmd->rp_count; rp++) { + if (!pos_arg_matches(&cmd->required_pos_args[rp], + &dup->required_pos_args[rp])) + goto next; + } + + for (f = 0; f < found; f++) { + if ((dups[f].i == j) && (dups[f].j == i)) + goto next; + } + + printf("Ambiguous commands %d and %d:\n", i, j); + print_usage(cmd, 0); + print_usage(dup, 0); + printf("\n"); + + dups[found].i = i; + dups[found].j = j; + found++; +next: + ; + } + } +} + +void print_command_list(void) +{ + int i; + + for (i = 0; i < MAX_CMD_NAMES; i++) { + if (!cmd_names[i].name) { + printf("found %d command names\n", i); + break; + } + printf("%s\n", cmd_names[i].name); + } +} + +void print_option_list(void) +{ + int i; + + for (i = 0; i < ARG_COUNT; i++) + printf("%d %s %s %c (%d)\n", + opt_names[i].opt_enum, opt_names[i].name, + opt_names[i].long_opt, opt_names[i].short_opt ?: ' ', + opt_names[i].short_opt ? opt_names[i].short_opt : 0); +} + +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("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[]) +{ + char *outputformat = NULL; + char *inputfile = NULL; + FILE *file; + struct command *cmd; + char line[MAX_LINE]; + char line_orig[MAX_LINE]; + const char *name; + char *line_argv[MAX_LINE_ARGC]; + char *n; + int line_argc; + int prev_was_oo_def = 0; + int prev_was_oo = 0; + int prev_was_op = 0; + + if (argc < 2) { + print_help(argc, argv); + exit(EXIT_FAILURE); + } + + if (!strcmp(argv[1], "debug")) { + print_command_list(); + print_option_list(); + return 0; + } + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"output", required_argument, 0, 'o' }, + {0, 0, 0, 0 } + }; + + while (1) { + int c; + int option_index = 0; + + c = getopt_long(argc, argv, "ho:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case '0': + break; + case 'h': + print_help(argc, argv); + exit(EXIT_SUCCESS); + case 'o': + outputformat = strdup(optarg); + break; + } + } + + if (optind < argc) + inputfile = argv[optind]; + else { + printf("Missing filename.\n"); + return 0; + } + + if (!(file = fopen(inputfile, "r"))) { + printf("Cannot open %s\n", argv[1]); + return -1; + } + + while (fgets(line, MAX_LINE, file)) { + if (line[0] == '#') + continue; + if (line[0] == '\n') + continue; + if (line[0] == '-' && line[1] == '-' && line[2] == '-') + continue; + + if ((n = strchr(line, '\n'))) + *n = '\0'; + + memcpy(line_orig, line, sizeof(line)); + split_line(line, &line_argc, line_argv, ' '); + + if (!line_argc) + continue; + + /* command ... */ + if ((name = is_command_name(line_argv[0]))) { + if (cmd_count >= MAX_CMDS) { + printf("MAX_CMDS too small\n"); + return -1; + } + cmd = &cmd_array[cmd_count++]; + cmd->name = name; + cmd->pos_count = 1; + add_required_line(cmd, line_argc, line_argv); + + /* Every cmd gets the OO_ALL options */ + include_optional_opt_args(cmd, "OO_ALL:"); + continue; + } + + 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); + free(desc); + } else + cmd->desc = desc; + continue; + } + + if (is_id_line(line_argv[0])) { + cmd->command_line_id = strdup(line_argv[1]); + continue; + } + + /* OO_FOO: ... */ + if (is_oo_definition(line_argv[0])) { + add_oo_definition_line(line_argv[0], line_orig); + prev_was_oo_def = 1; + prev_was_oo = 0; + prev_was_op = 0; + continue; + } + + /* OO: ... */ + if (is_oo_line(line_argv[0])) { + add_optional_opt_line(cmd, line_argc, line_argv); + prev_was_oo_def = 0; + prev_was_oo = 1; + prev_was_op = 0; + continue; + } + + /* OP: ... */ + if (is_op_line(line_argv[0])) { + add_optional_pos_line(cmd, line_argc, line_argv); + prev_was_oo_def = 0; + prev_was_oo = 0; + prev_was_op = 1; + continue; + } + + /* handle OO_FOO:, OO:, OP: continuing on multiple lines */ + + if (prev_was_oo_def) { + append_oo_definition_line(line_orig); + continue; + } + + if (prev_was_oo) { + add_optional_opt_line(cmd, line_argc, line_argv); + continue; + } + + if (prev_was_op) { + add_optional_pos_line(cmd, line_argc, line_argv); + continue; + } + } + + fclose(file); + + if (!outputformat) + print_command_struct(1); + else if (!strcmp(outputformat, "struct")) + print_command_struct(0); + else if (!strcmp(outputformat, "count")) + print_command_count(); + else if (!strcmp(outputformat, "usage")) + print_command_struct(1); + else if (!strcmp(outputformat, "expanded")) + print_expanded(); + else if (!strcmp(outputformat, "ambiguous")) + print_ambiguous(); + else if (!strcmp(outputformat, "man")) + print_command_man(); + else + print_help(argc, argv); +} + |