2 * QEMU disk image utility
4 * Copyright (c) 2003-2008 Fabrice Bellard
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 #include "qemu-common.h"
25 #include "qemu-option.h"
27 #include "block_int.h"
34 typedef struct img_cmd_t
{
36 int (*handler
)(int argc
, char **argv
);
39 /* Default to cache=writeback as data integrity is not important for qemu-tcg. */
40 #define BRDV_O_FLAGS BDRV_O_CACHE_WB
42 static void QEMU_NORETURN
error(const char *fmt
, ...)
46 fprintf(stderr
, "qemu-img: ");
47 vfprintf(stderr
, fmt
, ap
);
48 fprintf(stderr
, "\n");
53 static void format_print(void *opaque
, const char *name
)
58 /* Please keep in synch with qemu-img.texi */
59 static void help(void)
61 printf("qemu-img version " QEMU_VERSION
", Copyright (c) 2004-2008 Fabrice Bellard\n"
62 "usage: qemu-img command [command options]\n"
63 "QEMU disk image utility\n"
66 #define DEF(option, callback, arg_string) \
68 #include "qemu-img-cmds.h"
72 "Command parameters:\n"
73 " 'filename' is a disk image filename\n"
74 " 'base_image' is the read-only disk image which is used as base for a copy on\n"
75 " write image; the copy on write image only stores the modified data\n"
76 " 'output_base_image' forces the output image to be created as a copy on write\n"
77 " image of the specified base image; 'output_base_image' should have the same\n"
78 " content as the input's base image, however the path, image format, etc may\n"
80 " 'fmt' is the disk image format. It is guessed automatically in most cases\n"
81 " 'size' is the disk image size in kilobytes. Optional suffixes\n"
82 " 'M' (megabyte, 1024 * 1024) and 'G' (gigabyte, 1024 * 1024 * 1024) are\n"
83 " supported any 'k' or 'K' is ignored\n"
84 " 'output_filename' is the destination disk image filename\n"
85 " 'output_fmt' is the destination format\n"
86 " 'options' is a comma separated list of format specific options in a\n"
87 " name=value format. Use -o ? for an overview of the options supported by the\n"
89 " '-c' indicates that target image must be compressed (qcow format only)\n"
90 " '-h' with or without a command shows this help and lists the supported formats\n"
92 "Parameters to snapshot subcommand:\n"
93 " 'snapshot' is the name of the snapshot to create, apply or delete\n"
94 " '-a' applies a snapshot (revert disk to saved state)\n"
95 " '-c' creates a snapshot\n"
96 " '-d' deletes a snapshot\n"
97 " '-l' lists all snapshots in the given image\n"
99 printf("\nSupported formats:");
100 bdrv_iterate_format(format_print
, NULL
);
106 /* XXX: put correct support for win32 */
107 static int read_password(char *buf
, int buf_size
)
110 printf("Password: ");
117 if (i
< (buf_size
- 1))
128 static struct termios oldtty
;
130 static void term_exit(void)
132 tcsetattr (0, TCSANOW
, &oldtty
);
135 static void term_init(void)
142 tty
.c_iflag
&= ~(IGNBRK
|BRKINT
|PARMRK
|ISTRIP
143 |INLCR
|IGNCR
|ICRNL
|IXON
);
144 tty
.c_oflag
|= OPOST
;
145 tty
.c_lflag
&= ~(ECHO
|ECHONL
|ICANON
|IEXTEN
);
146 tty
.c_cflag
&= ~(CSIZE
|PARENB
);
151 tcsetattr (0, TCSANOW
, &tty
);
156 static int read_password(char *buf
, int buf_size
)
161 printf("password: ");
166 ret
= read(0, &ch
, 1);
168 if (errno
== EAGAIN
|| errno
== EINTR
) {
174 } else if (ret
== 0) {
182 if (i
< (buf_size
- 1))
193 static BlockDriverState
*bdrv_new_open(const char *filename
,
196 BlockDriverState
*bs
;
202 error("Not enough memory");
204 drv
= bdrv_find_format(fmt
);
206 error("Unknown file format '%s'", fmt
);
210 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
211 error("Could not open '%s'", filename
);
213 if (bdrv_is_encrypted(bs
)) {
214 printf("Disk image '%s' is encrypted.\n", filename
);
215 if (read_password(password
, sizeof(password
)) < 0)
216 error("No password given");
217 if (bdrv_set_key(bs
, password
) < 0)
218 error("invalid password");
223 static void add_old_style_options(const char *fmt
, QEMUOptionParameter
*list
,
224 int flags
, const char *base_filename
, const char *base_fmt
)
226 if (flags
& BLOCK_FLAG_ENCRYPT
) {
227 if (set_option_parameter(list
, BLOCK_OPT_ENCRYPT
, "on")) {
228 error("Encryption not supported for file format '%s'", fmt
);
231 if (flags
& BLOCK_FLAG_COMPAT6
) {
232 if (set_option_parameter(list
, BLOCK_OPT_COMPAT6
, "on")) {
233 error("VMDK version 6 not supported for file format '%s'", fmt
);
238 if (set_option_parameter(list
, BLOCK_OPT_BACKING_FILE
, base_filename
)) {
239 error("Backing file not supported for file format '%s'", fmt
);
243 if (set_option_parameter(list
, BLOCK_OPT_BACKING_FMT
, base_fmt
)) {
244 error("Backing file format not supported for file format '%s'", fmt
);
249 static int img_create(int argc
, char **argv
)
252 const char *fmt
= "raw";
253 const char *base_fmt
= NULL
;
254 const char *filename
;
255 const char *base_filename
= NULL
;
257 QEMUOptionParameter
*param
= NULL
;
258 char *options
= NULL
;
262 c
= getopt(argc
, argv
, "F:b:f:he6o:");
273 base_filename
= optarg
;
279 flags
|= BLOCK_FLAG_ENCRYPT
;
282 flags
|= BLOCK_FLAG_COMPAT6
;
290 /* Find driver and parse its options */
291 drv
= bdrv_find_format(fmt
);
293 error("Unknown file format '%s'", fmt
);
295 if (options
&& !strcmp(options
, "?")) {
296 print_option_help(drv
->create_options
);
301 param
= parse_option_parameters(options
, drv
->create_options
, param
);
303 error("Invalid options for file format '%s'.", fmt
);
306 param
= parse_option_parameters("", drv
->create_options
, param
);
309 /* Get the filename */
312 filename
= argv
[optind
++];
314 /* Add size to parameters */
316 set_option_parameter(param
, BLOCK_OPT_SIZE
, argv
[optind
++]);
319 /* Add old-style options to parameters */
320 add_old_style_options(fmt
, param
, flags
, base_filename
, base_fmt
);
322 // The size for the image must always be specified, with one exception:
323 // If we are using a backing file, we can obtain the size from there
324 if (get_option_parameter(param
, BLOCK_OPT_SIZE
)->value
.n
== 0) {
326 QEMUOptionParameter
*backing_file
=
327 get_option_parameter(param
, BLOCK_OPT_BACKING_FILE
);
328 QEMUOptionParameter
*backing_fmt
=
329 get_option_parameter(param
, BLOCK_OPT_BACKING_FMT
);
331 if (backing_file
&& backing_file
->value
.s
) {
332 BlockDriverState
*bs
;
334 const char *fmt
= NULL
;
337 if (backing_fmt
&& backing_fmt
->value
.s
) {
338 if (bdrv_find_format(backing_fmt
->value
.s
)) {
339 fmt
= backing_fmt
->value
.s
;
341 error("Unknown backing file format '%s'",
342 backing_fmt
->value
.s
);
346 bs
= bdrv_new_open(backing_file
->value
.s
, fmt
);
347 bdrv_get_geometry(bs
, &size
);
351 snprintf(buf
, sizeof(buf
), "%" PRId64
, size
);
352 set_option_parameter(param
, BLOCK_OPT_SIZE
, buf
);
354 error("Image creation needs a size parameter");
358 printf("Formatting '%s', fmt=%s ", filename
, fmt
);
359 print_option_parameters(param
);
362 ret
= bdrv_create(drv
, filename
, param
);
363 free_option_parameters(param
);
366 if (ret
== -ENOTSUP
) {
367 error("Formatting or formatting option not supported for file format '%s'", fmt
);
368 } else if (ret
== -EFBIG
) {
369 error("The image size is too large for file format '%s'", fmt
);
371 error("Error while formatting");
377 static int img_check(int argc
, char **argv
)
380 const char *filename
, *fmt
;
382 BlockDriverState
*bs
;
386 c
= getopt(argc
, argv
, "f:h");
400 filename
= argv
[optind
++];
404 error("Not enough memory");
406 drv
= bdrv_find_format(fmt
);
408 error("Unknown file format '%s'", fmt
);
412 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
413 error("Could not open '%s'", filename
);
415 ret
= bdrv_check(bs
);
418 printf("No errors were found on the image.\n");
421 error("This image format does not support checks");
425 error("An error occurred during the check");
427 printf("%d errors were found on the image.\n", ret
);
436 static int img_commit(int argc
, char **argv
)
439 const char *filename
, *fmt
;
441 BlockDriverState
*bs
;
445 c
= getopt(argc
, argv
, "f:h");
459 filename
= argv
[optind
++];
463 error("Not enough memory");
465 drv
= bdrv_find_format(fmt
);
467 error("Unknown file format '%s'", fmt
);
471 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
472 error("Could not open '%s'", filename
);
474 ret
= bdrv_commit(bs
);
477 printf("Image committed.\n");
480 error("No disk inserted");
483 error("Image is read-only");
486 error("Image is already committed");
489 error("Error while committing image");
497 static int is_not_zero(const uint8_t *sector
, int len
)
501 for(i
= 0;i
< len
; i
++) {
502 if (((uint32_t *)sector
)[i
] != 0)
509 * Returns true iff the first sector pointed to by 'buf' contains at least
512 * 'pnum' is set to the number of sectors (including and immediately following
513 * the first one) that are known to be in the same allocated/unallocated state.
515 static int is_allocated_sectors(const uint8_t *buf
, int n
, int *pnum
)
523 v
= is_not_zero(buf
, 512);
524 for(i
= 1; i
< n
; i
++) {
526 if (v
!= is_not_zero(buf
, 512))
533 #define IO_BUF_SIZE 65536
535 static int img_convert(int argc
, char **argv
)
537 int c
, ret
, n
, n1
, bs_n
, bs_i
, flags
, cluster_size
, cluster_sectors
;
538 const char *fmt
, *out_fmt
, *out_baseimg
, *out_filename
;
540 BlockDriverState
**bs
, *out_bs
;
541 int64_t total_sectors
, nb_sectors
, sector_num
, bs_offset
;
543 uint8_t buf
[IO_BUF_SIZE
];
546 QEMUOptionParameter
*param
= NULL
;
547 char *options
= NULL
;
554 c
= getopt(argc
, argv
, "f:O:B:hce6o:");
568 out_baseimg
= optarg
;
571 flags
|= BLOCK_FLAG_COMPRESS
;
574 flags
|= BLOCK_FLAG_ENCRYPT
;
577 flags
|= BLOCK_FLAG_COMPAT6
;
585 bs_n
= argc
- optind
- 1;
586 if (bs_n
< 1) help();
588 out_filename
= argv
[argc
- 1];
590 if (bs_n
> 1 && out_baseimg
)
591 error("-B makes no sense when concatenating multiple input images");
593 bs
= calloc(bs_n
, sizeof(BlockDriverState
*));
595 error("Out of memory");
598 for (bs_i
= 0; bs_i
< bs_n
; bs_i
++) {
599 bs
[bs_i
] = bdrv_new_open(argv
[optind
+ bs_i
], fmt
);
601 error("Could not open '%s'", argv
[optind
+ bs_i
]);
602 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
603 total_sectors
+= bs_sectors
;
606 /* Find driver and parse its options */
607 drv
= bdrv_find_format(out_fmt
);
609 error("Unknown file format '%s'", out_fmt
);
611 if (options
&& !strcmp(options
, "?")) {
612 print_option_help(drv
->create_options
);
617 param
= parse_option_parameters(options
, drv
->create_options
, param
);
619 error("Invalid options for file format '%s'.", out_fmt
);
622 param
= parse_option_parameters("", drv
->create_options
, param
);
625 set_option_parameter_int(param
, BLOCK_OPT_SIZE
, total_sectors
* 512);
626 add_old_style_options(out_fmt
, param
, flags
, out_baseimg
, NULL
);
628 /* Check if compression is supported */
629 if (flags
& BLOCK_FLAG_COMPRESS
) {
630 QEMUOptionParameter
*encryption
=
631 get_option_parameter(param
, BLOCK_OPT_ENCRYPT
);
633 if (!drv
->bdrv_write_compressed
) {
634 error("Compression not supported for this file format");
637 if (encryption
&& encryption
->value
.n
) {
638 error("Compression and encryption not supported at the same time");
642 /* Create the new image */
643 ret
= bdrv_create(drv
, out_filename
, param
);
644 free_option_parameters(param
);
647 if (ret
== -ENOTSUP
) {
648 error("Formatting not supported for file format '%s'", out_fmt
);
649 } else if (ret
== -EFBIG
) {
650 error("The image size is too large for file format '%s'", out_fmt
);
652 error("Error while formatting '%s'", out_filename
);
656 out_bs
= bdrv_new_open(out_filename
, out_fmt
);
660 bdrv_get_geometry(bs
[0], &bs_sectors
);
662 if (flags
& BLOCK_FLAG_COMPRESS
) {
663 if (bdrv_get_info(out_bs
, &bdi
) < 0)
664 error("could not get block driver info");
665 cluster_size
= bdi
.cluster_size
;
666 if (cluster_size
<= 0 || cluster_size
> IO_BUF_SIZE
)
667 error("invalid cluster size");
668 cluster_sectors
= cluster_size
>> 9;
675 nb_sectors
= total_sectors
- sector_num
;
678 if (nb_sectors
>= cluster_sectors
)
683 bs_num
= sector_num
- bs_offset
;
684 assert (bs_num
>= 0);
687 while (remainder
> 0) {
689 while (bs_num
== bs_sectors
) {
691 assert (bs_i
< bs_n
);
692 bs_offset
+= bs_sectors
;
693 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
695 /* printf("changing part: sector_num=%lld, "
696 "bs_i=%d, bs_offset=%lld, bs_sectors=%lld\n",
697 sector_num, bs_i, bs_offset, bs_sectors); */
699 assert (bs_num
< bs_sectors
);
701 nlow
= (remainder
> bs_sectors
- bs_num
) ? bs_sectors
- bs_num
: remainder
;
703 if (bdrv_read(bs
[bs_i
], bs_num
, buf2
, nlow
) < 0)
704 error("error while reading");
711 assert (remainder
== 0);
713 if (n
< cluster_sectors
)
714 memset(buf
+ n
* 512, 0, cluster_size
- n
* 512);
715 if (is_not_zero(buf
, cluster_size
)) {
716 if (bdrv_write_compressed(out_bs
, sector_num
, buf
,
717 cluster_sectors
) != 0)
718 error("error while compressing sector %" PRId64
,
723 /* signal EOF to align */
724 bdrv_write_compressed(out_bs
, 0, NULL
, 0);
726 sector_num
= 0; // total number of sectors converted so far
728 nb_sectors
= total_sectors
- sector_num
;
731 if (nb_sectors
>= (IO_BUF_SIZE
/ 512))
732 n
= (IO_BUF_SIZE
/ 512);
736 while (sector_num
- bs_offset
>= bs_sectors
) {
738 assert (bs_i
< bs_n
);
739 bs_offset
+= bs_sectors
;
740 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
741 /* printf("changing part: sector_num=%lld, bs_i=%d, "
742 "bs_offset=%lld, bs_sectors=%lld\n",
743 sector_num, bs_i, bs_offset, bs_sectors); */
746 if (n
> bs_offset
+ bs_sectors
- sector_num
)
747 n
= bs_offset
+ bs_sectors
- sector_num
;
749 if (strcmp(drv
->format_name
, "host_device")) {
750 /* If the output image is being created as a copy on write image,
751 assume that sectors which are unallocated in the input image
752 are present in both the output's and input's base images (no
753 need to copy them). */
755 if (!bdrv_is_allocated(bs
[bs_i
], sector_num
- bs_offset
,
760 /* The next 'n1' sectors are allocated in the input image. Copy
761 only those as they may be followed by unallocated sectors. */
768 if (bdrv_read(bs
[bs_i
], sector_num
- bs_offset
, buf
, n
) < 0)
769 error("error while reading");
770 /* NOTE: at the same time we convert, we do not write zero
771 sectors to have a chance to compress the image. Ideally, we
772 should add a specific call to have the info to go faster */
775 /* If the output image is being created as a copy on write image,
776 copy all sectors even the ones containing only NUL bytes,
777 because they may differ from the sectors in the base image.
779 If the output is to a host device, we also write out
780 sectors that are entirely 0, since whatever data was
781 already there is garbage, not 0s. */
782 if (strcmp(drv
->format_name
, "host_device") == 0 || out_baseimg
||
783 is_allocated_sectors(buf1
, n
, &n1
)) {
784 if (bdrv_write(out_bs
, sector_num
, buf1
, n1
) < 0)
785 error("error while writing");
794 for (bs_i
= 0; bs_i
< bs_n
; bs_i
++)
795 bdrv_delete(bs
[bs_i
]);
801 static int64_t get_allocated_file_size(const char *filename
)
803 typedef DWORD (WINAPI
* get_compressed_t
)(const char *filename
, DWORD
*high
);
804 get_compressed_t get_compressed
;
807 /* WinNT support GetCompressedFileSize to determine allocate size */
808 get_compressed
= (get_compressed_t
) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
809 if (get_compressed
) {
811 low
= get_compressed(filename
, &high
);
812 if (low
!= 0xFFFFFFFFlu
|| GetLastError() == NO_ERROR
)
813 return (((int64_t) high
) << 32) + low
;
816 if (_stati64(filename
, &st
) < 0)
821 static int64_t get_allocated_file_size(const char *filename
)
824 if (stat(filename
, &st
) < 0)
826 return (int64_t)st
.st_blocks
* 512;
830 static void dump_snapshots(BlockDriverState
*bs
)
832 QEMUSnapshotInfo
*sn_tab
, *sn
;
836 nb_sns
= bdrv_snapshot_list(bs
, &sn_tab
);
839 printf("Snapshot list:\n");
840 printf("%s\n", bdrv_snapshot_dump(buf
, sizeof(buf
), NULL
));
841 for(i
= 0; i
< nb_sns
; i
++) {
843 printf("%s\n", bdrv_snapshot_dump(buf
, sizeof(buf
), sn
));
848 static int img_info(int argc
, char **argv
)
851 const char *filename
, *fmt
;
853 BlockDriverState
*bs
;
854 char fmt_name
[128], size_buf
[128], dsize_buf
[128];
855 uint64_t total_sectors
;
856 int64_t allocated_size
;
857 char backing_filename
[1024];
858 char backing_filename2
[1024];
863 c
= getopt(argc
, argv
, "f:h");
877 filename
= argv
[optind
++];
881 error("Not enough memory");
883 drv
= bdrv_find_format(fmt
);
885 error("Unknown file format '%s'", fmt
);
889 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
890 error("Could not open '%s'", filename
);
892 bdrv_get_format(bs
, fmt_name
, sizeof(fmt_name
));
893 bdrv_get_geometry(bs
, &total_sectors
);
894 get_human_readable_size(size_buf
, sizeof(size_buf
), total_sectors
* 512);
895 allocated_size
= get_allocated_file_size(filename
);
896 if (allocated_size
< 0)
897 snprintf(dsize_buf
, sizeof(dsize_buf
), "unavailable");
899 get_human_readable_size(dsize_buf
, sizeof(dsize_buf
),
903 "virtual size: %s (%" PRId64
" bytes)\n"
905 filename
, fmt_name
, size_buf
,
906 (total_sectors
* 512),
908 if (bdrv_is_encrypted(bs
))
909 printf("encrypted: yes\n");
910 if (bdrv_get_info(bs
, &bdi
) >= 0) {
911 if (bdi
.cluster_size
!= 0)
912 printf("cluster_size: %d\n", bdi
.cluster_size
);
914 bdrv_get_backing_filename(bs
, backing_filename
, sizeof(backing_filename
));
915 if (backing_filename
[0] != '\0') {
916 path_combine(backing_filename2
, sizeof(backing_filename2
),
917 filename
, backing_filename
);
918 printf("backing file: %s (actual path: %s)\n",
927 #define SNAPSHOT_LIST 1
928 #define SNAPSHOT_CREATE 2
929 #define SNAPSHOT_APPLY 3
930 #define SNAPSHOT_DELETE 4
932 static int img_snapshot(int argc
, char **argv
)
934 BlockDriverState
*bs
;
936 char *filename
, *snapshot_name
= NULL
;
941 /* Parse commandline parameters */
943 c
= getopt(argc
, argv
, "la:c:d:h");
955 action
= SNAPSHOT_LIST
;
962 action
= SNAPSHOT_APPLY
;
963 snapshot_name
= optarg
;
970 action
= SNAPSHOT_CREATE
;
971 snapshot_name
= optarg
;
978 action
= SNAPSHOT_DELETE
;
979 snapshot_name
= optarg
;
986 filename
= argv
[optind
++];
991 error("Not enough memory");
993 if (bdrv_open2(bs
, filename
, 0, NULL
) < 0) {
994 error("Could not open '%s'", filename
);
997 /* Perform the requested action */
1003 case SNAPSHOT_CREATE
:
1004 memset(&sn
, 0, sizeof(sn
));
1005 pstrcpy(sn
.name
, sizeof(sn
.name
), snapshot_name
);
1007 qemu_gettimeofday(&tv
);
1008 sn
.date_sec
= tv
.tv_sec
;
1009 sn
.date_nsec
= tv
.tv_usec
* 1000;
1011 ret
= bdrv_snapshot_create(bs
, &sn
);
1013 error("Could not create snapshot '%s': %d (%s)",
1014 snapshot_name
, ret
, strerror(-ret
));
1017 case SNAPSHOT_APPLY
:
1018 ret
= bdrv_snapshot_goto(bs
, snapshot_name
);
1020 error("Could not apply snapshot '%s': %d (%s)",
1021 snapshot_name
, ret
, strerror(-ret
));
1024 case SNAPSHOT_DELETE
:
1025 ret
= bdrv_snapshot_delete(bs
, snapshot_name
);
1027 error("Could not delete snapshot '%s': %d (%s)",
1028 snapshot_name
, ret
, strerror(-ret
));
1038 static const img_cmd_t img_cmds
[] = {
1039 #define DEF(option, callback, arg_string) \
1040 { option, callback },
1041 #include "qemu-img-cmds.h"
1047 int main(int argc
, char **argv
)
1049 const img_cmd_t
*cmd
;
1050 const char *cmdname
;
1058 /* find the command */
1059 for(cmd
= img_cmds
; cmd
->name
!= NULL
; cmd
++) {
1060 if (!strcmp(cmdname
, cmd
->name
)) {
1061 return cmd
->handler(argc
, argv
);