mirror of
https://git.proxmox.com/git/qemu
synced 2025-08-07 09:40:44 +00:00
Merge remote-tracking branch 'stefanha/block' into staging
# By Kevin Wolf (19) and others # Via Stefan Hajnoczi * stefanha/block: (26 commits) hmp: add parameters device and -v for info block hmp: show ImageInfo in 'info block' qmp: add ImageInfo in BlockDeviceInfo used by query-block block: add image info query function bdrv_query_image_info() block: add snapshot info query function bdrv_query_snapshot_info_list() ide-test: Add FLUSH CACHE test case ide: Set BSY bit during FLUSH ide-test: Add enum value for DEV blkdebug: Add BLKDBG_FLUSH_TO_OS/DISK events Make qemu-io commands available in HMP qemu-io: Use the qemu version for -V qemu-io: Interface cleanup qemu-io: Move remaining helpers from cmd.c qemu-io: Move command_loop() and friends qemu-io: Move functions for registering and running commands qemu-io: Move qemu_strsep() to cutils.c qemu-io: Move 'quit' function qemu-io: Move 'help' function qemu-io: Factor out qemuio_command qemu-io: Split off commands to qemu-io-cmds.c ... Message-id: 1370606325-10680-1-git-send-email-stefanha@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
7387de16d0
2
Makefile
2
Makefile
@ -186,7 +186,7 @@ qemu-img.o: qemu-img-cmds.h
|
|||||||
|
|
||||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a
|
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-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||||
qemu-io$(EXESUF): qemu-io.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a
|
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||||
|
|
||||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ block-obj-$(CONFIG_POSIX) += aio-posix.o
|
|||||||
block-obj-$(CONFIG_WIN32) += aio-win32.o
|
block-obj-$(CONFIG_WIN32) += aio-win32.o
|
||||||
block-obj-y += block/
|
block-obj-y += block/
|
||||||
block-obj-y += qapi-types.o qapi-visit.o
|
block-obj-y += qapi-types.o qapi-visit.o
|
||||||
|
block-obj-y += qemu-io-cmds.o
|
||||||
|
|
||||||
block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
|
block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
|
||||||
block-obj-y += qemu-coroutine-sleep.o
|
block-obj-y += qemu-coroutine-sleep.o
|
||||||
|
8
block.c
8
block.c
@ -3186,13 +3186,11 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
|||||||
|
|
||||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||||
{
|
{
|
||||||
BlockDriver *drv = bs->drv;
|
if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
|
||||||
|
|
||||||
if (!drv || !drv->bdrv_debug_event) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
drv->bdrv_debug_event(bs, event);
|
bs->drv->bdrv_debug_event(bs, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||||
@ -4024,6 +4022,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Write back cached data to the OS even with cache=unsafe */
|
/* Write back cached data to the OS even with cache=unsafe */
|
||||||
|
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS);
|
||||||
if (bs->drv->bdrv_co_flush_to_os) {
|
if (bs->drv->bdrv_co_flush_to_os) {
|
||||||
ret = bs->drv->bdrv_co_flush_to_os(bs);
|
ret = bs->drv->bdrv_co_flush_to_os(bs);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -4036,6 +4035,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
|||||||
goto flush_parent;
|
goto flush_parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK);
|
||||||
if (bs->drv->bdrv_co_flush_to_disk) {
|
if (bs->drv->bdrv_co_flush_to_disk) {
|
||||||
ret = bs->drv->bdrv_co_flush_to_disk(bs);
|
ret = bs->drv->bdrv_co_flush_to_disk(bs);
|
||||||
} else if (bs->drv->bdrv_aio_flush) {
|
} else if (bs->drv->bdrv_aio_flush) {
|
||||||
|
@ -182,6 +182,9 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
|
|||||||
[BLKDBG_CLUSTER_ALLOC] = "cluster_alloc",
|
[BLKDBG_CLUSTER_ALLOC] = "cluster_alloc",
|
||||||
[BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes",
|
[BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes",
|
||||||
[BLKDBG_CLUSTER_FREE] = "cluster_free",
|
[BLKDBG_CLUSTER_FREE] = "cluster_free",
|
||||||
|
|
||||||
|
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
|
||||||
|
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
|
||||||
};
|
};
|
||||||
|
|
||||||
static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
||||||
|
148
block/qapi.c
148
block/qapi.c
@ -26,29 +26,56 @@
|
|||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
|
|
||||||
void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info)
|
/*
|
||||||
|
* Returns 0 on success, with *p_list either set to describe snapshot
|
||||||
|
* information, or NULL because there are no snapshots. Returns -errno on
|
||||||
|
* error, with *p_list untouched.
|
||||||
|
*/
|
||||||
|
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
|
||||||
|
SnapshotInfoList **p_list,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
int i, sn_count;
|
int i, sn_count;
|
||||||
QEMUSnapshotInfo *sn_tab = NULL;
|
QEMUSnapshotInfo *sn_tab = NULL;
|
||||||
SnapshotInfoList *info_list, *cur_item = NULL;
|
SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL;
|
||||||
|
SnapshotInfo *info;
|
||||||
|
|
||||||
sn_count = bdrv_snapshot_list(bs, &sn_tab);
|
sn_count = bdrv_snapshot_list(bs, &sn_tab);
|
||||||
|
if (sn_count < 0) {
|
||||||
|
const char *dev = bdrv_get_device_name(bs);
|
||||||
|
switch (sn_count) {
|
||||||
|
case -ENOMEDIUM:
|
||||||
|
error_setg(errp, "Device '%s' is not inserted", dev);
|
||||||
|
break;
|
||||||
|
case -ENOTSUP:
|
||||||
|
error_setg(errp,
|
||||||
|
"Device '%s' does not support internal snapshots",
|
||||||
|
dev);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_setg_errno(errp, -sn_count,
|
||||||
|
"Can't list snapshots of device '%s'", dev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return sn_count;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < sn_count; i++) {
|
for (i = 0; i < sn_count; i++) {
|
||||||
info->has_snapshots = true;
|
info = g_new0(SnapshotInfo, 1);
|
||||||
info_list = g_new0(SnapshotInfoList, 1);
|
info->id = g_strdup(sn_tab[i].id_str);
|
||||||
|
info->name = g_strdup(sn_tab[i].name);
|
||||||
|
info->vm_state_size = sn_tab[i].vm_state_size;
|
||||||
|
info->date_sec = sn_tab[i].date_sec;
|
||||||
|
info->date_nsec = sn_tab[i].date_nsec;
|
||||||
|
info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
|
||||||
|
info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
|
||||||
|
|
||||||
info_list->value = g_new0(SnapshotInfo, 1);
|
info_list = g_new0(SnapshotInfoList, 1);
|
||||||
info_list->value->id = g_strdup(sn_tab[i].id_str);
|
info_list->value = info;
|
||||||
info_list->value->name = g_strdup(sn_tab[i].name);
|
|
||||||
info_list->value->vm_state_size = sn_tab[i].vm_state_size;
|
|
||||||
info_list->value->date_sec = sn_tab[i].date_sec;
|
|
||||||
info_list->value->date_nsec = sn_tab[i].date_nsec;
|
|
||||||
info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
|
|
||||||
info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
|
|
||||||
|
|
||||||
/* XXX: waiting for the qapi to support qemu-queue.h types */
|
/* XXX: waiting for the qapi to support qemu-queue.h types */
|
||||||
if (!cur_item) {
|
if (!cur_item) {
|
||||||
info->snapshots = cur_item = info_list;
|
head = cur_item = info_list;
|
||||||
} else {
|
} else {
|
||||||
cur_item->next = info_list;
|
cur_item->next = info_list;
|
||||||
cur_item = info_list;
|
cur_item = info_list;
|
||||||
@ -57,20 +84,40 @@ void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_free(sn_tab);
|
g_free(sn_tab);
|
||||||
|
*p_list = head;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_collect_image_info(BlockDriverState *bs,
|
/**
|
||||||
ImageInfo *info,
|
* bdrv_query_image_info:
|
||||||
const char *filename)
|
* @bs: block device to examine
|
||||||
|
* @p_info: location to store image information
|
||||||
|
* @errp: location to store error information
|
||||||
|
*
|
||||||
|
* Store "flat" image information in @p_info.
|
||||||
|
*
|
||||||
|
* "Flat" means it does *not* query backing image information,
|
||||||
|
* i.e. (*pinfo)->has_backing_image will be set to false and
|
||||||
|
* (*pinfo)->backing_image to NULL even when the image does in fact have
|
||||||
|
* a backing image.
|
||||||
|
*
|
||||||
|
* @p_info will be set only on success. On error, store error in @errp.
|
||||||
|
*/
|
||||||
|
void bdrv_query_image_info(BlockDriverState *bs,
|
||||||
|
ImageInfo **p_info,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
uint64_t total_sectors;
|
uint64_t total_sectors;
|
||||||
char backing_filename[1024];
|
const char *backing_filename;
|
||||||
char backing_filename2[1024];
|
char backing_filename2[1024];
|
||||||
BlockDriverInfo bdi;
|
BlockDriverInfo bdi;
|
||||||
|
int ret;
|
||||||
|
Error *err = NULL;
|
||||||
|
ImageInfo *info = g_new0(ImageInfo, 1);
|
||||||
|
|
||||||
bdrv_get_geometry(bs, &total_sectors);
|
bdrv_get_geometry(bs, &total_sectors);
|
||||||
|
|
||||||
info->filename = g_strdup(filename);
|
info->filename = g_strdup(bs->filename);
|
||||||
info->format = g_strdup(bdrv_get_format_name(bs));
|
info->format = g_strdup(bdrv_get_format_name(bs));
|
||||||
info->virtual_size = total_sectors * 512;
|
info->virtual_size = total_sectors * 512;
|
||||||
info->actual_size = bdrv_get_allocated_file_size(bs);
|
info->actual_size = bdrv_get_allocated_file_size(bs);
|
||||||
@ -87,7 +134,7 @@ void bdrv_collect_image_info(BlockDriverState *bs,
|
|||||||
info->dirty_flag = bdi.is_dirty;
|
info->dirty_flag = bdi.is_dirty;
|
||||||
info->has_dirty_flag = true;
|
info->has_dirty_flag = true;
|
||||||
}
|
}
|
||||||
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
|
backing_filename = bs->backing_file;
|
||||||
if (backing_filename[0] != '\0') {
|
if (backing_filename[0] != '\0') {
|
||||||
info->backing_filename = g_strdup(backing_filename);
|
info->backing_filename = g_strdup(backing_filename);
|
||||||
info->has_backing_filename = true;
|
info->has_backing_filename = true;
|
||||||
@ -105,11 +152,37 @@ void bdrv_collect_image_info(BlockDriverState *bs,
|
|||||||
info->has_backing_filename_format = true;
|
info->has_backing_filename_format = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err);
|
||||||
|
switch (ret) {
|
||||||
|
case 0:
|
||||||
|
if (info->snapshots) {
|
||||||
|
info->has_snapshots = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* recoverable error */
|
||||||
|
case -ENOMEDIUM:
|
||||||
|
case -ENOTSUP:
|
||||||
|
error_free(err);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_propagate(errp, err);
|
||||||
|
qapi_free_ImageInfo(info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p_info = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockInfo *bdrv_query_info(BlockDriverState *bs)
|
/* @p_info will be set only on success. */
|
||||||
|
void bdrv_query_info(BlockDriverState *bs,
|
||||||
|
BlockInfo **p_info,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockInfo *info = g_malloc0(sizeof(*info));
|
BlockInfo *info = g_malloc0(sizeof(*info));
|
||||||
|
BlockDriverState *bs0;
|
||||||
|
ImageInfo **p_image_info;
|
||||||
|
Error *local_err = NULL;
|
||||||
info->device = g_strdup(bs->device_name);
|
info->device = g_strdup(bs->device_name);
|
||||||
info->type = g_strdup("unknown");
|
info->type = g_strdup("unknown");
|
||||||
info->locked = bdrv_dev_is_medium_locked(bs);
|
info->locked = bdrv_dev_is_medium_locked(bs);
|
||||||
@ -163,8 +236,30 @@ BlockInfo *bdrv_query_info(BlockDriverState *bs)
|
|||||||
info->inserted->iops_wr =
|
info->inserted->iops_wr =
|
||||||
bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
|
bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bs0 = bs;
|
||||||
|
p_image_info = &info->inserted->image;
|
||||||
|
while (1) {
|
||||||
|
bdrv_query_image_info(bs0, p_image_info, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (bs0->drv && bs0->backing_hd) {
|
||||||
|
bs0 = bs0->backing_hd;
|
||||||
|
(*p_image_info)->has_backing_image = true;
|
||||||
|
p_image_info = &((*p_image_info)->backing_image);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return info;
|
|
||||||
|
*p_info = info;
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
qapi_free_BlockInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
|
||||||
@ -201,16 +296,25 @@ BlockInfoList *qmp_query_block(Error **errp)
|
|||||||
{
|
{
|
||||||
BlockInfoList *head = NULL, **p_next = &head;
|
BlockInfoList *head = NULL, **p_next = &head;
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
while ((bs = bdrv_next(bs))) {
|
while ((bs = bdrv_next(bs))) {
|
||||||
BlockInfoList *info = g_malloc0(sizeof(*info));
|
BlockInfoList *info = g_malloc0(sizeof(*info));
|
||||||
info->value = bdrv_query_info(bs);
|
bdrv_query_info(bs, &info->value, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
*p_next = info;
|
*p_next = info;
|
||||||
p_next = &info->next;
|
p_next = &info->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return head;
|
return head;
|
||||||
|
|
||||||
|
err:
|
||||||
|
qapi_free_BlockInfoList(head);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockStatsList *qmp_query_blockstats(Error **errp)
|
BlockStatsList *qmp_query_blockstats(Error **errp)
|
||||||
|
@ -1180,6 +1180,10 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|||||||
*/
|
*/
|
||||||
if (bdrv_get_attached_dev(bs)) {
|
if (bdrv_get_attached_dev(bs)) {
|
||||||
bdrv_make_anon(bs);
|
bdrv_make_anon(bs);
|
||||||
|
|
||||||
|
/* Further I/O must not pause the guest */
|
||||||
|
bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT,
|
||||||
|
BLOCKDEV_ON_ERROR_REPORT);
|
||||||
} else {
|
} else {
|
||||||
drive_uninit(drive_get_by_blockdev(bs));
|
drive_uninit(drive_get_by_blockdev(bs));
|
||||||
}
|
}
|
||||||
|
612
cmd.c
612
cmd.c
@ -1,612 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
|
|
||||||
* All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#include "cmd.h"
|
|
||||||
#include "block/aio.h"
|
|
||||||
#include "qemu/main-loop.h"
|
|
||||||
|
|
||||||
#define _(x) x /* not gettext support yet */
|
|
||||||
|
|
||||||
/* from libxcmd/command.c */
|
|
||||||
|
|
||||||
cmdinfo_t *cmdtab;
|
|
||||||
int ncmds;
|
|
||||||
|
|
||||||
static argsfunc_t args_func;
|
|
||||||
static checkfunc_t check_func;
|
|
||||||
static int ncmdline;
|
|
||||||
static char **cmdline;
|
|
||||||
|
|
||||||
static int
|
|
||||||
compare(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
return strcmp(((const cmdinfo_t *)a)->name,
|
|
||||||
((const cmdinfo_t *)b)->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_command(const cmdinfo_t *ci)
|
|
||||||
{
|
|
||||||
cmdtab = g_realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab));
|
|
||||||
cmdtab[ncmds - 1] = *ci;
|
|
||||||
qsort(cmdtab, ncmds, sizeof(*cmdtab), compare);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
check_command(
|
|
||||||
const cmdinfo_t *ci)
|
|
||||||
{
|
|
||||||
if (check_func)
|
|
||||||
return check_func(ci);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
add_check_command(
|
|
||||||
checkfunc_t cf)
|
|
||||||
{
|
|
||||||
check_func = cf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
command_usage(
|
|
||||||
const cmdinfo_t *ci)
|
|
||||||
{
|
|
||||||
printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
command(
|
|
||||||
const cmdinfo_t *ct,
|
|
||||||
int argc,
|
|
||||||
char **argv)
|
|
||||||
{
|
|
||||||
char *cmd = argv[0];
|
|
||||||
|
|
||||||
if (!check_command(ct))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) {
|
|
||||||
if (ct->argmax == -1)
|
|
||||||
fprintf(stderr,
|
|
||||||
_("bad argument count %d to %s, expected at least %d arguments\n"),
|
|
||||||
argc-1, cmd, ct->argmin);
|
|
||||||
else if (ct->argmin == ct->argmax)
|
|
||||||
fprintf(stderr,
|
|
||||||
_("bad argument count %d to %s, expected %d arguments\n"),
|
|
||||||
argc-1, cmd, ct->argmin);
|
|
||||||
else
|
|
||||||
fprintf(stderr,
|
|
||||||
_("bad argument count %d to %s, expected between %d and %d arguments\n"),
|
|
||||||
argc-1, cmd, ct->argmin, ct->argmax);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
optind = 0;
|
|
||||||
return ct->cfunc(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cmdinfo_t *
|
|
||||||
find_command(
|
|
||||||
const char *cmd)
|
|
||||||
{
|
|
||||||
cmdinfo_t *ct;
|
|
||||||
|
|
||||||
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
|
|
||||||
if (strcmp(ct->name, cmd) == 0 ||
|
|
||||||
(ct->altname && strcmp(ct->altname, cmd) == 0))
|
|
||||||
return (const cmdinfo_t *)ct;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_user_command(char *optarg)
|
|
||||||
{
|
|
||||||
cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *));
|
|
||||||
cmdline[ncmdline-1] = optarg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
args_command(
|
|
||||||
int index)
|
|
||||||
{
|
|
||||||
if (args_func)
|
|
||||||
return args_func(index);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
add_args_command(
|
|
||||||
argsfunc_t af)
|
|
||||||
{
|
|
||||||
args_func = af;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void prep_fetchline(void *opaque)
|
|
||||||
{
|
|
||||||
int *fetchable = opaque;
|
|
||||||
|
|
||||||
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
|
|
||||||
*fetchable= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *get_prompt(void);
|
|
||||||
|
|
||||||
void command_loop(void)
|
|
||||||
{
|
|
||||||
int c, i, j = 0, done = 0, fetchable = 0, prompted = 0;
|
|
||||||
char *input;
|
|
||||||
char **v;
|
|
||||||
const cmdinfo_t *ct;
|
|
||||||
|
|
||||||
for (i = 0; !done && i < ncmdline; i++) {
|
|
||||||
input = strdup(cmdline[i]);
|
|
||||||
if (!input) {
|
|
||||||
fprintf(stderr, _("cannot strdup command '%s': %s\n"),
|
|
||||||
cmdline[i], strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
v = breakline(input, &c);
|
|
||||||
if (c) {
|
|
||||||
ct = find_command(v[0]);
|
|
||||||
if (ct) {
|
|
||||||
if (ct->flags & CMD_FLAG_GLOBAL) {
|
|
||||||
done = command(ct, c, v);
|
|
||||||
} else {
|
|
||||||
j = 0;
|
|
||||||
while (!done && (j = args_command(j))) {
|
|
||||||
done = command(ct, c, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doneline(input, v);
|
|
||||||
}
|
|
||||||
if (cmdline) {
|
|
||||||
g_free(cmdline);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!done) {
|
|
||||||
if (!prompted) {
|
|
||||||
printf("%s", get_prompt());
|
|
||||||
fflush(stdout);
|
|
||||||
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
|
|
||||||
prompted = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
main_loop_wait(false);
|
|
||||||
|
|
||||||
if (!fetchable) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
input = fetchline();
|
|
||||||
if (input == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
v = breakline(input, &c);
|
|
||||||
if (c) {
|
|
||||||
ct = find_command(v[0]);
|
|
||||||
if (ct) {
|
|
||||||
done = command(ct, c, v);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doneline(input, v);
|
|
||||||
|
|
||||||
prompted = 0;
|
|
||||||
fetchable = 0;
|
|
||||||
}
|
|
||||||
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* from libxcmd/input.c */
|
|
||||||
|
|
||||||
#if defined(ENABLE_READLINE)
|
|
||||||
# include <readline/history.h>
|
|
||||||
# include <readline/readline.h>
|
|
||||||
#elif defined(ENABLE_EDITLINE)
|
|
||||||
# include <histedit.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static char *
|
|
||||||
get_prompt(void)
|
|
||||||
{
|
|
||||||
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
|
|
||||||
|
|
||||||
if (!prompt[0])
|
|
||||||
snprintf(prompt, sizeof(prompt), "%s> ", progname);
|
|
||||||
return prompt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(ENABLE_READLINE)
|
|
||||||
char *
|
|
||||||
fetchline(void)
|
|
||||||
{
|
|
||||||
char *line;
|
|
||||||
|
|
||||||
line = readline(get_prompt());
|
|
||||||
if (line && *line)
|
|
||||||
add_history(line);
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
#elif defined(ENABLE_EDITLINE)
|
|
||||||
static char *el_get_prompt(EditLine *e) { return get_prompt(); }
|
|
||||||
char *
|
|
||||||
fetchline(void)
|
|
||||||
{
|
|
||||||
static EditLine *el;
|
|
||||||
static History *hist;
|
|
||||||
HistEvent hevent;
|
|
||||||
char *line;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
if (!el) {
|
|
||||||
hist = history_init();
|
|
||||||
history(hist, &hevent, H_SETSIZE, 100);
|
|
||||||
el = el_init(progname, stdin, stdout, stderr);
|
|
||||||
el_source(el, NULL);
|
|
||||||
el_set(el, EL_SIGNAL, 1);
|
|
||||||
el_set(el, EL_PROMPT, el_get_prompt);
|
|
||||||
el_set(el, EL_HIST, history, (const char *)hist);
|
|
||||||
}
|
|
||||||
line = strdup(el_gets(el, &count));
|
|
||||||
if (line) {
|
|
||||||
if (count > 0)
|
|
||||||
line[count-1] = '\0';
|
|
||||||
if (*line)
|
|
||||||
history(hist, &hevent, H_ENTER, line);
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
# define MAXREADLINESZ 1024
|
|
||||||
char *
|
|
||||||
fetchline(void)
|
|
||||||
{
|
|
||||||
char *p, *line = malloc(MAXREADLINESZ);
|
|
||||||
|
|
||||||
if (!line)
|
|
||||||
return NULL;
|
|
||||||
if (!fgets(line, MAXREADLINESZ, stdin)) {
|
|
||||||
free(line);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
p = line + strlen(line);
|
|
||||||
if (p != line && p[-1] == '\n')
|
|
||||||
p[-1] = '\0';
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static char *qemu_strsep(char **input, const char *delim)
|
|
||||||
{
|
|
||||||
char *result = *input;
|
|
||||||
if (result != NULL) {
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
for (p = result; *p != '\0'; p++) {
|
|
||||||
if (strchr(delim, *p)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*p == '\0') {
|
|
||||||
*input = NULL;
|
|
||||||
} else {
|
|
||||||
*p = '\0';
|
|
||||||
*input = p + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
char **breakline(char *input, int *count)
|
|
||||||
{
|
|
||||||
int c = 0;
|
|
||||||
char *p;
|
|
||||||
char **rval = calloc(sizeof(char *), 1);
|
|
||||||
char **tmp;
|
|
||||||
|
|
||||||
while (rval && (p = qemu_strsep(&input, " ")) != NULL) {
|
|
||||||
if (!*p) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
c++;
|
|
||||||
tmp = realloc(rval, sizeof(*rval) * (c + 1));
|
|
||||||
if (!tmp) {
|
|
||||||
free(rval);
|
|
||||||
rval = NULL;
|
|
||||||
c = 0;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
rval = tmp;
|
|
||||||
}
|
|
||||||
rval[c - 1] = p;
|
|
||||||
rval[c] = NULL;
|
|
||||||
}
|
|
||||||
*count = c;
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
doneline(
|
|
||||||
char *input,
|
|
||||||
char **vec)
|
|
||||||
{
|
|
||||||
free(input);
|
|
||||||
free(vec);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXABYTES(x) ((long long)(x) << 60)
|
|
||||||
#define PETABYTES(x) ((long long)(x) << 50)
|
|
||||||
#define TERABYTES(x) ((long long)(x) << 40)
|
|
||||||
#define GIGABYTES(x) ((long long)(x) << 30)
|
|
||||||
#define MEGABYTES(x) ((long long)(x) << 20)
|
|
||||||
#define KILOBYTES(x) ((long long)(x) << 10)
|
|
||||||
|
|
||||||
long long
|
|
||||||
cvtnum(
|
|
||||||
char *s)
|
|
||||||
{
|
|
||||||
long long i;
|
|
||||||
char *sp;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
i = strtoll(s, &sp, 0);
|
|
||||||
if (i == 0 && sp == s)
|
|
||||||
return -1LL;
|
|
||||||
if (*sp == '\0')
|
|
||||||
return i;
|
|
||||||
|
|
||||||
if (sp[1] != '\0')
|
|
||||||
return -1LL;
|
|
||||||
|
|
||||||
c = qemu_tolower(*sp);
|
|
||||||
switch (c) {
|
|
||||||
default:
|
|
||||||
return i;
|
|
||||||
case 'k':
|
|
||||||
return KILOBYTES(i);
|
|
||||||
case 'm':
|
|
||||||
return MEGABYTES(i);
|
|
||||||
case 'g':
|
|
||||||
return GIGABYTES(i);
|
|
||||||
case 't':
|
|
||||||
return TERABYTES(i);
|
|
||||||
case 'p':
|
|
||||||
return PETABYTES(i);
|
|
||||||
case 'e':
|
|
||||||
return EXABYTES(i);
|
|
||||||
}
|
|
||||||
return -1LL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TO_EXABYTES(x) ((x) / EXABYTES(1))
|
|
||||||
#define TO_PETABYTES(x) ((x) / PETABYTES(1))
|
|
||||||
#define TO_TERABYTES(x) ((x) / TERABYTES(1))
|
|
||||||
#define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
|
|
||||||
#define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
|
|
||||||
#define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
|
|
||||||
|
|
||||||
void
|
|
||||||
cvtstr(
|
|
||||||
double value,
|
|
||||||
char *str,
|
|
||||||
size_t size)
|
|
||||||
{
|
|
||||||
char *trim;
|
|
||||||
const char *suffix;
|
|
||||||
|
|
||||||
if (value >= EXABYTES(1)) {
|
|
||||||
suffix = " EiB";
|
|
||||||
snprintf(str, size - 4, "%.3f", TO_EXABYTES(value));
|
|
||||||
} else if (value >= PETABYTES(1)) {
|
|
||||||
suffix = " PiB";
|
|
||||||
snprintf(str, size - 4, "%.3f", TO_PETABYTES(value));
|
|
||||||
} else if (value >= TERABYTES(1)) {
|
|
||||||
suffix = " TiB";
|
|
||||||
snprintf(str, size - 4, "%.3f", TO_TERABYTES(value));
|
|
||||||
} else if (value >= GIGABYTES(1)) {
|
|
||||||
suffix = " GiB";
|
|
||||||
snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value));
|
|
||||||
} else if (value >= MEGABYTES(1)) {
|
|
||||||
suffix = " MiB";
|
|
||||||
snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value));
|
|
||||||
} else if (value >= KILOBYTES(1)) {
|
|
||||||
suffix = " KiB";
|
|
||||||
snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value));
|
|
||||||
} else {
|
|
||||||
suffix = " bytes";
|
|
||||||
snprintf(str, size - 6, "%f", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
trim = strstr(str, ".000");
|
|
||||||
if (trim) {
|
|
||||||
strcpy(trim, suffix);
|
|
||||||
} else {
|
|
||||||
strcat(str, suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval
|
|
||||||
tsub(struct timeval t1, struct timeval t2)
|
|
||||||
{
|
|
||||||
t1.tv_usec -= t2.tv_usec;
|
|
||||||
if (t1.tv_usec < 0) {
|
|
||||||
t1.tv_usec += 1000000;
|
|
||||||
t1.tv_sec--;
|
|
||||||
}
|
|
||||||
t1.tv_sec -= t2.tv_sec;
|
|
||||||
return t1;
|
|
||||||
}
|
|
||||||
|
|
||||||
double
|
|
||||||
tdiv(double value, struct timeval tv)
|
|
||||||
{
|
|
||||||
return value / ((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define HOURS(sec) ((sec) / (60 * 60))
|
|
||||||
#define MINUTES(sec) (((sec) % (60 * 60)) / 60)
|
|
||||||
#define SECONDS(sec) ((sec) % 60)
|
|
||||||
|
|
||||||
void
|
|
||||||
timestr(
|
|
||||||
struct timeval *tv,
|
|
||||||
char *ts,
|
|
||||||
size_t size,
|
|
||||||
int format)
|
|
||||||
{
|
|
||||||
double usec = (double)tv->tv_usec / 1000000.0;
|
|
||||||
|
|
||||||
if (format & TERSE_FIXED_TIME) {
|
|
||||||
if (!HOURS(tv->tv_sec)) {
|
|
||||||
snprintf(ts, size, "%u:%02u.%02u",
|
|
||||||
(unsigned int) MINUTES(tv->tv_sec),
|
|
||||||
(unsigned int) SECONDS(tv->tv_sec),
|
|
||||||
(unsigned int) (usec * 100));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((format & VERBOSE_FIXED_TIME) || tv->tv_sec) {
|
|
||||||
snprintf(ts, size, "%u:%02u:%02u.%02u",
|
|
||||||
(unsigned int) HOURS(tv->tv_sec),
|
|
||||||
(unsigned int) MINUTES(tv->tv_sec),
|
|
||||||
(unsigned int) SECONDS(tv->tv_sec),
|
|
||||||
(unsigned int) (usec * 100));
|
|
||||||
} else {
|
|
||||||
snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* from libxcmd/quit.c */
|
|
||||||
|
|
||||||
static cmdinfo_t quit_cmd;
|
|
||||||
|
|
||||||
/* ARGSUSED */
|
|
||||||
static int
|
|
||||||
quit_f(
|
|
||||||
int argc,
|
|
||||||
char **argv)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
quit_init(void)
|
|
||||||
{
|
|
||||||
quit_cmd.name = _("quit");
|
|
||||||
quit_cmd.altname = _("q");
|
|
||||||
quit_cmd.cfunc = quit_f;
|
|
||||||
quit_cmd.argmin = -1;
|
|
||||||
quit_cmd.argmax = -1;
|
|
||||||
quit_cmd.flags = CMD_FLAG_GLOBAL;
|
|
||||||
quit_cmd.oneline = _("exit the program");
|
|
||||||
|
|
||||||
add_command(&quit_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* from libxcmd/help.c */
|
|
||||||
|
|
||||||
static cmdinfo_t help_cmd;
|
|
||||||
static void help_onecmd(const char *cmd, const cmdinfo_t *ct);
|
|
||||||
static void help_oneline(const char *cmd, const cmdinfo_t *ct);
|
|
||||||
|
|
||||||
static void
|
|
||||||
help_all(void)
|
|
||||||
{
|
|
||||||
const cmdinfo_t *ct;
|
|
||||||
|
|
||||||
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++)
|
|
||||||
help_oneline(ct->name, ct);
|
|
||||||
printf(_("\nUse 'help commandname' for extended help.\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
help_f(
|
|
||||||
int argc,
|
|
||||||
char **argv)
|
|
||||||
{
|
|
||||||
const cmdinfo_t *ct;
|
|
||||||
|
|
||||||
if (argc == 1) {
|
|
||||||
help_all();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ct = find_command(argv[1]);
|
|
||||||
if (ct == NULL) {
|
|
||||||
printf(_("command %s not found\n"), argv[1]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
help_onecmd(argv[1], ct);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
help_onecmd(
|
|
||||||
const char *cmd,
|
|
||||||
const cmdinfo_t *ct)
|
|
||||||
{
|
|
||||||
help_oneline(cmd, ct);
|
|
||||||
if (ct->help)
|
|
||||||
ct->help();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
help_oneline(
|
|
||||||
const char *cmd,
|
|
||||||
const cmdinfo_t *ct)
|
|
||||||
{
|
|
||||||
if (cmd)
|
|
||||||
printf("%s ", cmd);
|
|
||||||
else {
|
|
||||||
printf("%s ", ct->name);
|
|
||||||
if (ct->altname)
|
|
||||||
printf("(or %s) ", ct->altname);
|
|
||||||
}
|
|
||||||
if (ct->args)
|
|
||||||
printf("%s ", ct->args);
|
|
||||||
printf("-- %s\n", ct->oneline);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
help_init(void)
|
|
||||||
{
|
|
||||||
help_cmd.name = _("help");
|
|
||||||
help_cmd.altname = _("?");
|
|
||||||
help_cmd.cfunc = help_f;
|
|
||||||
help_cmd.argmin = 0;
|
|
||||||
help_cmd.argmax = 1;
|
|
||||||
help_cmd.flags = CMD_FLAG_GLOBAL;
|
|
||||||
help_cmd.args = _("[command]");
|
|
||||||
help_cmd.oneline = _("help for one or all commands");
|
|
||||||
|
|
||||||
add_command(&help_cmd);
|
|
||||||
}
|
|
79
cmd.h
79
cmd.h
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
|
||||||
* All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#ifndef __COMMAND_H__
|
|
||||||
#define __COMMAND_H__
|
|
||||||
|
|
||||||
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
|
|
||||||
|
|
||||||
typedef int (*cfunc_t)(int argc, char **argv);
|
|
||||||
typedef void (*helpfunc_t)(void);
|
|
||||||
|
|
||||||
typedef struct cmdinfo {
|
|
||||||
const char *name;
|
|
||||||
const char *altname;
|
|
||||||
cfunc_t cfunc;
|
|
||||||
int argmin;
|
|
||||||
int argmax;
|
|
||||||
int canpush;
|
|
||||||
int flags;
|
|
||||||
const char *args;
|
|
||||||
const char *oneline;
|
|
||||||
helpfunc_t help;
|
|
||||||
} cmdinfo_t;
|
|
||||||
|
|
||||||
extern cmdinfo_t *cmdtab;
|
|
||||||
extern int ncmds;
|
|
||||||
|
|
||||||
void help_init(void);
|
|
||||||
void quit_init(void);
|
|
||||||
|
|
||||||
typedef int (*argsfunc_t)(int index);
|
|
||||||
typedef int (*checkfunc_t)(const cmdinfo_t *ci);
|
|
||||||
|
|
||||||
void add_command(const cmdinfo_t *ci);
|
|
||||||
void add_user_command(char *optarg);
|
|
||||||
void add_args_command(argsfunc_t af);
|
|
||||||
void add_check_command(checkfunc_t cf);
|
|
||||||
|
|
||||||
const cmdinfo_t *find_command(const char *cmd);
|
|
||||||
|
|
||||||
void command_loop(void);
|
|
||||||
int command_usage(const cmdinfo_t *ci);
|
|
||||||
int command(const cmdinfo_t *ci, int argc, char **argv);
|
|
||||||
|
|
||||||
/* from input.h */
|
|
||||||
char **breakline(char *input, int *count);
|
|
||||||
void doneline(char *input, char **vec);
|
|
||||||
char *fetchline(void);
|
|
||||||
|
|
||||||
long long cvtnum(char *s);
|
|
||||||
void cvtstr(double value, char *str, size_t sz);
|
|
||||||
|
|
||||||
struct timeval tsub(struct timeval t1, struct timeval t2);
|
|
||||||
double tdiv(double value, struct timeval tv);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
DEFAULT_TIME = 0x0,
|
|
||||||
TERSE_FIXED_TIME = 0x1,
|
|
||||||
VERBOSE_FIXED_TIME = 0x2
|
|
||||||
};
|
|
||||||
|
|
||||||
void timestr(struct timeval *tv, char *str, size_t sz, int flags);
|
|
||||||
|
|
||||||
extern char *progname;
|
|
||||||
|
|
||||||
#endif /* __COMMAND_H__ */
|
|
@ -185,6 +185,8 @@ Remove host block device. The result is that guest generated IO is no longer
|
|||||||
submitted against the host device underlying the disk. Once a drive has
|
submitted against the host device underlying the disk. Once a drive has
|
||||||
been deleted, the QEMU Block layer returns -EIO which results in IO
|
been deleted, the QEMU Block layer returns -EIO which results in IO
|
||||||
errors in the guest for applications that are reading/writing to the device.
|
errors in the guest for applications that are reading/writing to the device.
|
||||||
|
These errors are always reported to the guest, regardless of the drive's error
|
||||||
|
actions (drive options rerror, werror).
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1548,6 +1550,22 @@ STEXI
|
|||||||
|
|
||||||
Removes the chardev @var{id}.
|
Removes the chardev @var{id}.
|
||||||
|
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "qemu-io",
|
||||||
|
.args_type = "device:B,command:s",
|
||||||
|
.params = "[device] \"[command]\"",
|
||||||
|
.help = "run a qemu-io command on a block device",
|
||||||
|
.mhandler.cmd = hmp_qemu_io,
|
||||||
|
},
|
||||||
|
|
||||||
|
STEXI
|
||||||
|
@item qemu-io @var{device} @var{command}
|
||||||
|
@findex qemu-io
|
||||||
|
|
||||||
|
Executes a qemu-io command on the given block device.
|
||||||
|
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
39
hmp.c
39
hmp.c
@ -22,6 +22,8 @@
|
|||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
|
#include "block/qapi.h"
|
||||||
|
#include "qemu-io.h"
|
||||||
|
|
||||||
static void hmp_handle_error(Monitor *mon, Error **errp)
|
static void hmp_handle_error(Monitor *mon, Error **errp)
|
||||||
{
|
{
|
||||||
@ -277,10 +279,16 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
|
|||||||
void hmp_info_block(Monitor *mon, const QDict *qdict)
|
void hmp_info_block(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
BlockInfoList *block_list, *info;
|
BlockInfoList *block_list, *info;
|
||||||
|
ImageInfo *image_info;
|
||||||
|
const char *device = qdict_get_try_str(qdict, "device");
|
||||||
|
bool verbose = qdict_get_try_bool(qdict, "verbose", 0);
|
||||||
|
|
||||||
block_list = qmp_query_block(NULL);
|
block_list = qmp_query_block(NULL);
|
||||||
|
|
||||||
for (info = block_list; info; info = info->next) {
|
for (info = block_list; info; info = info->next) {
|
||||||
|
if (device && strcmp(device, info->value->device)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
monitor_printf(mon, "%s: removable=%d",
|
monitor_printf(mon, "%s: removable=%d",
|
||||||
info->value->device, info->value->removable);
|
info->value->device, info->value->removable);
|
||||||
|
|
||||||
@ -318,6 +326,20 @@ void hmp_info_block(Monitor *mon, const QDict *qdict)
|
|||||||
info->value->inserted->iops,
|
info->value->inserted->iops,
|
||||||
info->value->inserted->iops_rd,
|
info->value->inserted->iops_rd,
|
||||||
info->value->inserted->iops_wr);
|
info->value->inserted->iops_wr);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
monitor_printf(mon, " images:\n");
|
||||||
|
image_info = info->value->inserted->image;
|
||||||
|
while (1) {
|
||||||
|
bdrv_image_info_dump((fprintf_function)monitor_printf,
|
||||||
|
mon, image_info);
|
||||||
|
if (image_info->has_backing_image) {
|
||||||
|
image_info = image_info->backing_image;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
monitor_printf(mon, " [not inserted]");
|
monitor_printf(mon, " [not inserted]");
|
||||||
}
|
}
|
||||||
@ -1425,3 +1447,20 @@ void hmp_chardev_remove(Monitor *mon, const QDict *qdict)
|
|||||||
qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
|
qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
|
||||||
hmp_handle_error(mon, &local_err);
|
hmp_handle_error(mon, &local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs;
|
||||||
|
const char* device = qdict_get_str(qdict, "device");
|
||||||
|
const char* command = qdict_get_str(qdict, "command");
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
bs = bdrv_find(device);
|
||||||
|
if (bs) {
|
||||||
|
qemuio_command(bs, command);
|
||||||
|
} else {
|
||||||
|
error_set(&err, QERR_DEVICE_NOT_FOUND, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
hmp_handle_error(mon, &err);
|
||||||
|
}
|
||||||
|
1
hmp.h
1
hmp.h
@ -85,5 +85,6 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
|
|||||||
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
|
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_chardev_add(Monitor *mon, const QDict *qdict);
|
void hmp_chardev_add(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
|
void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_qemu_io(Monitor *mon, const QDict *qdict);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -814,6 +814,7 @@ void ide_flush_cache(IDEState *s)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->status |= BUSY_STAT;
|
||||||
bdrv_acct_start(s->bs, &s->acct, 0, BDRV_ACCT_FLUSH);
|
bdrv_acct_start(s->bs, &s->acct, 0, BDRV_ACCT_FLUSH);
|
||||||
bdrv_aio_flush(s->bs, ide_flush_cb, s);
|
bdrv_aio_flush(s->bs, ide_flush_cb, s);
|
||||||
}
|
}
|
||||||
|
@ -424,6 +424,9 @@ typedef enum {
|
|||||||
BLKDBG_CLUSTER_ALLOC_BYTES,
|
BLKDBG_CLUSTER_ALLOC_BYTES,
|
||||||
BLKDBG_CLUSTER_FREE,
|
BLKDBG_CLUSTER_FREE,
|
||||||
|
|
||||||
|
BLKDBG_FLUSH_TO_OS,
|
||||||
|
BLKDBG_FLUSH_TO_DISK,
|
||||||
|
|
||||||
BLKDBG_EVENT_MAX,
|
BLKDBG_EVENT_MAX,
|
||||||
} BlkDebugEvent;
|
} BlkDebugEvent;
|
||||||
|
|
||||||
|
@ -29,11 +29,15 @@
|
|||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
#include "block/snapshot.h"
|
#include "block/snapshot.h"
|
||||||
|
|
||||||
void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info);
|
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
|
||||||
void bdrv_collect_image_info(BlockDriverState *bs,
|
SnapshotInfoList **p_list,
|
||||||
ImageInfo *info,
|
Error **errp);
|
||||||
const char *filename);
|
void bdrv_query_image_info(BlockDriverState *bs,
|
||||||
BlockInfo *bdrv_query_info(BlockDriverState *s);
|
ImageInfo **p_info,
|
||||||
|
Error **errp);
|
||||||
|
void bdrv_query_info(BlockDriverState *bs,
|
||||||
|
BlockInfo **p_info,
|
||||||
|
Error **errp);
|
||||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs);
|
BlockStats *bdrv_query_stats(const BlockDriverState *bs);
|
||||||
|
|
||||||
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
||||||
|
@ -174,6 +174,7 @@ char *pstrcat(char *buf, int buf_size, const char *s);
|
|||||||
int strstart(const char *str, const char *val, const char **ptr);
|
int strstart(const char *str, const char *val, const char **ptr);
|
||||||
int stristart(const char *str, const char *val, const char **ptr);
|
int stristart(const char *str, const char *val, const char **ptr);
|
||||||
int qemu_strnlen(const char *s, int max_len);
|
int qemu_strnlen(const char *s, int max_len);
|
||||||
|
char *qemu_strsep(char **input, const char *delim);
|
||||||
time_t mktimegm(struct tm *tm);
|
time_t mktimegm(struct tm *tm);
|
||||||
int qemu_fls(int i);
|
int qemu_fls(int i);
|
||||||
int qemu_fdatasync(int fd);
|
int qemu_fdatasync(int fd);
|
||||||
@ -191,6 +192,8 @@ int parse_uint_full(const char *s, unsigned long long *value, int base);
|
|||||||
* A-Z, as strtosz() will use qemu_toupper() on the given argument
|
* A-Z, as strtosz() will use qemu_toupper() on the given argument
|
||||||
* prior to comparison.
|
* prior to comparison.
|
||||||
*/
|
*/
|
||||||
|
#define STRTOSZ_DEFSUFFIX_EB 'E'
|
||||||
|
#define STRTOSZ_DEFSUFFIX_PB 'P'
|
||||||
#define STRTOSZ_DEFSUFFIX_TB 'T'
|
#define STRTOSZ_DEFSUFFIX_TB 'T'
|
||||||
#define STRTOSZ_DEFSUFFIX_GB 'G'
|
#define STRTOSZ_DEFSUFFIX_GB 'G'
|
||||||
#define STRTOSZ_DEFSUFFIX_MB 'M'
|
#define STRTOSZ_DEFSUFFIX_MB 'M'
|
||||||
|
46
include/qemu-io.h
Normal file
46
include/qemu-io.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it would be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QEMU_IO_H
|
||||||
|
#define QEMU_IO_H
|
||||||
|
|
||||||
|
#include "qemu-common.h"
|
||||||
|
|
||||||
|
#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
|
||||||
|
|
||||||
|
typedef int (*cfunc_t)(BlockDriverState *bs, int argc, char **argv);
|
||||||
|
typedef void (*helpfunc_t)(void);
|
||||||
|
|
||||||
|
typedef struct cmdinfo {
|
||||||
|
const char* name;
|
||||||
|
const char* altname;
|
||||||
|
cfunc_t cfunc;
|
||||||
|
int argmin;
|
||||||
|
int argmax;
|
||||||
|
int canpush;
|
||||||
|
int flags;
|
||||||
|
const char *args;
|
||||||
|
const char *oneline;
|
||||||
|
helpfunc_t help;
|
||||||
|
} cmdinfo_t;
|
||||||
|
|
||||||
|
bool qemuio_command(BlockDriverState *bs, const char *cmd);
|
||||||
|
|
||||||
|
void qemuio_add_command(const cmdinfo_t *ci);
|
||||||
|
int qemuio_command_usage(const cmdinfo_t *ci);
|
||||||
|
|
||||||
|
#endif /* QEMU_IO_H */
|
15
monitor.c
15
monitor.c
@ -93,10 +93,10 @@
|
|||||||
* 'M' Non-negative target long (32 or 64 bit), in user mode the
|
* 'M' Non-negative target long (32 or 64 bit), in user mode the
|
||||||
* value is multiplied by 2^20 (think Mebibyte)
|
* value is multiplied by 2^20 (think Mebibyte)
|
||||||
* 'o' octets (aka bytes)
|
* 'o' octets (aka bytes)
|
||||||
* user mode accepts an optional T, t, G, g, M, m, K, k
|
* user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
|
||||||
* suffix, which multiplies the value by 2^40 for
|
* K, k suffix, which multiplies the value by 2^60 for suffixes E
|
||||||
* suffixes T and t, 2^30 for suffixes G and g, 2^20 for
|
* and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
|
||||||
* M and m, 2^10 for K and k
|
* 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
|
||||||
* 'T' double
|
* 'T' double
|
||||||
* user mode accepts an optional ms, us, ns suffix,
|
* user mode accepts an optional ms, us, ns suffix,
|
||||||
* which divides the value by 1e3, 1e6, 1e9, respectively
|
* which divides the value by 1e3, 1e6, 1e9, respectively
|
||||||
@ -2472,9 +2472,10 @@ static mon_cmd_t info_cmds[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "block",
|
.name = "block",
|
||||||
.args_type = "",
|
.args_type = "verbose:-v,device:B?",
|
||||||
.params = "",
|
.params = "[-v] [device]",
|
||||||
.help = "show the block devices",
|
.help = "show info of one block device or all block devices "
|
||||||
|
"(and details of images with -v option)",
|
||||||
.mhandler.cmd = hmp_info_block,
|
.mhandler.cmd = hmp_info_block,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -236,6 +236,8 @@
|
|||||||
#
|
#
|
||||||
# @snapshots: #optional list of VM snapshots
|
# @snapshots: #optional list of VM snapshots
|
||||||
#
|
#
|
||||||
|
# @backing-image: #optional info of the backing image (since 1.6)
|
||||||
|
#
|
||||||
# Since: 1.3
|
# Since: 1.3
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
@ -245,7 +247,8 @@
|
|||||||
'*actual-size': 'int', 'virtual-size': 'int',
|
'*actual-size': 'int', 'virtual-size': 'int',
|
||||||
'*cluster-size': 'int', '*encrypted': 'bool',
|
'*cluster-size': 'int', '*encrypted': 'bool',
|
||||||
'*backing-filename': 'str', '*full-backing-filename': 'str',
|
'*backing-filename': 'str', '*full-backing-filename': 'str',
|
||||||
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'] } }
|
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
|
||||||
|
'*backing-image': 'ImageInfo' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @ImageCheck:
|
# @ImageCheck:
|
||||||
@ -756,6 +759,8 @@
|
|||||||
#
|
#
|
||||||
# @iops_wr: write I/O operations per second is specified
|
# @iops_wr: write I/O operations per second is specified
|
||||||
#
|
#
|
||||||
|
# @image: the info of image used (since: 1.6)
|
||||||
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
#
|
#
|
||||||
# Notes: This interface is only found in @BlockInfo.
|
# Notes: This interface is only found in @BlockInfo.
|
||||||
@ -765,7 +770,8 @@
|
|||||||
'*backing_file': 'str', 'backing_file_depth': 'int',
|
'*backing_file': 'str', 'backing_file_depth': 'int',
|
||||||
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
||||||
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
||||||
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int'} }
|
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
|
||||||
|
'image': 'ImageInfo' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockDeviceIoStatus:
|
# @BlockDeviceIoStatus:
|
||||||
|
20
qemu-img.c
20
qemu-img.c
@ -85,8 +85,9 @@ static void help(void)
|
|||||||
" options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n"
|
" options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n"
|
||||||
" 'directsync' and 'unsafe' (default for convert)\n"
|
" 'directsync' and 'unsafe' (default for convert)\n"
|
||||||
" 'size' is the disk image size in bytes. Optional suffixes\n"
|
" 'size' is the disk image size in bytes. Optional suffixes\n"
|
||||||
" 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n"
|
" 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n"
|
||||||
" and T (terabyte, 1024G) are supported. 'b' is ignored.\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_filename' is the destination disk image filename\n"
|
||||||
" 'output_fmt' is the destination format\n"
|
" 'output_fmt' is the destination format\n"
|
||||||
" 'options' is a comma separated list of format specific options in a\n"
|
" 'options' is a comma separated list of format specific options in a\n"
|
||||||
@ -387,8 +388,9 @@ static int img_create(int argc, char **argv)
|
|||||||
error_report("Image size must be less than 8 EiB!");
|
error_report("Image size must be less than 8 EiB!");
|
||||||
} else {
|
} else {
|
||||||
error_report("Invalid image size specified! You may use k, M, "
|
error_report("Invalid image size specified! You may use k, M, "
|
||||||
"G or T suffixes for ");
|
"G, T, P or E suffixes for ");
|
||||||
error_report("kilobytes, megabytes, gigabytes and terabytes.");
|
error_report("kilobytes, megabytes, gigabytes, terabytes, "
|
||||||
|
"petabytes and exabytes.");
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1642,6 +1644,7 @@ static ImageInfoList *collect_image_info_list(const char *filename,
|
|||||||
ImageInfoList *head = NULL;
|
ImageInfoList *head = NULL;
|
||||||
ImageInfoList **last = &head;
|
ImageInfoList **last = &head;
|
||||||
GHashTable *filenames;
|
GHashTable *filenames;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL);
|
filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL);
|
||||||
|
|
||||||
@ -1663,9 +1666,12 @@ static ImageInfoList *collect_image_info_list(const char *filename,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
info = g_new0(ImageInfo, 1);
|
bdrv_query_image_info(bs, &info, &err);
|
||||||
bdrv_collect_image_info(bs, info, filename);
|
if (error_is_set(&err)) {
|
||||||
bdrv_collect_snapshots(bs, info);
|
error_report("%s", error_get_pretty(err));
|
||||||
|
error_free(err);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
elem = g_new0(ImageInfoList, 1);
|
elem = g_new0(ImageInfoList, 1);
|
||||||
elem->value = info;
|
elem->value = info;
|
||||||
|
2118
qemu-io-cmds.c
Normal file
2118
qemu-io-cmds.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1704,6 +1704,47 @@ Each json-object contain the following:
|
|||||||
- "iops": limit total I/O operations per second (json-int)
|
- "iops": limit total I/O operations per second (json-int)
|
||||||
- "iops_rd": limit read operations per second (json-int)
|
- "iops_rd": limit read operations per second (json-int)
|
||||||
- "iops_wr": limit write operations per second (json-int)
|
- "iops_wr": limit write operations per second (json-int)
|
||||||
|
- "image": the detail of the image, it is a json-object containing
|
||||||
|
the following:
|
||||||
|
- "filename": image file name (json-string)
|
||||||
|
- "format": image format (json-string)
|
||||||
|
- "virtual-size": image capacity in bytes (json-int)
|
||||||
|
- "dirty-flag": true if image is not cleanly closed, not present
|
||||||
|
means clean (json-bool, optional)
|
||||||
|
- "actual-size": actual size on disk in bytes of the image, not
|
||||||
|
present when image does not support thin
|
||||||
|
provision (json-int, optional)
|
||||||
|
- "cluster-size": size of a cluster in bytes, not present if image
|
||||||
|
format does not support it (json-int, optional)
|
||||||
|
- "encrypted": true if the image is encrypted, not present means
|
||||||
|
false or the image format does not support
|
||||||
|
encryption (json-bool, optional)
|
||||||
|
- "backing_file": backing file name, not present means no backing
|
||||||
|
file is used or the image format does not
|
||||||
|
support backing file chain
|
||||||
|
(json-string, optional)
|
||||||
|
- "full-backing-filename": full path of the backing file, not
|
||||||
|
present if it equals backing_file or no
|
||||||
|
backing file is used
|
||||||
|
(json-string, optional)
|
||||||
|
- "backing-filename-format": the format of the backing file, not
|
||||||
|
present means unknown or no backing
|
||||||
|
file (json-string, optional)
|
||||||
|
- "snapshots": the internal snapshot info, it is an optional list
|
||||||
|
of json-object containing the following:
|
||||||
|
- "id": unique snapshot id (json-string)
|
||||||
|
- "name": snapshot name (json-string)
|
||||||
|
- "vm-state-size": size of the VM state in bytes (json-int)
|
||||||
|
- "date-sec": UTC date of the snapshot in seconds (json-int)
|
||||||
|
- "date-nsec": fractional part in nanoseconds to be used with
|
||||||
|
date-sec(json-int)
|
||||||
|
- "vm-clock-sec": VM clock relative to boot in seconds
|
||||||
|
(json-int)
|
||||||
|
- "vm-clock-nsec": fractional part in nanoseconds to be used
|
||||||
|
with vm-clock-sec (json-int)
|
||||||
|
- "backing-image": the detail of the backing image, it is an
|
||||||
|
optional json-object only present when a
|
||||||
|
backing image present for this image
|
||||||
|
|
||||||
- "io-status": I/O operation status, only present if the device supports it
|
- "io-status": I/O operation status, only present if the device supports it
|
||||||
and the VM is configured to stop on errors. It's always reset
|
and the VM is configured to stop on errors. It's always reset
|
||||||
@ -1724,14 +1765,38 @@ Example:
|
|||||||
"ro":false,
|
"ro":false,
|
||||||
"drv":"qcow2",
|
"drv":"qcow2",
|
||||||
"encrypted":false,
|
"encrypted":false,
|
||||||
"file":"disks/test.img",
|
"file":"disks/test.qcow2",
|
||||||
"backing_file_depth":0,
|
"backing_file_depth":1,
|
||||||
"bps":1000000,
|
"bps":1000000,
|
||||||
"bps_rd":0,
|
"bps_rd":0,
|
||||||
"bps_wr":0,
|
"bps_wr":0,
|
||||||
"iops":1000000,
|
"iops":1000000,
|
||||||
"iops_rd":0,
|
"iops_rd":0,
|
||||||
"iops_wr":0,
|
"iops_wr":0,
|
||||||
|
"image":{
|
||||||
|
"filename":"disks/test.qcow2",
|
||||||
|
"format":"qcow2",
|
||||||
|
"virtual-size":2048000,
|
||||||
|
"backing_file":"base.qcow2",
|
||||||
|
"full-backing-filename":"disks/base.qcow2",
|
||||||
|
"backing-filename-format:"qcow2",
|
||||||
|
"snapshots":[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "snapshot1",
|
||||||
|
"vm-state-size": 0,
|
||||||
|
"date-sec": 10000200,
|
||||||
|
"date-nsec": 12,
|
||||||
|
"vm-clock-sec": 206,
|
||||||
|
"vm-clock-nsec": 30
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"backing-image":{
|
||||||
|
"filename":"disks/base.qcow2",
|
||||||
|
"format":"qcow2",
|
||||||
|
"virtual-size":2048000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"type":"unknown"
|
"type":"unknown"
|
||||||
},
|
},
|
||||||
|
@ -64,6 +64,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
DEV = 0x10,
|
||||||
LBA = 0x40,
|
LBA = 0x40,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,6 +77,7 @@ enum {
|
|||||||
enum {
|
enum {
|
||||||
CMD_READ_DMA = 0xc8,
|
CMD_READ_DMA = 0xc8,
|
||||||
CMD_WRITE_DMA = 0xca,
|
CMD_WRITE_DMA = 0xca,
|
||||||
|
CMD_FLUSH_CACHE = 0xe7,
|
||||||
CMD_IDENTIFY = 0xec,
|
CMD_IDENTIFY = 0xec,
|
||||||
|
|
||||||
CMDF_ABORT = 0x100,
|
CMDF_ABORT = 0x100,
|
||||||
@ -394,7 +396,7 @@ static void test_identify(void)
|
|||||||
|
|
||||||
/* Read in the IDENTIFY buffer and check registers */
|
/* Read in the IDENTIFY buffer and check registers */
|
||||||
data = inb(IDE_BASE + reg_device);
|
data = inb(IDE_BASE + reg_device);
|
||||||
g_assert_cmpint(data & 0x10, ==, 0);
|
g_assert_cmpint(data & DEV, ==, 0);
|
||||||
|
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
data = inb(IDE_BASE + reg_status);
|
data = inb(IDE_BASE + reg_status);
|
||||||
@ -423,6 +425,43 @@ static void test_identify(void)
|
|||||||
ide_test_quit();
|
ide_test_quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_flush(void)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
ide_test_start(
|
||||||
|
"-vnc none "
|
||||||
|
"-drive file=blkdebug::%s,if=ide,cache=writeback",
|
||||||
|
tmp_path);
|
||||||
|
|
||||||
|
/* Delay the completion of the flush request until we explicitly do it */
|
||||||
|
qmp("{'execute':'human-monitor-command', 'arguments': { "
|
||||||
|
"'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
|
||||||
|
|
||||||
|
/* FLUSH CACHE command on device 0*/
|
||||||
|
outb(IDE_BASE + reg_device, 0);
|
||||||
|
outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
|
||||||
|
|
||||||
|
/* Check status while request is in flight*/
|
||||||
|
data = inb(IDE_BASE + reg_status);
|
||||||
|
assert_bit_set(data, BSY | DRDY);
|
||||||
|
assert_bit_clear(data, DF | ERR | DRQ);
|
||||||
|
|
||||||
|
/* Complete the command */
|
||||||
|
qmp("{'execute':'human-monitor-command', 'arguments': { "
|
||||||
|
"'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }");
|
||||||
|
|
||||||
|
/* Check registers */
|
||||||
|
data = inb(IDE_BASE + reg_device);
|
||||||
|
g_assert_cmpint(data & DEV, ==, 0);
|
||||||
|
|
||||||
|
data = inb(IDE_BASE + reg_status);
|
||||||
|
assert_bit_set(data, DRDY);
|
||||||
|
assert_bit_clear(data, BSY | DF | ERR | DRQ);
|
||||||
|
|
||||||
|
ide_test_quit();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *arch = qtest_get_arch();
|
const char *arch = qtest_get_arch();
|
||||||
@ -453,6 +492,8 @@ int main(int argc, char **argv)
|
|||||||
qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
|
qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
|
||||||
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
|
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
|
||||||
|
|
||||||
|
qtest_add_func("/ide/flush", test_flush);
|
||||||
|
|
||||||
ret = g_test_run();
|
ret = g_test_run();
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
|
@ -108,15 +108,15 @@ qemu-img: Formatting or formatting option not supported for file format 'qcow2'
|
|||||||
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off
|
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off
|
||||||
|
|
||||||
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
|
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
|
||||||
qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for
|
qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
|
||||||
qemu-img: kilobytes, megabytes, gigabytes and terabytes.
|
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||||
|
|
||||||
qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2
|
qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2
|
||||||
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
|
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off
|
||||||
|
|
||||||
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar
|
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar
|
||||||
qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for
|
qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
|
||||||
qemu-img: kilobytes, megabytes, gigabytes and terabytes.
|
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||||
|
|
||||||
qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
|
qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
|
||||||
qemu-img: Parameter 'size' expects a size
|
qemu-img: Parameter 'size' expects a size
|
||||||
|
@ -107,6 +107,27 @@ int qemu_strnlen(const char *s, int max_len)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *qemu_strsep(char **input, const char *delim)
|
||||||
|
{
|
||||||
|
char *result = *input;
|
||||||
|
if (result != NULL) {
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
for (p = result; *p != '\0'; p++) {
|
||||||
|
if (strchr(delim, *p)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*p == '\0') {
|
||||||
|
*input = NULL;
|
||||||
|
} else {
|
||||||
|
*p = '\0';
|
||||||
|
*input = p + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
time_t mktimegm(struct tm *tm)
|
time_t mktimegm(struct tm *tm)
|
||||||
{
|
{
|
||||||
time_t t;
|
time_t t;
|
||||||
@ -267,6 +288,10 @@ static int64_t suffix_mul(char suffix, int64_t unit)
|
|||||||
return unit * unit * unit;
|
return unit * unit * unit;
|
||||||
case STRTOSZ_DEFSUFFIX_TB:
|
case STRTOSZ_DEFSUFFIX_TB:
|
||||||
return unit * unit * unit * unit;
|
return unit * unit * unit * unit;
|
||||||
|
case STRTOSZ_DEFSUFFIX_PB:
|
||||||
|
return unit * unit * unit * unit * unit;
|
||||||
|
case STRTOSZ_DEFSUFFIX_EB:
|
||||||
|
return unit * unit * unit * unit * unit * unit;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user