From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55315) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WMMHf-0000Zk-Ep for qemu-devel@nongnu.org; Sat, 08 Mar 2014 13:47:49 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WMMHZ-00014C-7e for qemu-devel@nongnu.org; Sat, 08 Mar 2014 13:47:43 -0500 Received: from mail-yk0-f176.google.com ([209.85.160.176]:64985) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WMMHY-00013i-U3 for qemu-devel@nongnu.org; Sat, 08 Mar 2014 13:47:37 -0500 Received: by mail-yk0-f176.google.com with SMTP id 19so14630357ykq.7 for ; Sat, 08 Mar 2014 10:47:36 -0800 (PST) From: Leandro Dorileo Date: Sat, 8 Mar 2014 15:47:18 -0300 Message-Id: <1394304438-14848-3-git-send-email-l@dorileo.org> In-Reply-To: <1394304438-14848-1-git-send-email-l@dorileo.org> References: <1394304438-14848-1-git-send-email-l@dorileo.org> Subject: [Qemu-devel] [PATCH RFC 2/2] qemu-img: migrate to use qemu-arg List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Kevin Wolf , Peter Maydell , Fam Zheng , Leandro Dorileo , Stefan Weil , Michael Tokarev , Stefan Hajnoczi , Paolo Bonzini , Laszlo Ersek , Peter Lieven Remove the arg parsing implementations using getopt and use qemu-arg. Also remove the qemu-img-cmds.hx since it's now generated on building time, adapted the build system to generate the .hx file using the qemu-img itself using the qemu-arg internal command generate-hx. Signed-off-by: Leandro Dorileo --- .gitignore | 1 + Makefile | 12 +- qemu-img-cmds.hx | 77 ---- qemu-img-descs.h | 128 ++++++ qemu-img.c | 1184 ++++++++++++++++++------------------------------------ 5 files changed, 531 insertions(+), 871 deletions(-) delete mode 100644 qemu-img-cmds.hx create mode 100644 qemu-img-descs.h diff --git a/.gitignore b/.gitignore index ef7019f..700e92d 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,4 @@ cscope.* tags TAGS *~ +qemu-img-cmds.hx diff --git a/Makefile b/Makefile index bd9cd4f..0f2f102 100644 --- a/Makefile +++ b/Makefile @@ -214,8 +214,6 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' ###################################################################### -qemu-img.o: qemu-img-cmds.h - qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a @@ -225,9 +223,6 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap -qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") - qemu-ga$(EXESUF): LIBS = $(LIBS_QGA) qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated @@ -272,7 +267,7 @@ clean: rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -f fsdev/*.pod rm -rf .libs */.libs - rm -f qemu-img-cmds.h + rm -f qemu-img-cmds.hx @# May not be present in GENERATED_HEADERS rm -f trace/generated-tracers-dtrace.dtrace* rm -f trace/generated-tracers-dtrace.h* @@ -442,7 +437,10 @@ qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@") -qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx +qemu-img-cmds.hx: qemu-img$(EXESUF) + $(call quiet-command,$(SRC_PATH)/qemu-img generate-hx -h > $(SRC_PATH)/$@," GEN $@") + +qemu-img-cmds.texi: qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx deleted file mode 100644 index d029609..0000000 --- a/qemu-img-cmds.hx +++ /dev/null @@ -1,77 +0,0 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version -HXCOMM DEF(command, callback, arg_string) is used to construct -HXCOMM command structures and help message. -HXCOMM HXCOMM can be used for comments, discarded from both texi and C - -STEXI -@table @option -ETEXI - -DEF("check", img_check, - "check [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] filename") -STEXI -@item check [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] @var{filename} -ETEXI - -DEF("create", img_create, - "create [-q] [-f fmt] [-o options] filename [size]") -STEXI -@item create [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] -ETEXI - -DEF("commit", img_commit, - "commit [-q] [-f fmt] [-t cache] filename") -STEXI -@item commit [-q] [-f @var{fmt}] [-t @var{cache}] @var{filename} -ETEXI - -DEF("compare", img_compare, - "compare [-f fmt] [-F fmt] [-p] [-q] [-s] filename1 filename2") -STEXI -@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-q] [-s] @var{filename1} @var{filename2} -ETEXI - -DEF("convert", img_convert, - "convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename") -STEXI -@item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} -ETEXI - -DEF("info", img_info, - "info [-f fmt] [--output=ofmt] [--backing-chain] filename") -STEXI -@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename} -ETEXI - -DEF("map", img_map, - "map [-f fmt] [--output=ofmt] filename") -STEXI -@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename} -ETEXI - -DEF("snapshot", img_snapshot, - "snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") -STEXI -@item snapshot [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} -ETEXI - -DEF("rebase", img_rebase, - "rebase [-q] [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") -STEXI -@item rebase [-q] [-f @var{fmt}] [-t @var{cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} -ETEXI - -DEF("resize", img_resize, - "resize [-q] filename [+ | -]size") -STEXI -@item resize [-q] @var{filename} [+ | -]@var{size} -ETEXI - -DEF("amend", img_amend, - "amend [-q] [-f fmt] -o options filename") -STEXI -@item amend [-q] [-f @var{fmt}] -o @var{options} @var{filename} -@end table -ETEXI diff --git a/qemu-img-descs.h b/qemu-img-descs.h new file mode 100644 index 0000000..5725161 --- /dev/null +++ b/qemu-img-descs.h @@ -0,0 +1,128 @@ +#ifndef _QEMU_IMG_DESCS_H_ +#define _QEMU_IMG_DESCS_H_ + +#define OPT_POS_FILENAME_HELP \ + "the specified disk image file name" \ + +#define OPT_POS_SIZE_HELP \ + "the disk image size in bytes. Optional suffixes 'k' or 'K' " \ + "(kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M), " \ + "'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E'" \ + " (exabyte, 1024P) are supported. 'b' is ignored" \ + +#define OPT_INC_SHIK_HELP \ + "grow or shrink the image size i.e +1G to increase it 1G or -1G " \ + "to shirink it 1G" \ + +#define OPT_BACKING_FILE_HELP \ + "the backing file name" \ + +#define OPT_BACKING_FMT_HELP "the backing file format" + +#define OPT_FMT_HELP \ + "the disk image format. It is automatically guessed in most cases" \ + +#define OPT_SPARSE_SIZE_HELP \ + "indicates the consecutive number of bytes that must contain only " \ + "zeros for qemu-img to create a sparse image during conversion. " \ + "This value is rounded down to the nearest 512 bytes. You may" \ + "use the common size suffixes like \"k\" for kilobytes." \ + +#define OPT_SNAPSHOT_ID_NAME_HELP "a snapshot id or name" + +#define OPT_OPTIONS \ + "a comma separated list of format specific options " \ + "in a name=value format. Use -o ? for an overview of the options " \ + "supported by the used format" \ + +#define OPT_CACHE_HELP \ + "the cache mode used to write the output disk image, the valid " \ + "options are: 'none', 'writeback' (default, except for convert), " \ + "'writethrough', 'directsync' and 'unsafe' (default for convert)" \ + + +#define OPT_COMPRESS_HELP \ + "indicates that target image must be compressed (qcow format only)" \ + +#define OPT_OFMT_HELP \ + "the output format" \ + +#define OPT_OUT_BASE_IMG_HELP \ + "the output base image" \ + +#define OPT_SNAPSHOT_OPT_HELP \ + "snapshot option like snapshot.*" \ + +#define OPT_OUTPUT_HELP \ + "takes the format in which the output must be " \ + "done (human or json)" \ + +#define OPT_BACKING_CHAIN_HELP \ + "will enumerate information about backing files in a disk" \ + "image chain" \ + +#define OPT_QUIET_HELP \ + "use Quiet mode - do not print any output (except errors)" \ + +#define OPT_REPAIR_HELP \ + "tries to repair any inconsistencies that are found during " \ + "the check. '-r leaks' repairs only cluster leaks, whereas " \ + "'-r all' fixes all kinds of errors, with a higher risk of " \ + "choosing the wrong fix or hiding corruption that has " \ + "already occurred." \ + +#define OPT_FMT2_HELP \ + "second image format" \ + +#define OPT_PROGRESS_HELP \ + "show progress of command" \ + +#define OPT_STRICT_HELP \ + "run in Strict mode - fail on different image size or " \ + "sector allocation" \ + +#define OPT_SKIP_HELP \ + "skips the target volume creation (useful if the volume " \ + "is created prior to running qemu-img)" \ + +#define OPT_SNAPSHOT_PARAM_HELP \ + "the snapshot params" \ + +#define OPT_SNAPSHOT_LIST_HELP \ + "lists all snapshots in the given image" \ + +#define OPT_SNAPSHOT_APPLY_HELP \ + "applies a snapshot (revert disk to saved state)" \ + +#define OPT_SNAPSHOT_CREATE_HELP \ + "create a snapshot" \ + +#define OPT_SNAPSHOT_DELETE_HELP \ + "delete a snapshot" \ + +#define OPT_UNSAFE_HELP \ + "enables unsafe rebasing. It is assumed that old " \ + "and new backing file match exactly. The image doesn't " \ + "need a working backing file before rebasing in this case " \ + "(useful for renaming the backing file) " \ + +#define OPT_CREATE_E_DEP_HELP \ + "option -e is deprecated, please use \'-o encryption\' instead" \ + +#define OPT_CREATE_6_DEP_HELP \ + "option -6 is deprecated, please use \'-o compat6\' instead" \ + +#define QEMU_IMG_CHECK_HELP "check image integrity/consistency" +#define QEMU_IMG_CREATE_HELP "create a new image file" +#define QEMU_IMG_COMMIT_HELP "commit COW file into raw image" +#define QEMU_IMG_COMPARE_HELP "compare 2 image files" +#define QEMU_IMG_CONVERT_HELP "convert N image files or snapshot to" \ + " a new format" \ +#define QEMU_IMG_INFO_HELP "show image file informations" +#define QEMU_IMG_MAP_HELP "dump the image file metadata" +#define QEMU_IMG_SNAPSHOT_HELP "list, apply, create or delete snapshots" +#define QEMU_IMG_REBASE_HELP "changes the backing file of an image" +#define QEMU_IMG_RESIZE_HELP "resize an image file" +#define QEMU_IMG_AMEND_HELP "amend the image format" + +#endif /* _QEMU_IMG_DESCS_H_ */ diff --git a/qemu-img.c b/qemu-img.c index 2e40cc1..bafe439 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -31,6 +31,8 @@ #include "sysemu/sysemu.h" #include "block/block_int.h" #include "block/qapi.h" +#include "qemu/qemu-arg.h" +#include "qemu-img-descs.h" #include typedef struct img_cmd_t { @@ -52,85 +54,151 @@ typedef enum OutputFormat { #define BDRV_O_FLAGS BDRV_O_CACHE_WB #define BDRV_DEFAULT_CACHE "writeback" -static void format_print(void *opaque, const char *name) -{ - printf(" %s", name); -} +static bool _quiet, _progress, _strict, _skip_create, _backing_chain; +static bool _snapshot_list, _unsafe, _compress; + +static char *_filename, *_filename2, *_fmt, *_ofmt, *_fmt2; +static char *_rep, *_options, *_size, *_cache, *_sparse_size; +static char *_snapshot_name, *_snapshot_apply, *_snapshot_create; +static char *_snapshot_delete; +static char *_backing_file, *_backing_fmt, *_output, *_out_baseimg; +static char *_out_basefmt, **_filename_list; + +#define PROLOGUE \ + "qemu-img version "QEMU_VERSION", Copyright (c) 2004-2008 " \ + "Fabrice Bellard\nQEMU disk image utility" \ + +static QemuArgOpt _check_args[] = { + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_STR('f', "format", "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_STR(0, "output", "ofmt", OPT_OUTPUT_HELP, &_output, "human"), + OPT_STR('r', "repair", "[leaks | all]", OPT_REPAIR_HELP, &_rep, "all"), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_SENTINEL +}; -/* Please keep in synch with qemu-img.texi */ -static void help(void) -{ - const char *help_msg = - "qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n" - "usage: qemu-img command [command options]\n" - "QEMU disk image utility\n" - "\n" - "Command syntax:\n" -#define DEF(option, callback, arg_string) \ - " " arg_string "\n" -#include "qemu-img-cmds.h" -#undef DEF -#undef GEN_DOCS - "\n" - "Command parameters:\n" - " 'filename' is a disk image filename\n" - " 'fmt' is the disk image format. It is guessed automatically in most cases\n" - " 'cache' is the cache mode used to write the output disk image, the valid\n" - " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" - " 'directsync' and 'unsafe' (default for convert)\n" - " 'size' is the disk image size in bytes. Optional suffixes\n" - " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n" - " 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n" - " supported. 'b' is ignored.\n" - " 'output_filename' is the destination disk image filename\n" - " 'output_fmt' is the destination format\n" - " 'options' is a comma separated list of format specific options in a\n" - " name=value format. Use -o ? for an overview of the options supported by the\n" - " used format\n" - " 'snapshot_param' is param used for internal snapshot, format\n" - " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" - " '[ID_OR_NAME]'\n" - " 'snapshot_id_or_name' is deprecated, use 'snapshot_param'\n" - " instead\n" - " '-c' indicates that target image must be compressed (qcow format only)\n" - " '-u' enables unsafe rebasing. It is assumed that old and new backing file\n" - " match exactly. The image doesn't need a working backing file before\n" - " rebasing in this case (useful for renaming the backing file)\n" - " '-h' with or without a command shows this help and lists the supported formats\n" - " '-p' show progress of command (only certain commands)\n" - " '-q' use Quiet mode - do not print any output (except errors)\n" - " '-S' indicates the consecutive number of bytes (defaults to 4k) that must\n" - " contain only zeros for qemu-img to create a sparse image during\n" - " conversion. If the number of bytes is 0, the source will not be scanned for\n" - " unallocated or zero sectors, and the destination image will always be\n" - " fully allocated\n" - " '--output' takes the format in which the output must be done (human or json)\n" - " '-n' skips the target volume creation (useful if the volume is created\n" - " prior to running qemu-img)\n" - "\n" - "Parameters to check subcommand:\n" - " '-r' tries to repair any inconsistencies that are found during the check.\n" - " '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all\n" - " kinds of errors, with a higher risk of choosing the wrong fix or\n" - " hiding corruption that has already occurred.\n" - "\n" - "Parameters to snapshot subcommand:\n" - " 'snapshot' is the name of the snapshot to create, apply or delete\n" - " '-a' applies a snapshot (revert disk to saved state)\n" - " '-c' creates a snapshot\n" - " '-d' deletes a snapshot\n" - " '-l' lists all snapshots in the given image\n" - "\n" - "Parameters to compare subcommand:\n" - " '-f' first image format\n" - " '-F' second image format\n" - " '-s' run in Strict mode - fail on different image size or sector allocation\n"; - - printf("%s\nSupported formats:", help_msg); - bdrv_iterate_format(format_print, NULL); - printf("\n"); - exit(1); -} +static QemuArgOpt _create_args[] = { + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, NULL), + OPT_STR('F', NULL, "fmt", OPT_BACKING_FMT_HELP, &_backing_fmt, NULL), + OPT_STR('b', NULL, "filename", OPT_BACKING_FILE_HELP, &_backing_file, NULL), + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, "raw"), + OPT_CUMUL_STR('o', NULL, "options", OPT_OPTIONS, &_options, NULL), + OPT_DEP('e', NULL, OPT_CREATE_E_DEP_HELP), + OPT_DEP('6', NULL, OPT_CREATE_6_DEP_HELP), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_POS_REQ("size", OPT_POS_SIZE_HELP, &_size, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _commit_args[] = { + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_STR('t', NULL, "cache", OPT_CACHE_HELP, &_cache, BDRV_DEFAULT_CACHE), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _compare_args[] = { + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_STR('F', NULL, "fmt", OPT_FMT2_HELP, &_fmt2, NULL), + OPT_BOOL('p', NULL, OPT_PROGRESS_HELP, &_progress, false), + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_BOOL('s', NULL, OPT_STRICT_HELP, &_strict, false), + OPT_POS_REQ("filename1", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_POS_REQ("filename2", OPT_POS_FILENAME_HELP, &_filename2, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _convert_args[] = { + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_STR('O', NULL, "output_fmt", OPT_OFMT_HELP, &_ofmt, "raw"), + OPT_STR('B', NULL, "output_base_img", OPT_OUT_BASE_IMG_HELP, + &_out_baseimg, NULL), + OPT_BOOL('c', NULL, OPT_COMPRESS_HELP, &_compress, false), + OPT_DEP('e', NULL, OPT_CREATE_E_DEP_HELP), + OPT_DEP('6', NULL, OPT_CREATE_6_DEP_HELP), + OPT_CUMUL_STR('o', NULL, "options", OPT_OPTIONS, &_options, NULL), + OPT_CUMUL_STR('s', NULL, "snapshot_name", OPT_SNAPSHOT_ID_NAME_HELP, + &_snapshot_name, NULL), + OPT_CUMUL_STR('l', NULL, "snapshot_opt", OPT_SNAPSHOT_OPT_HELP, + &_snapshot_name, NULL), + OPT_CUMUL_STR('S', NULL, "sparse_size", OPT_SPARSE_SIZE_HELP, &_sparse_size, + NULL), + OPT_BOOL('p', NULL, OPT_PROGRESS_HELP, &_progress, false), + OPT_STR('t', NULL, "cache", OPT_CACHE_HELP, &_cache, "unsafe"), + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_BOOL('n', NULL, OPT_SKIP_HELP, &_skip_create, false), + OPT_POS_LIST("filename [filename2 [...]] output_filename", + OPT_POS_FILENAME_HELP, &_filename_list, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _info_args[] = { + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_STR(0, "output", "ofmt", OPT_OUTPUT_HELP, &_ofmt, NULL), + OPT_BOOL(0, "backing-chain", OPT_BACKING_CHAIN_HELP, &_backing_chain, + false), + OPT_POS("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _map_args[] = { + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_STR(0, "output", "ofmt", OPT_OUTPUT_HELP, &_ofmt, NULL), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _snapshot_action_args[] = { + OPT_BOOL('l', NULL, OPT_SNAPSHOT_LIST_HELP, &_snapshot_list, false), + OPT_STR('a', NULL, "snapshot", OPT_SNAPSHOT_APPLY_HELP, + &_snapshot_apply, NULL), + OPT_STR('c', NULL, "snapshot", OPT_SNAPSHOT_CREATE_HELP, &_snapshot_create, + NULL), + OPT_STR('d', NULL, "snapshot", OPT_SNAPSHOT_DELETE_HELP, &_snapshot_delete, + NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _snapshot_args[] = { + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt *_snapshot_mutual_groups[] = { + _snapshot_action_args, + OPT_MUTUAL_GROUP_SENTINEL +}; + +static QemuArgOpt _rebase_args[] = { + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_STR('t', NULL, "cache", OPT_CACHE_HELP, &_cache, BDRV_DEFAULT_CACHE), + OPT_BOOL('p', NULL, OPT_PROGRESS_HELP, &_progress, false), + OPT_BOOL('u', NULL, OPT_UNSAFE_HELP, &_unsafe, false), + OPT_STR('b', NULL, "backing_file", OPT_BACKING_FILE_HELP, &_backing_file, + NULL), + OPT_STR('F', NULL, "backing_fmt", OPT_BACKING_FMT_HELP, &_out_basefmt, + NULL), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _resize_args[] = { + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_POS_REQ("[+ | -]size", OPT_INC_SHIK_HELP, &_size, NULL), + OPT_SENTINEL +}; + +static QemuArgOpt _amend_args[] = { + OPT_BOOL('q', NULL, OPT_QUIET_HELP, &_quiet, false), + OPT_STR('f', NULL, "fmt", OPT_FMT_HELP, &_fmt, NULL), + OPT_CUMUL_STR_REQ('o', NULL, "options", OPT_OPTIONS, &_options, NULL), + OPT_POS_REQ("filename", OPT_POS_FILENAME_HELP, &_filename, NULL), + OPT_SENTINEL +}; static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...) { @@ -332,81 +400,19 @@ static int add_old_style_options(const char *fmt, QEMUOptionParameter *list, return 0; } -static int img_create(int argc, char **argv) +static int img_create(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c; uint64_t img_size = -1; - const char *fmt = "raw"; - const char *base_fmt = NULL; - const char *filename; - const char *base_filename = NULL; - char *options = NULL; Error *local_err = NULL; - bool quiet = false; - for(;;) { - c = getopt(argc, argv, "F:b:f:he6o:q"); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - break; - case 'F': - base_fmt = optarg; - break; - case 'b': - base_filename = optarg; - break; - case 'f': - fmt = optarg; - break; - case 'e': - error_report("option -e is deprecated, please use \'-o " - "encryption\' instead!"); - goto fail; - case '6': - error_report("option -6 is deprecated, please use \'-o " - "compat6\' instead!"); - goto fail; - case 'o': - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); - goto fail; - } - if (!options) { - options = g_strdup(optarg); - } else { - char *old_options = options; - options = g_strdup_printf("%s,%s", options, optarg); - g_free(old_options); - } - break; - case 'q': - quiet = true; - break; - } - } - - /* Get the filename */ - filename = (optind < argc) ? argv[optind] : NULL; - if (options && has_help_option(options)) { - g_free(options); - return print_block_option_help(filename, fmt); + if (_options && has_help_option(_options)) { + return print_block_option_help(_filename, _fmt); } - if (optind >= argc) { - help(); - } - optind++; - - /* Get image size, if specified */ - if (optind < argc) { + if (_size) { int64_t sval; char *end; - sval = strtosz_suffix(argv[optind++], &end, STRTOSZ_DEFSUFFIX_B); + sval = strtosz_suffix(_size, &end, STRTOSZ_DEFSUFFIX_B); if (sval < 0 || *end) { if (sval == -ERANGE) { error_report("Image size must be less than 8 EiB!"); @@ -420,23 +426,18 @@ static int img_create(int argc, char **argv) } img_size = (uint64_t)sval; } - if (optind != argc) { - help(); - } - bdrv_img_create(filename, fmt, base_filename, base_fmt, - options, img_size, BDRV_O_FLAGS, &local_err, quiet); + bdrv_img_create(_filename, _fmt, _backing_file, _backing_fmt, + _options, img_size, BDRV_O_FLAGS, &local_err, _quiet); if (local_err) { - error_report("%s: %s", filename, error_get_pretty(local_err)); + error_report("%s: %s", _filename, error_get_pretty(local_err)); error_free(local_err); goto fail; } - g_free(options); return 0; fail: - g_free(options); return 1; } @@ -547,81 +548,44 @@ static int collect_image_check(BlockDriverState *bs, * 2 - Check completed, image is corrupted * 3 - Check completed, image has leaked clusters, but is good otherwise */ -static int img_check(int argc, char **argv) +static int img_check(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c, ret; + int ret; OutputFormat output_format = OFORMAT_HUMAN; - const char *filename, *fmt, *output; BlockDriverState *bs; int fix = 0; int flags = BDRV_O_FLAGS | BDRV_O_CHECK; ImageCheck *check; - bool quiet = false; - fmt = NULL; - output = NULL; - for(;;) { - int option_index = 0; - static const struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"format", required_argument, 0, 'f'}, - {"repair", no_argument, 0, 'r'}, - {"output", required_argument, 0, OPTION_OUTPUT}, - {0, 0, 0, 0} - }; - c = getopt_long(argc, argv, "f:hr:q", - long_options, &option_index); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - break; - case 'f': - fmt = optarg; - break; - case 'r': - flags |= BDRV_O_RDWR; - - if (!strcmp(optarg, "leaks")) { - fix = BDRV_FIX_LEAKS; - } else if (!strcmp(optarg, "all")) { - fix = BDRV_FIX_LEAKS | BDRV_FIX_ERRORS; - } else { - help(); - } - break; - case OPTION_OUTPUT: - output = optarg; - break; - case 'q': - quiet = true; - break; + if (_rep) { + flags |= BDRV_O_RDWR; + if (!strcmp(_rep, "leaks")) { + fix = BDRV_FIX_LEAKS; + } else if (!strcmp(_rep, "all")) { + fix = BDRV_FIX_LEAKS | BDRV_FIX_ERRORS; + } else { + error_report("[-r | --repair] must be used with leak or all as " + "argument."); + return -1; } } - if (optind != argc - 1) { - help(); - } - filename = argv[optind++]; - if (output && !strcmp(output, "json")) { + if (!strcmp(_output, "json")) { output_format = OFORMAT_JSON; - } else if (output && !strcmp(output, "human")) { + } else if (!strcmp(_output, "human")) { output_format = OFORMAT_HUMAN; - } else if (output) { + } else if (_output) { error_report("--output must be used with human or json as argument."); - return 1; + return -1; } - bs = bdrv_new_open(filename, fmt, flags, true, quiet); + bs = bdrv_new_open(_filename, _fmt, flags, true, _quiet); if (!bs) { - return 1; + return -1; } check = g_new0(ImageCheck, 1); - ret = collect_image_check(bs, check, filename, fmt, fix); + ret = collect_image_check(bs, check, _filename, _fmt, fix); if (ret == -ENOTSUP) { if (output_format == OFORMAT_HUMAN) { @@ -638,7 +602,7 @@ static int img_check(int argc, char **argv) corruptions_fixed = check->corruptions_fixed; if (output_format == OFORMAT_HUMAN) { - qprintf(quiet, + qprintf(_quiet, "The following inconsistencies were found and repaired:\n\n" " %" PRId64 " leaked clusters\n" " %" PRId64 " corruptions\n\n" @@ -647,7 +611,7 @@ static int img_check(int argc, char **argv) check->corruptions_fixed); } - ret = collect_image_check(bs, check, filename, fmt, 0); + ret = collect_image_check(bs, check, _filename, _fmt, 0); check->leaks_fixed = leaks_fixed; check->corruptions_fixed = corruptions_fixed; @@ -655,10 +619,10 @@ static int img_check(int argc, char **argv) switch (output_format) { case OFORMAT_HUMAN: - dump_human_image_check(check, quiet); + dump_human_image_check(check, _quiet); break; case OFORMAT_JSON: - dump_json_image_check(check, quiet); + dump_json_image_check(check, _quiet); break; } @@ -682,56 +646,26 @@ fail: return ret; } -static int img_commit(int argc, char **argv) +static int img_commit(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c, ret, flags; - const char *filename, *fmt, *cache; + int ret, flags; BlockDriverState *bs; - bool quiet = false; - - fmt = NULL; - cache = BDRV_DEFAULT_CACHE; - for(;;) { - c = getopt(argc, argv, "f:ht:q"); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - break; - case 'f': - fmt = optarg; - break; - case 't': - cache = optarg; - break; - case 'q': - quiet = true; - break; - } - } - if (optind != argc - 1) { - help(); - } - filename = argv[optind++]; flags = BDRV_O_RDWR; - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_flags(_cache, &flags); if (ret < 0) { - error_report("Invalid cache option: %s", cache); + error_report("Invalid cache option: %s", _cache); return -1; } - bs = bdrv_new_open(filename, fmt, flags, true, quiet); + bs = bdrv_new_open(_filename, _fmt, flags, true, _quiet); if (!bs) { return 1; } ret = bdrv_commit(bs); switch(ret) { case 0: - qprintf(quiet, "Image committed.\n"); + qprintf(_quiet, "Image committed.\n"); break; case -ENOENT: error_report("No disk inserted"); @@ -907,76 +841,39 @@ static int check_empty_sectors(BlockDriverState *bs, int64_t sect_num, * 1 - Images differ * >1 - Error occurred */ -static int img_compare(int argc, char **argv) +static int img_compare(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - const char *fmt1 = NULL, *fmt2 = NULL, *filename1, *filename2; BlockDriverState *bs1, *bs2; int64_t total_sectors1, total_sectors2; uint8_t *buf1 = NULL, *buf2 = NULL; int pnum1, pnum2; int allocated1, allocated2; int ret = 0; /* return value - 0 Ident, 1 Different, >1 Error */ - bool progress = false, quiet = false, strict = false; int64_t total_sectors; int64_t sector_num = 0; int64_t nb_sectors; - int c, pnum; + int pnum; uint64_t bs_sectors; uint64_t progress_base; - for (;;) { - c = getopt(argc, argv, "hpf:F:sq"); - if (c == -1) { - break; - } - switch (c) { - case '?': - case 'h': - help(); - break; - case 'f': - fmt1 = optarg; - break; - case 'F': - fmt2 = optarg; - break; - case 'p': - progress = true; - break; - case 'q': - quiet = true; - break; - case 's': - strict = true; - break; - } - } - /* Progress is not shown in Quiet mode */ - if (quiet) { - progress = false; - } - - - if (optind != argc - 2) { - help(); + if (_quiet) { + _progress = false; } - filename1 = argv[optind++]; - filename2 = argv[optind++]; /* Initialize before goto out */ - qemu_progress_init(progress, 2.0); + qemu_progress_init(_progress, 2.0); - bs1 = bdrv_new_open(filename1, fmt1, BDRV_O_FLAGS, true, quiet); + bs1 = bdrv_new_open(_filename, _fmt, BDRV_O_FLAGS, true, _quiet); if (!bs1) { - error_report("Can't open file %s", filename1); + error_report("Can't open file %s", _filename); ret = 2; goto out3; } - bs2 = bdrv_new_open(filename2, fmt2, BDRV_O_FLAGS, true, quiet); + bs2 = bdrv_new_open(_filename2, _fmt2, BDRV_O_FLAGS, true, _quiet); if (!bs2) { - error_report("Can't open file %s", filename2); + error_report("Can't open file %s", _filename2); ret = 2; goto out2; } @@ -992,9 +889,9 @@ static int img_compare(int argc, char **argv) qemu_progress_print(0, 100); - if (strict && total_sectors1 != total_sectors2) { + if (_strict && total_sectors1 != total_sectors2) { ret = 1; - qprintf(quiet, "Strict mode: Image size mismatch!\n"); + qprintf(_quiet, "Strict mode: Image size mismatch!\n"); goto out; } @@ -1007,7 +904,7 @@ static int img_compare(int argc, char **argv) &pnum1); if (allocated1 < 0) { ret = 3; - error_report("Sector allocation test failed for %s", filename1); + error_report("Sector allocation test failed for %s", _filename); goto out; } @@ -1015,7 +912,7 @@ static int img_compare(int argc, char **argv) &pnum2); if (allocated2 < 0) { ret = 3; - error_report("Sector allocation test failed for %s", filename2); + error_report("Sector allocation test failed for %s", _filename2); goto out; } nb_sectors = MIN(pnum1, pnum2); @@ -1025,7 +922,7 @@ static int img_compare(int argc, char **argv) ret = bdrv_read(bs1, sector_num, buf1, nb_sectors); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s:" - " %s", sectors_to_bytes(sector_num), filename1, + " %s", sectors_to_bytes(sector_num), _filename, strerror(-ret)); ret = 4; goto out; @@ -1034,13 +931,13 @@ static int img_compare(int argc, char **argv) if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", sectors_to_bytes(sector_num), - filename2, strerror(-ret)); + _filename2, strerror(-ret)); ret = 4; goto out; } ret = compare_sectors(buf1, buf2, nb_sectors, &pnum); if (ret || pnum != nb_sectors) { - qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", + qprintf(_quiet, "Content mismatch at offset %" PRId64 "!\n", sectors_to_bytes( ret ? sector_num : sector_num + pnum)); ret = 1; @@ -1048,9 +945,9 @@ static int img_compare(int argc, char **argv) } } } else { - if (strict) { + if (_strict) { ret = 1; - qprintf(quiet, "Strict mode: Offset %" PRId64 + qprintf(_quiet, "Strict mode: Offset %" PRId64 " allocation mismatch!\n", sectors_to_bytes(sector_num)); goto out; @@ -1058,10 +955,10 @@ static int img_compare(int argc, char **argv) if (allocated1) { ret = check_empty_sectors(bs1, sector_num, nb_sectors, - filename1, buf1, quiet); + _filename, buf1, _quiet); } else { ret = check_empty_sectors(bs2, sector_num, nb_sectors, - filename2, buf1, quiet); + _filename2, buf1, _quiet); } if (ret) { if (ret < 0) { @@ -1081,15 +978,15 @@ static int img_compare(int argc, char **argv) int64_t total_sectors_over; const char *filename_over; - qprintf(quiet, "Warning: Image size mismatch!\n"); + qprintf(_quiet, "Warning: Image size mismatch!\n"); if (total_sectors1 > total_sectors2) { total_sectors_over = total_sectors1; bs_over = bs1; - filename_over = filename1; + filename_over = _filename; } else { total_sectors_over = total_sectors2; bs_over = bs2; - filename_over = filename2; + filename_over = _filename2; } for (;;) { @@ -1109,7 +1006,7 @@ static int img_compare(int argc, char **argv) nb_sectors = pnum; if (ret) { ret = check_empty_sectors(bs_over, sector_num, nb_sectors, - filename_over, buf1, quiet); + filename_over, buf1, _quiet); if (ret) { if (ret < 0) { error_report("Error while reading offset %" PRId64 @@ -1125,7 +1022,7 @@ static int img_compare(int argc, char **argv) } } - qprintf(quiet, "Images are identical.\n"); + qprintf(_quiet, "Images are identical.\n"); ret = 0; out: @@ -1139,12 +1036,13 @@ out3: return ret; } -static int img_convert(int argc, char **argv) +static int img_convert(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c, n, n1, bs_n, bs_i, compress, cluster_sectors, skip_create; + int n, n1, bs_n, bs_i, cluster_sectors; int64_t ret = 0; - int progress = 0, flags; - const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename; + int flags; + char **curr; + const char *out_filename; BlockDriver *drv, *proto_drv; BlockDriverState **bs = NULL, *out_bs = NULL; int64_t total_sectors, nb_sectors, sector_num, bs_offset; @@ -1155,131 +1053,57 @@ static int img_convert(int argc, char **argv) BlockDriverInfo bdi; QEMUOptionParameter *param = NULL, *create_options = NULL; QEMUOptionParameter *out_baseimg_param; - char *options = NULL; - const char *snapshot_name = NULL; int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */ - bool quiet = false; Error *local_err = NULL; QemuOpts *sn_opts = NULL; - fmt = NULL; - out_fmt = "raw"; - cache = "unsafe"; - out_baseimg = NULL; - compress = 0; - skip_create = 0; - for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:qnl:"); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - break; - case 'f': - fmt = optarg; - break; - case 'O': - out_fmt = optarg; - break; - case 'B': - out_baseimg = optarg; - break; - case 'c': - compress = 1; - break; - case 'e': - error_report("option -e is deprecated, please use \'-o " - "encryption\' instead!"); - ret = -1; - goto fail_getopt; - case '6': - error_report("option -6 is deprecated, please use \'-o " - "compat6\' instead!"); - ret = -1; - goto fail_getopt; - case 'o': - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); - ret = -1; - goto fail_getopt; - } - if (!options) { - options = g_strdup(optarg); - } else { - char *old_options = options; - options = g_strdup_printf("%s,%s", options, optarg); - g_free(old_options); - } - break; - case 's': - snapshot_name = optarg; - break; - case 'l': - if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { - sn_opts = qemu_opts_parse(&internal_snapshot_opts, optarg, 0); - if (!sn_opts) { - error_report("Failed in parsing snapshot param '%s'", - optarg); - ret = -1; - goto fail_getopt; - } - } else { - snapshot_name = optarg; - } - break; - case 'S': - { - int64_t sval; - char *end; - sval = strtosz_suffix(optarg, &end, STRTOSZ_DEFSUFFIX_B); - if (sval < 0 || *end) { - error_report("Invalid minimum zero buffer size for sparse output specified"); - ret = -1; - goto fail_getopt; - } + bs_n = qemu_arg_get_command_positional_list_cnt(cmd) - 1; - min_sparse = sval / BDRV_SECTOR_SIZE; - break; - } - case 'p': - progress = 1; - break; - case 't': - cache = optarg; - break; - case 'q': - quiet = true; - break; - case 'n': - skip_create = 1; - break; - } + if (bs_n < 1) { + error_report("At least an image and an output image file name must be" + " provided"); + return -1; } - /* Initialize before goto out */ - if (quiet) { - progress = 0; + out_filename = _filename_list[bs_n]; + if (_options && has_help_option(_options)) { + ret = print_block_option_help(out_filename, _ofmt); + goto out; } - qemu_progress_init(progress, 1.0); + if (_snapshot_name) { + if (strstart(_snapshot_name, SNAPSHOT_OPT_BASE, NULL)) { + sn_opts = qemu_opts_parse(&internal_snapshot_opts, + _snapshot_name, 0); + if (!sn_opts) { + error_report("Failed in parsing snapshot param '%s'", + optarg); + return -1; + } + } + } - bs_n = argc - optind - 1; - out_filename = bs_n >= 1 ? argv[argc - 1] : NULL; + if (_sparse_size) { + int64_t sval; + char *end; + sval = strtosz_suffix(_sparse_size, &end, STRTOSZ_DEFSUFFIX_B); + if (sval < 0 || *end) { + error_report("Invalid minimum zero buffer size for sparse output " + "specified"); + return -1; + } - if (options && has_help_option(options)) { - ret = print_block_option_help(out_filename, out_fmt); - goto out; + min_sparse = sval / BDRV_SECTOR_SIZE; } - if (bs_n < 1) { - help(); + if (_quiet) { + _progress = 0; } + /* Initialize before goto out */ + qemu_progress_init(_progress, 1.0); - if (bs_n > 1 && out_baseimg) { + if (bs_n > 1 && _out_baseimg) { error_report("-B makes no sense when concatenating multiple input " "images"); ret = -1; @@ -1291,11 +1115,11 @@ static int img_convert(int argc, char **argv) bs = g_malloc0(bs_n * sizeof(BlockDriverState *)); total_sectors = 0; - for (bs_i = 0; bs_i < bs_n; bs_i++) { - bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt, BDRV_O_FLAGS, true, - quiet); + for (bs_i = 0, curr = _filename_list; *curr && *curr != out_filename; + curr++, bs_i++) { + bs[bs_i] = bdrv_new_open(*curr, _fmt, BDRV_O_FLAGS, true, _quiet); if (!bs[bs_i]) { - error_report("Could not open '%s'", argv[optind + bs_i]); + error_report("Could not open '%s'", *curr); ret = -1; goto out; } @@ -1308,14 +1132,14 @@ static int img_convert(int argc, char **argv) qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), &local_err); - } else if (snapshot_name != NULL) { + } else if (_snapshot_name != NULL) { if (bs_n > 1) { error_report("No support for concatenating multiple snapshot"); ret = -1; goto out; } - bdrv_snapshot_load_tmp_by_id_or_name(bs[0], snapshot_name, &local_err); + bdrv_snapshot_load_tmp_by_id_or_name(bs[0], _snapshot_name, &local_err); } if (local_err) { error_report("Failed to load snapshot: %s", @@ -1326,9 +1150,9 @@ static int img_convert(int argc, char **argv) } /* Find driver and parse its options */ - drv = bdrv_find_format(out_fmt); + drv = bdrv_find_format(_ofmt); if (!drv) { - error_report("Unknown file format '%s'", out_fmt); + error_report("Unknown file format '%s'", _ofmt); ret = -1; goto out; } @@ -1345,10 +1169,10 @@ static int img_convert(int argc, char **argv) create_options = append_option_parameters(create_options, proto_drv->create_options); - if (options) { - param = parse_option_parameters(options, create_options, param); + if (_options) { + param = parse_option_parameters(_options, create_options, param); if (param == NULL) { - error_report("Invalid options for file format '%s'.", out_fmt); + error_report("Invalid options for file format '%s'.", _ofmt); ret = -1; goto out; } @@ -1357,7 +1181,7 @@ static int img_convert(int argc, char **argv) } set_option_parameter_int(param, BLOCK_OPT_SIZE, total_sectors * 512); - ret = add_old_style_options(out_fmt, param, out_baseimg, NULL); + ret = add_old_style_options(_ofmt, param, _out_baseimg, NULL); if (ret < 0) { goto out; } @@ -1365,11 +1189,11 @@ static int img_convert(int argc, char **argv) /* Get backing file name if -o backing_file was used */ out_baseimg_param = get_option_parameter(param, BLOCK_OPT_BACKING_FILE); if (out_baseimg_param) { - out_baseimg = out_baseimg_param->value.s; + _out_baseimg = out_baseimg_param->value.s; } /* Check if compression is supported */ - if (compress) { + if (_compress) { QEMUOptionParameter *encryption = get_option_parameter(param, BLOCK_OPT_ENCRYPT); QEMUOptionParameter *preallocation = @@ -1398,25 +1222,25 @@ static int img_convert(int argc, char **argv) } } - if (!skip_create) { + if (!_skip_create) { /* Create the new image */ ret = bdrv_create(drv, out_filename, param, &local_err); if (ret < 0) { error_report("%s: error while converting %s: %s", - out_filename, out_fmt, error_get_pretty(local_err)); + out_filename, _ofmt, error_get_pretty(local_err)); error_free(local_err); goto out; } } flags = min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_flags(_cache, &flags); if (ret < 0) { - error_report("Invalid cache option: %s", cache); + error_report("Invalid cache option: %s", _cache); return -1; } - out_bs = bdrv_new_open(out_filename, out_fmt, flags, true, quiet); + out_bs = bdrv_new_open(out_filename, _ofmt, flags, true, _quiet); if (!out_bs) { ret = -1; goto out; @@ -1436,7 +1260,7 @@ static int img_convert(int argc, char **argv) buf = qemu_blockalign(out_bs, bufsectors * BDRV_SECTOR_SIZE); - if (skip_create) { + if (_skip_create) { int64_t output_length = bdrv_getlength(out_bs); if (output_length < 0) { error_report("unable to get output image length: %s\n", @@ -1453,7 +1277,7 @@ static int img_convert(int argc, char **argv) cluster_sectors = 0; ret = bdrv_get_info(out_bs, &bdi); if (ret < 0) { - if (compress) { + if (_compress) { error_report("could not get block driver info"); goto out; } @@ -1461,7 +1285,7 @@ static int img_convert(int argc, char **argv) cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; } - if (compress) { + if (_compress) { if (cluster_sectors <= 0 || cluster_sectors > bufsectors) { error_report("invalid cluster size"); ret = -1; @@ -1545,7 +1369,7 @@ static int img_convert(int argc, char **argv) } sectors_to_read = total_sectors; - count_allocated_sectors = progress && (out_baseimg || has_zero_init); + count_allocated_sectors = _progress && (_out_baseimg || has_zero_init); restart: sector_num = 0; // total number of sectors converted so far sectors_read = 0; @@ -1573,7 +1397,7 @@ restart: sector_num, bs_i, bs_offset, bs_sectors); */ } - if ((out_baseimg || has_zero_init) && + if ((_out_baseimg || has_zero_init) && sector_num >= sector_num_next_status) { n = nb_sectors > INT_MAX ? INT_MAX : nb_sectors; ret = bdrv_get_block_status(bs[bs_i], sector_num - bs_offset, @@ -1587,7 +1411,7 @@ restart: /* If the output image is zero initialized, we are not working * on a shared base and the input is zero we can skip the next * n1 sectors */ - if (has_zero_init && !out_baseimg && (ret & BDRV_BLOCK_ZERO)) { + if (has_zero_init && !_out_baseimg && (ret & BDRV_BLOCK_ZERO)) { sector_num += n1; continue; } @@ -1595,7 +1419,7 @@ restart: * image, assume that sectors which are unallocated in the * input image are present in both the output's and input's * base images (no need to copy them). */ - if (out_baseimg) { + if (_out_baseimg) { if (!(ret & BDRV_BLOCK_DATA)) { sector_num += n1; continue; @@ -1681,8 +1505,6 @@ out: } g_free(bs); } -fail_getopt: - g_free(options); if (ret) { return 1; @@ -1840,61 +1662,21 @@ err: return NULL; } -static int img_info(int argc, char **argv) +static int img_info(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c; OutputFormat output_format = OFORMAT_HUMAN; - bool chain = false; - const char *filename, *fmt, *output; ImageInfoList *list; - fmt = NULL; - output = NULL; - for(;;) { - int option_index = 0; - static const struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"format", required_argument, 0, 'f'}, - {"output", required_argument, 0, OPTION_OUTPUT}, - {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, - {0, 0, 0, 0} - }; - c = getopt_long(argc, argv, "f:h", - long_options, &option_index); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - break; - case 'f': - fmt = optarg; - break; - case OPTION_OUTPUT: - output = optarg; - break; - case OPTION_BACKING_CHAIN: - chain = true; - break; - } - } - if (optind != argc - 1) { - help(); - } - filename = argv[optind++]; - - if (output && !strcmp(output, "json")) { + if (_ofmt && !strcmp(_ofmt, "json")) { output_format = OFORMAT_JSON; - } else if (output && !strcmp(output, "human")) { + } else if (_ofmt && !strcmp(_ofmt, "human")) { output_format = OFORMAT_HUMAN; - } else if (output) { + } else if (_ofmt) { error_report("--output must be used with human or json as argument."); return 1; } - list = collect_image_info_list(filename, fmt, chain); + list = collect_image_info_list(_filename, _fmt, _backing_chain); if (!list) { return 1; } @@ -1904,7 +1686,7 @@ static int img_info(int argc, char **argv) dump_human_image_info_list(list); break; case OFORMAT_JSON: - if (chain) { + if (_backing_chain) { dump_json_image_info_list(list); } else { dump_json_image_info(list->value); @@ -1916,7 +1698,6 @@ static int img_info(int argc, char **argv) return 0; } - typedef struct MapEntry { int flags; int depth; @@ -2007,59 +1788,24 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, return 0; } -static int img_map(int argc, char **argv) +static int img_map(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c; OutputFormat output_format = OFORMAT_HUMAN; BlockDriverState *bs; - const char *filename, *fmt, *output; int64_t length; MapEntry curr = { .length = 0 }, next; int ret = 0; - fmt = NULL; - output = NULL; - for (;;) { - int option_index = 0; - static const struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"format", required_argument, 0, 'f'}, - {"output", required_argument, 0, OPTION_OUTPUT}, - {0, 0, 0, 0} - }; - c = getopt_long(argc, argv, "f:h", - long_options, &option_index); - if (c == -1) { - break; - } - switch (c) { - case '?': - case 'h': - help(); - break; - case 'f': - fmt = optarg; - break; - case OPTION_OUTPUT: - output = optarg; - break; - } - } - if (optind >= argc) { - help(); - } - filename = argv[optind++]; - - if (output && !strcmp(output, "json")) { + if (_ofmt && !strcmp(_ofmt, "json")) { output_format = OFORMAT_JSON; - } else if (output && !strcmp(output, "human")) { + } else if (_ofmt && !strcmp(_ofmt, "human")) { output_format = OFORMAT_HUMAN; - } else if (output) { + } else if (_ofmt) { error_report("--output must be used with human or json as argument."); return 1; } - bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS, true, false); + bs = bdrv_new_open(_filename, _fmt, BDRV_O_FLAGS, true, false); if (!bs) { return 1; } @@ -2112,74 +1858,34 @@ out: #define SNAPSHOT_APPLY 3 #define SNAPSHOT_DELETE 4 -static int img_snapshot(int argc, char **argv) +static int img_snapshot(const QemuArgContext *ctx, const QemuArgCommand *cmd) { BlockDriverState *bs; QEMUSnapshotInfo sn; - char *filename, *snapshot_name = NULL; - int c, ret = 0, bdrv_oflags; + char *snapshot_name = NULL; + int ret = 0, bdrv_oflags; int action = 0; qemu_timeval tv; - bool quiet = false; Error *err = NULL; bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; - /* Parse commandline parameters */ - for(;;) { - c = getopt(argc, argv, "la:c:d:hq"); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - return 0; - case 'l': - if (action) { - help(); - return 0; - } - action = SNAPSHOT_LIST; - bdrv_oflags &= ~BDRV_O_RDWR; /* no need for RW */ - break; - case 'a': - if (action) { - help(); - return 0; - } - action = SNAPSHOT_APPLY; - snapshot_name = optarg; - break; - case 'c': - if (action) { - help(); - return 0; - } - action = SNAPSHOT_CREATE; - snapshot_name = optarg; - break; - case 'd': - if (action) { - help(); - return 0; - } - action = SNAPSHOT_DELETE; - snapshot_name = optarg; - break; - case 'q': - quiet = true; - break; - } - } - if (optind != argc - 1) { - help(); + if (_snapshot_list) { + action = SNAPSHOT_LIST; + bdrv_oflags &= ~BDRV_O_RDWR; /* no need for RW */ + } else if (_snapshot_apply) { + action = SNAPSHOT_APPLY; + snapshot_name = _snapshot_apply; + } else if (_snapshot_create) { + action = SNAPSHOT_CREATE; + snapshot_name = _snapshot_create; + } else if (_snapshot_delete) { + action = SNAPSHOT_DELETE; + snapshot_name = _snapshot_delete; } - filename = argv[optind++]; /* Open the image */ - bs = bdrv_new_open(filename, NULL, bdrv_oflags, true, quiet); + bs = bdrv_new_open(_filename, NULL, bdrv_oflags, true, _quiet); if (!bs) { return 1; } @@ -2232,73 +1938,29 @@ static int img_snapshot(int argc, char **argv) return 0; } -static int img_rebase(int argc, char **argv) +static int img_rebase(const QemuArgContext *ctx, const QemuArgCommand *cmd) { BlockDriverState *bs, *bs_old_backing = NULL, *bs_new_backing = NULL; BlockDriver *old_backing_drv, *new_backing_drv; - char *filename; - const char *fmt, *cache, *out_basefmt, *out_baseimg; - int c, flags, ret; - int unsafe = 0; - int progress = 0; - bool quiet = false; + int flags, ret; Error *local_err = NULL; - /* Parse commandline parameters */ - fmt = NULL; - cache = BDRV_DEFAULT_CACHE; - out_baseimg = NULL; - out_basefmt = NULL; - for(;;) { - c = getopt(argc, argv, "uhf:F:b:pt:q"); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - return 0; - case 'f': - fmt = optarg; - break; - case 'F': - out_basefmt = optarg; - break; - case 'b': - out_baseimg = optarg; - break; - case 'u': - unsafe = 1; - break; - case 'p': - progress = 1; - break; - case 't': - cache = optarg; - break; - case 'q': - quiet = true; - break; - } - } - - if (quiet) { - progress = 0; + if (_quiet) { + _progress = 0; } - if ((optind != argc - 1) || (!unsafe && !out_baseimg)) { - help(); + if (!_unsafe && !_out_baseimg) { + qemu_arg_print_command_help(ctx, cmd); + return -1; } - filename = argv[optind++]; - qemu_progress_init(progress, 2.0); + qemu_progress_init(_progress, 2.0); qemu_progress_print(0, 100); - flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); - ret = bdrv_parse_cache_flags(cache, &flags); + flags = BDRV_O_RDWR | (_unsafe ? BDRV_O_NO_BACKING : 0); + ret = bdrv_parse_cache_flags(_cache, &flags); if (ret < 0) { - error_report("Invalid cache option: %s", cache); + error_report("Invalid cache option: %s", _cache); return -1; } @@ -2308,7 +1970,7 @@ static int img_rebase(int argc, char **argv) * Ignore the old backing file for unsafe rebase in case we want to correct * the reference to a renamed or moved backing file. */ - bs = bdrv_new_open(filename, fmt, flags, true, quiet); + bs = bdrv_new_open(_filename, _fmt, flags, true, _quiet); if (!bs) { return 1; } @@ -2317,7 +1979,7 @@ static int img_rebase(int argc, char **argv) old_backing_drv = NULL; new_backing_drv = NULL; - if (!unsafe && bs->backing_format[0] != '\0') { + if (!_unsafe && bs->backing_format[0] != '\0') { old_backing_drv = bdrv_find_format(bs->backing_format); if (old_backing_drv == NULL) { error_report("Invalid format name: '%s'", bs->backing_format); @@ -2326,17 +1988,17 @@ static int img_rebase(int argc, char **argv) } } - if (out_basefmt != NULL) { - new_backing_drv = bdrv_find_format(out_basefmt); + if (_out_basefmt != NULL) { + new_backing_drv = bdrv_find_format(_out_basefmt); if (new_backing_drv == NULL) { - error_report("Invalid format name: '%s'", out_basefmt); + error_report("Invalid format name: '%s'", _out_basefmt); ret = -1; goto out; } } /* For safe rebasing we need to compare old and new backing file */ - if (unsafe) { + if (_unsafe) { /* Make the compiler happy */ bs_old_backing = NULL; bs_new_backing = NULL; @@ -2353,13 +2015,13 @@ static int img_rebase(int argc, char **argv) error_free(local_err); goto out; } - if (out_baseimg[0]) { + if (_out_baseimg[0]) { bs_new_backing = bdrv_new("new_backing"); - ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL, + ret = bdrv_open(&bs_new_backing, _out_baseimg, NULL, NULL, BDRV_O_FLAGS, new_backing_drv, &local_err); if (ret) { error_report("Could not open new backing file '%s': %s", - out_baseimg, error_get_pretty(local_err)); + _out_baseimg, error_get_pretty(local_err)); error_free(local_err); goto out; } @@ -2375,7 +2037,7 @@ static int img_rebase(int argc, char **argv) * If qemu-img crashes during this step, no harm is done. The content of * the image is the same as the original one at any time. */ - if (!unsafe) { + if (!_unsafe) { uint64_t num_sectors; uint64_t old_backing_num_sectors; uint64_t new_backing_num_sectors = 0; @@ -2483,18 +2145,18 @@ static int img_rebase(int argc, char **argv) * backing file are overwritten in the COW file now, so the visible content * doesn't change when we switch the backing file. */ - if (out_baseimg && *out_baseimg) { - ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt); + if (_out_baseimg && *_out_baseimg) { + ret = bdrv_change_backing_file(bs, _out_baseimg, _out_basefmt); } else { ret = bdrv_change_backing_file(bs, NULL, NULL); } if (ret == -ENOSPC) { error_report("Could not change the backing file to '%s': No " - "space left in the file header", out_baseimg); + "space left in the file header", _out_baseimg); } else if (ret < 0) { error_report("Could not change the backing file to '%s': %s", - out_baseimg, strerror(-ret)); + _out_baseimg, strerror(-ret)); } qemu_progress_print(100, 0); @@ -2507,7 +2169,7 @@ static int img_rebase(int argc, char **argv) out: qemu_progress_end(); /* Cleanup */ - if (!unsafe) { + if (!_unsafe) { if (bs_old_backing != NULL) { bdrv_unref(bs_old_backing); } @@ -2523,10 +2185,9 @@ out: return 0; } -static int img_resize(int argc, char **argv) +static int img_resize(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c, ret, relative; - const char *filename, *fmt, *size; + int ret, relative; int64_t n, total_size; bool quiet = false; BlockDriverState *bs = NULL; @@ -2545,49 +2206,15 @@ static int img_resize(int argc, char **argv) }, }; - /* Remove size from argv manually so that negative numbers are not treated - * as options by getopt. */ - if (argc < 3) { - help(); - return 1; - } - - size = argv[--argc]; - - /* Parse getopt arguments */ - fmt = NULL; - for(;;) { - c = getopt(argc, argv, "f:hq"); - if (c == -1) { - break; - } - switch(c) { - case '?': - case 'h': - help(); - break; - case 'f': - fmt = optarg; - break; - case 'q': - quiet = true; - break; - } - } - if (optind != argc - 1) { - help(); - } - filename = argv[optind++]; - /* Choose grow, shrink, or absolute resize mode */ - switch (size[0]) { + switch (_size[0]) { case '+': relative = 1; - size++; + _size++; break; case '-': relative = -1; - size++; + _size++; break; default: relative = 0; @@ -2596,7 +2223,7 @@ static int img_resize(int argc, char **argv) /* Parse size */ param = qemu_opts_create(&resize_options, NULL, 0, &error_abort); - if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) { + if (qemu_opt_set(param, BLOCK_OPT_SIZE, _size)) { /* Error message already printed when size parsing fails */ ret = -1; qemu_opts_del(param); @@ -2605,7 +2232,8 @@ static int img_resize(int argc, char **argv) n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); qemu_opts_del(param); - bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet); + bs = bdrv_new_open(_filename, _fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, + _quiet); if (!bs) { ret = -1; goto out; @@ -2647,86 +2275,41 @@ out: return 0; } -static int img_amend(int argc, char **argv) +static int img_amend(const QemuArgContext *ctx, const QemuArgCommand *cmd) { - int c, ret = 0; - char *options = NULL; + int ret = 0; QEMUOptionParameter *create_options = NULL, *options_param = NULL; - const char *fmt = NULL, *filename; - bool quiet = false; BlockDriverState *bs = NULL; - for (;;) { - c = getopt(argc, argv, "hqf:o:"); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - case '?': - help(); - break; - case 'o': - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); - ret = -1; - goto out; - } - if (!options) { - options = g_strdup(optarg); - } else { - char *old_options = options; - options = g_strdup_printf("%s,%s", options, optarg); - g_free(old_options); - } - break; - case 'f': - fmt = optarg; - break; - case 'q': - quiet = true; - break; - } - } - - if (!options) { - help(); - } - - filename = (optind == argc - 1) ? argv[argc - 1] : NULL; - if (fmt && has_help_option(options)) { + if (_fmt && has_help_option(_options)) { /* If a format is explicitly specified (and possibly no filename is * given), print option help here */ - ret = print_block_option_help(filename, fmt); + ret = print_block_option_help(_filename, _fmt); goto out; } - if (optind != argc - 1) { - help(); - } - - bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet); + bs = bdrv_new_open(_filename, _fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, + _quiet); if (!bs) { - error_report("Could not open image '%s'", filename); + error_report("Could not open image '%s'", _filename); ret = -1; goto out; } - fmt = bs->drv->format_name; + _fmt = (char *)bs->drv->format_name; - if (has_help_option(options)) { + if (has_help_option(_options)) { /* If the format was auto-detected, print option help here */ - ret = print_block_option_help(filename, fmt); + ret = print_block_option_help(_filename, _fmt); goto out; } create_options = append_option_parameters(create_options, bs->drv->create_options); - options_param = parse_option_parameters(options, create_options, + options_param = parse_option_parameters(_options, create_options, options_param); if (options_param == NULL) { - error_report("Invalid options for file format '%s'", fmt); + error_report("Invalid options for file format '%s'", _fmt); ret = -1; goto out; } @@ -2743,7 +2326,6 @@ out: } free_option_parameters(create_options); free_option_parameters(options_param); - g_free(options); if (ret) { return 1; @@ -2751,19 +2333,46 @@ out: return 0; } -static const img_cmd_t img_cmds[] = { -#define DEF(option, callback, arg_string) \ - { option, callback }, -#include "qemu-img-cmds.h" -#undef DEF -#undef GEN_DOCS - { NULL, NULL, }, +static const QemuArgCommand _cmds[] = { + OPT_CMD("check", QEMU_IMG_CHECK_HELP, _check_args, NULL, img_check), + OPT_CMD("create", QEMU_IMG_CREATE_HELP, _create_args, NULL, img_create), + OPT_CMD("commit", QEMU_IMG_COMMIT_HELP, _commit_args, NULL, img_commit), + OPT_CMD("compare", QEMU_IMG_COMPARE_HELP, _compare_args, NULL, img_compare), + OPT_CMD("convert", QEMU_IMG_CONVERT_HELP, _convert_args, NULL, img_convert), + OPT_CMD("info", QEMU_IMG_INFO_HELP, _info_args, NULL, img_info), + OPT_CMD("map", QEMU_IMG_MAP_HELP, _map_args, NULL, img_map), + OPT_CMD("snapshot", QEMU_IMG_SNAPSHOT_HELP, _snapshot_args, + _snapshot_mutual_groups, img_snapshot), + OPT_CMD("rebase", QEMU_IMG_REBASE_HELP, _rebase_args, NULL, img_rebase), + OPT_CMD("resize", QEMU_IMG_RESIZE_HELP, _resize_args, NULL, img_resize), + OPT_CMD("amend", QEMU_IMG_AMEND_HELP, _amend_args, NULL, img_amend), + CMD_SENTINEL }; +static void format_print(void *opaque, const char *name) +{ + char **str = opaque; + char *cp; + + if (*str) { + cp = g_strdup(*str); + free(*str); + + *str = g_malloc0(strlen(cp) + strlen(name) + 1); + sprintf(*str, "%s %s", cp, name); + + free(cp); + } else { + *str = g_malloc0(strlen(name) + 1); + sprintf(*str, "%s", name); + } +} + int main(int argc, char **argv) { - const img_cmd_t *cmd; - const char *cmdname; + int ret; + char *formats = NULL, *epilogue = NULL; + const char *pref = "Supported formats: "; #ifdef CONFIG_POSIX signal(SIGPIPE, SIG_IGN); @@ -2774,19 +2383,20 @@ int main(int argc, char **argv) qemu_init_main_loop(); bdrv_init(); - if (argc < 2) - help(); - cmdname = argv[1]; - argc--; argv++; - /* find the command */ - for(cmd = img_cmds; cmd->name != NULL; cmd++) { - if (!strcmp(cmdname, cmd->name)) { - return cmd->handler(argc, argv); - } - } + bdrv_iterate_format(format_print, &formats); - /* not found */ - help(); - return 0; + epilogue = g_malloc0(strlen(formats) + strlen(pref)); + sprintf(epilogue, "%s%s", pref, formats); + + OPT_CTX(ctx, PROLOGUE, epilogue, "qemu-img", _cmds, NULL, NULL, + OPT_DECORATE_LONG); + + ret = qemu_arg_parse(argc, argv, &ctx); + qemu_arg_context_cleanup(&ctx); + + free(formats); + free(epilogue); + + return ret; } -- 1.9.0