sim-card
/
qemu
Archived
10
0
Fork 0

Merge remote-tracking branch 'qmp/queue/qmp' into staging

This commit is contained in:
Anthony Liguori 2011-12-12 10:08:08 -06:00
commit 9bf4896e5d
28 changed files with 2153 additions and 716 deletions

View File

@ -169,7 +169,7 @@ test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y)
$(qapi-obj-y): $(GENERATED_HEADERS)
qapi-dir := $(BUILD_DIR)/qapi-generated
test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
test-qmp-input-visitor.o test-qmp-output-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
qemu-ga$(EXESUF): LIBS = $(LIBS_QGA)
$(qapi-dir)/test-qapi-types.c $(qapi-dir)/test-qapi-types.h :\
@ -202,8 +202,11 @@ qmp-commands.h qmp-marshal.c :\
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -m -o "." < $<, " GEN $@")
test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
test-visitor: test-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
test-qmp-output-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
test-qmp-output-visitor: test-qmp-output-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
test-qmp-input-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
test-qmp-input-visitor: test-qmp-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o

126
QMP/qmp Executable file
View File

@ -0,0 +1,126 @@
#!/usr/bin/python
#
# QMP command line tool
#
# Copyright IBM, Corp. 2011
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
#
# This work is licensed under the terms of the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import sys, os
from qmp import QEMUMonitorProtocol
def print_response(rsp, prefix=[]):
if type(rsp) == list:
i = 0
for item in rsp:
if prefix == []:
prefix = ['item']
print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
i += 1
elif type(rsp) == dict:
for key in rsp.keys():
print_response(rsp[key], prefix + [key])
else:
if len(prefix):
print '%s: %s' % ('.'.join(prefix), rsp)
else:
print '%s' % (rsp)
def main(args):
path = None
# Use QMP_PATH if it's set
if os.environ.has_key('QMP_PATH'):
path = os.environ['QMP_PATH']
while len(args):
arg = args[0]
if arg.startswith('--'):
arg = arg[2:]
if arg.find('=') == -1:
value = True
else:
arg, value = arg.split('=', 1)
if arg in ['path']:
if type(value) == str:
path = value
elif arg in ['help']:
os.execlp('man', 'man', 'qmp')
else:
print 'Unknown argument "%s"' % arg
args = args[1:]
else:
break
if not path:
print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
return 1
if len(args):
command, args = args[0], args[1:]
else:
print 'No command found'
print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
return 1
if command in ['help']:
os.execlp('man', 'man', 'qmp')
srv = QEMUMonitorProtocol(path)
srv.connect()
def do_command(srv, cmd, **kwds):
rsp = srv.cmd(cmd, kwds)
if rsp.has_key('error'):
raise Exception(rsp['error']['desc'])
return rsp['return']
commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
srv.close()
if command not in commands:
fullcmd = 'qmp-%s' % command
try:
os.environ['QMP_PATH'] = path
os.execvp(fullcmd, [fullcmd] + args)
except OSError, (errno, msg):
if errno == 2:
print 'Command "%s" not found.' % (fullcmd)
return 1
raise
return 0
srv = QEMUMonitorProtocol(path)
srv.connect()
arguments = {}
for arg in args:
if not arg.startswith('--'):
print 'Unknown argument "%s"' % arg
return 1
arg = arg[2:]
if arg.find('=') == -1:
value = True
else:
arg, value = arg.split('=', 1)
if arg in ['help']:
os.execlp('man', 'man', 'qmp-%s' % command)
return 1
arguments[arg] = value
rsp = do_command(srv, command, **arguments)
print_response(rsp)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -100,31 +100,19 @@ BalloonInfo *qmp_query_balloon(Error **errp)
return info;
}
/**
* do_balloon(): Request VM to change its memory allocation
*/
int do_balloon(Monitor *mon, const QDict *params,
MonitorCompletion cb, void *opaque)
void qmp_balloon(int64_t value, Error **errp)
{
int64_t target;
int ret;
if (kvm_enabled() && !kvm_has_sync_mmu()) {
qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
return -1;
error_set(errp, QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
return;
}
target = qdict_get_int(params, "value");
if (target <= 0) {
if (value <= 0) {
qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size");
return -1;
return;
}
ret = qemu_balloon(target);
if (ret == 0) {
qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
return -1;
if (qemu_balloon(value) == 0) {
error_set(errp, QERR_DEVICE_NOT_ACTIVE, "balloon");
}
cb(opaque, NULL);
return 0;
}

View File

@ -24,7 +24,4 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
QEMUBalloonStatus *stat_func, void *opaque);
void qemu_remove_balloon_handler(void *opaque);
int do_balloon(Monitor *mon, const QDict *params,
MonitorCompletion cb, void *opaque);
#endif

View File

@ -15,6 +15,7 @@
#include "qemu-config.h"
#include "sysemu.h"
#include "block_int.h"
#include "qmp-commands.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
@ -600,28 +601,20 @@ void do_commit(Monitor *mon, const QDict *qdict)
}
}
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
bool has_format, const char *format,
Error **errp)
{
const char *device = qdict_get_str(qdict, "device");
const char *filename = qdict_get_try_str(qdict, "snapshot-file");
const char *format = qdict_get_try_str(qdict, "format");
BlockDriverState *bs;
BlockDriver *drv, *old_drv, *proto_drv;
int ret = 0;
int flags;
char old_filename[1024];
if (!filename) {
qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
ret = -1;
goto out;
}
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
ret = -1;
goto out;
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
pstrcpy(old_filename, sizeof(old_filename), bs->filename);
@ -629,35 +622,34 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
old_drv = bs->drv;
flags = bs->open_flags;
if (!format) {
if (!has_format) {
format = "qcow2";
}
drv = bdrv_find_format(format);
if (!drv) {
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
ret = -1;
goto out;
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
}
proto_drv = bdrv_find_protocol(filename);
proto_drv = bdrv_find_protocol(snapshot_file);
if (!proto_drv) {
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
ret = -1;
goto out;
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
}
ret = bdrv_img_create(filename, format, bs->filename,
ret = bdrv_img_create(snapshot_file, format, bs->filename,
bs->drv->format_name, NULL, -1, flags);
if (ret) {
goto out;
error_set(errp, QERR_UNDEFINED_ERROR);
return;
}
bdrv_drain_all();
bdrv_flush(bs);
bdrv_close(bs);
ret = bdrv_open(bs, filename, flags, drv);
ret = bdrv_open(bs, snapshot_file, flags, drv);
/*
* If reopening the image file we just created fails, fall back
* and try to re-open the original image. If that fails too, we
@ -666,17 +658,11 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
if (ret != 0) {
ret = bdrv_open(bs, old_filename, flags, old_drv);
if (ret != 0) {
qerror_report(QERR_OPEN_FILE_FAILED, old_filename);
error_set(errp, QERR_OPEN_FILE_FAILED, old_filename);
} else {
qerror_report(QERR_OPEN_FILE_FAILED, filename);
error_set(errp, QERR_OPEN_FILE_FAILED, snapshot_file);
}
}
out:
if (ret) {
ret = -1;
}
return ret;
}
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
@ -710,28 +696,25 @@ int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
return eject_device(mon, bs, force);
}
int do_block_set_passwd(Monitor *mon, const QDict *qdict,
QObject **ret_data)
void qmp_block_passwd(const char *device, const char *password, Error **errp)
{
BlockDriverState *bs;
int err;
bs = bdrv_find(qdict_get_str(qdict, "device"));
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
return -1;
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
err = bdrv_set_key(bs, qdict_get_str(qdict, "password"));
err = bdrv_set_key(bs, password);
if (err == -EINVAL) {
qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
return -1;
error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
return;
} else if (err < 0) {
qerror_report(QERR_INVALID_PASSWORD);
return -1;
error_set(errp, QERR_INVALID_PASSWORD);
return;
}
return 0;
}
int do_change_block(Monitor *mon, const char *device,
@ -863,27 +846,23 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
* existing QERR_ macro mess is cleaned up. A good example for better
* error reports can be found in the qemu-img resize code.
*/
int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data)
void qmp_block_resize(const char *device, int64_t size, Error **errp)
{
const char *device = qdict_get_str(qdict, "device");
int64_t size = qdict_get_int(qdict, "size");
BlockDriverState *bs;
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
return -1;
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (size < 0) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
error_set(errp, QERR_UNDEFINED_ERROR);
return;
}
if (bdrv_truncate(bs, size)) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
error_set(errp, QERR_UNDEFINED_ERROR);
return;
}
return 0;
}

View File

@ -59,13 +59,9 @@ DriveInfo *add_init_drive(const char *opts);
void do_commit(Monitor *mon, const QDict *qdict);
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_change_block(Monitor *mon, const char *device,
const char *filename, const char *fmt);
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_block_set_io_throttle(Monitor *mon,
const QDict *qdict, QObject **ret_data);
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data);
#endif

5
configure vendored
View File

@ -2740,8 +2740,9 @@ if test "$softmmu" = yes ; then
fi
if [ "$check_utests" = "yes" ]; then
checks="check-qint check-qstring check-qdict check-qlist"
checks="check-qfloat check-qjson test-coroutine $checks"
checks="check-qfloat check-qjson $checks"
fi
test_progs="$checks test-coroutine test-qmp-output-visitor test-qmp-input-visitor"
fi
fi
@ -3227,7 +3228,7 @@ if test "$trace_default" = "yes"; then
fi
echo "TOOLS=$tools" >> $config_host_mak
echo "CHECKS=$checks" >> $config_host_mak
echo "CHECKS=$test_progs" >> $config_host_mak
echo "ROMS=$roms" >> $config_host_mak
echo "MAKE=$make" >> $config_host_mak
echo "INSTALL=$install" >> $config_host_mak

View File

@ -74,8 +74,6 @@ struct MouseTransformInfo {
int a[7];
};
void do_info_mice_print(Monitor *mon, const QObject *data);
void do_info_mice(Monitor *mon, QObject **ret_data);
void do_mouse_set(Monitor *mon, const QDict *qdict);
/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx

90
cpus.c
View File

@ -1136,3 +1136,93 @@ CpuInfoList *qmp_query_cpus(Error **errp)
return head;
}
void qmp_memsave(int64_t addr, int64_t size, const char *filename,
bool has_cpu, int64_t cpu_index, Error **errp)
{
FILE *f;
uint32_t l;
CPUState *env;
uint8_t buf[1024];
if (!has_cpu) {
cpu_index = 0;
}
for (env = first_cpu; env; env = env->next_cpu) {
if (cpu_index == env->cpu_index) {
break;
}
}
if (env == NULL) {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
"a CPU number");
return;
}
f = fopen(filename, "wb");
if (!f) {
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
return;
}
while (size != 0) {
l = sizeof(buf);
if (l > size)
l = size;
cpu_memory_rw_debug(env, addr, buf, l, 0);
if (fwrite(buf, 1, l, f) != l) {
error_set(errp, QERR_IO_ERROR);
goto exit;
}
addr += l;
size -= l;
}
exit:
fclose(f);
}
void qmp_pmemsave(int64_t addr, int64_t size, const char *filename,
Error **errp)
{
FILE *f;
uint32_t l;
uint8_t buf[1024];
f = fopen(filename, "wb");
if (!f) {
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
return;
}
while (size != 0) {
l = sizeof(buf);
if (l > size)
l = size;
cpu_physical_memory_rw(addr, buf, l, 0);
if (fwrite(buf, 1, l, f) != l) {
error_set(errp, QERR_IO_ERROR);
goto exit;
}
addr += l;
size -= l;
}
exit:
fclose(f);
}
void qmp_inject_nmi(Error **errp)
{
#if defined(TARGET_I386)
CPUState *env;
for (env = first_cpu; env != NULL; env = env->next_cpu) {
cpu_interrupt(env, CPU_INTERRUPT_NMI);
}
#else
error_set(errp, QERR_UNSUPPORTED);
#endif
}

View File

@ -0,0 +1,642 @@
= How to write QMP commands using the QAPI framework =
This document is a step-by-step guide on how to write new QMP commands using
the QAPI framework. It also shows how to implement new style HMP commands.
This document doesn't discuss QMP protocol level details, nor does it dive
into the QAPI framework implementation.
For an in-depth introduction to the QAPI framework, please refer to
docs/qapi-code-gen.txt. For documentation about the QMP protocol, please
check the files in QMP/.
== Overview ==
Generally speaking, the following steps should be taken in order to write a
new QMP command.
1. Write the command's and type(s) specification in the QAPI schema file
(qapi-schema.json in the root source directory)
2. Write the QMP command itself, which is a regular C function. Preferably,
the command should be exported by some QEMU subsystem. But it can also be
added to the qmp.c file
3. At this point the command can be tested under the QMP protocol
4. Write the HMP command equivalent. This is not required and should only be
done if it does make sense to have the functionality in HMP. The HMP command
is implemented in terms of the QMP command
The following sections will demonstrate each of the steps above. We will start
very simple and get more complex as we progress.
=== Testing ===
For all the examples in the next sections, the test setup is the same and is
shown here.
First, QEMU should be started as:
# /path/to/your/source/qemu [...] \
-chardev socket,id=qmp,port=4444,host=localhost,server \
-mon chardev=qmp,mode=control,pretty=on
Then, in a different terminal:
$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
{
"QMP": {
"version": {
"qemu": {
"micro": 50,
"minor": 15,
"major": 0
},
"package": ""
},
"capabilities": [
]
}
}
The above output is the QMP server saying you're connected. The server is
actually in capabilities negotiation mode. To enter in command mode type:
{ "execute": "qmp_capabilities" }
Then the server should respond:
{
"return": {
}
}
Which is QMP's way of saying "the latest command executed OK and didn't return
any data". Now you're ready to enter the QMP example commands as explained in
the following sections.
== Writing a command that doesn't return data ==
That's the most simple QMP command that can be written. Usually, this kind of
command carries some meaningful action in QEMU but here it will just print
"Hello, world" to the standard output.
Our command will be called "hello-world". It takes no arguments, nor does it
return any data.
The first step is to add the following line to the bottom of the
qapi-schema.json file:
{ 'command': 'hello-world' }
The "command" keyword defines a new QMP command. It's an JSON object. All
schema entries are JSON objects. The line above will instruct the QAPI to
generate any prototypes and the necessary code to marshal and unmarshal
protocol data.
The next step is to write the "hello-world" implementation. As explained
earlier, it's preferable for commands to live in QEMU subsystems. But
"hello-world" doesn't pertain to any, so we put its implementation in qmp.c:
void qmp_hello_world(Error **errp)
{
printf("Hello, world!\n");
}
There are a few things to be noticed:
1. QMP command implementation functions must be prefixed with "qmp_"
2. qmp_hello_world() returns void, this is in accordance with the fact that the
command doesn't return any data
3. It takes an "Error **" argument. This is required. Later we will see how to
return errors and take additional arguments. The Error argument should not
be touched if the command doesn't return errors
4. We won't add the function's prototype. That's automatically done by the QAPI
5. Printing to the terminal is discouraged for QMP commands, we do it here
because it's the easiest way to demonstrate a QMP command
Now a little hack is needed. As we're still using the old QMP server we need
to add the new command to its internal dispatch table. This step won't be
required in the near future. Open the qmp-commands.hx file and add the
following in the botton:
{
.name = "hello-world",
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_hello_world,
},
You're done. Now build qemu, run it as suggested in the "Testing" section,
and then type the following QMP command:
{ "execute": "hello-world" }
Then check the terminal running qemu and look for the "Hello, world" string. If
you don't see it then something went wrong.
=== Arguments ===
Let's add an argument called "message" to our "hello-world" command. The new
argument will contain the string to be printed to stdout. It's an optional
argument, if it's not present we print our default "Hello, World" string.
The first change we have to do is to modify the command specification in the
schema file to the following:
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
Notice the new 'data' member in the schema. It's an JSON object whose each
element is an argument to the command in question. Also notice the asterisk,
it's used to mark the argument optional (that means that you shouldn't use it
for mandatory arguments). Finally, 'str' is the argument's type, which
stands for "string". The QAPI also supports integers, booleans, enumerations
and user defined types.
Now, let's update our C implementation in qmp.c:
void qmp_hello_world(bool has_message, const char *message, Error **errp)
{
if (has_message) {
printf("%s\n", message);
} else {
printf("Hello, world\n");
}
}
There are two important details to be noticed:
1. All optional arguments are accompanied by a 'has_' boolean, which is set
if the optional argument is present or false otherwise
2. The C implementation signature must follow the schema's argument ordering,
which is defined by the "data" member
The last step is to update the qmp-commands.hx file:
{
.name = "hello-world",
.args_type = "message:s?",
.mhandler.cmd_new = qmp_marshal_input_hello_world,
},
Notice that the "args_type" member got our "message" argument. The character
"s" stands for "string" and "?" means it's optional. This too must be ordered
according to the C implementation and schema file. You can look for more
examples in the qmp-commands.hx file if you need to define more arguments.
Again, this step won't be required in the future.
Time to test our new version of the "hello-world" command. Build qemu, run it as
described in the "Testing" section and then send two commands:
{ "execute": "hello-world" }
{
"return": {
}
}
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }
{
"return": {
}
}
You should see "Hello, world" and "we love qemu" in the terminal running qemu,
if you don't see these strings, then something went wrong.
=== Errors ===
QMP commands should use the error interface exported by the error.h header
file. The basic function used to set an error is the error_set() one.
Let's say we don't accept the string "message" to contain the word "love". If
it does contain it, we want the "hello-world" command to the return the
InvalidParameter error.
Only one change is required, and it's in the C implementation:
void qmp_hello_world(bool has_message, const char *message, Error **errp)
{
if (has_message) {
if (strstr(message, "love")) {
error_set(errp, QERR_INVALID_PARAMETER, "message");
return;
}
printf("%s\n", message);
} else {
printf("Hello, world\n");
}
}
Let's test it. Build qemu, run it as defined in the "Testing" section, and
then issue the following command:
{ "execute": "hello-world", "arguments": { "message": "we love qemu" } }
The QMP server's response should be:
{
"error": {
"class": "InvalidParameter",
"desc": "Invalid parameter 'message'",
"data": {
"name": "message"
}
}
}
Which is the InvalidParameter error.
When you have to return an error but you're unsure what error to return or
which arguments an error takes, you should look at the qerror.h file. Note
that you might be required to add new errors if needed.
FIXME: describe better the error API and how to add new errors.
=== Command Documentation ===
There's only one step missing to make "hello-world"'s implementation complete,
and that's its documentation in the schema file.
This is very important. No QMP command will be accepted in QEMU without proper
documentation.
There are many examples of such documentation in the schema file already, but
here goes "hello-world"'s new entry for the qapi-schema.json file:
##
# @hello-world
#
# Print a client provided string to the standard output stream.
#
# @message: #optional string to be printed
#
# Returns: Nothing on success.
# If @message contains "love", InvalidParameter
#
# Notes: if @message is not provided, the "Hello, world" string will
# be printed instead
#
# Since: <next qemu stable release, eg. 1.0>
##
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
Please, note that the "Returns" clause is optional if a command doesn't return
any data nor any errors.
=== Implementing the HMP command ===
Now that the QMP command is in place, we can also make it available in the human
monitor (HMP).
With the introduction of the QAPI, HMP commands make QMP calls. Most of the
time HMP commands are simple wrappers. All HMP commands implementation exist in
the hmp.c file.
Here's the implementation of the "hello-world" HMP command:
void hmp_hello_world(Monitor *mon, const QDict *qdict)
{
const char *message = qdict_get_try_str(qdict, "message");
Error *errp = NULL;
qmp_hello_world(!!message, message, &errp);
if (error_is_set(&errp)) {
monitor_printf(mon, "%s\n", error_get_pretty(errp));
error_free(errp);
return;
}
}
Also, you have to add the function's prototype to the hmp.h file.
There are three important points to be noticed:
1. The "mon" and "qdict" arguments are mandatory for all HMP functions. The
former is the monitor object. The latter is how the monitor passes
arguments entered by the user to the command implementation
2. hmp_hello_world() performs error checking. In this example we just print
the error description to the user, but we could do more, like taking
different actions depending on the error qmp_hello_world() returns
3. The "errp" variable must be initialized to NULL before performing the
QMP call
There's one last step to actually make the command available to monitor users,
we should add it to the hmp-commands.hx file:
{
.name = "hello-world",
.args_type = "message:s?",
.params = "hello-world [message]",
.help = "Print message to the standard output",
.mhandler.cmd = hmp_hello_world,
},
STEXI
@item hello_world @var{message}
@findex hello_world
Print message to the standard output
ETEXI
To test this you have to open a user monitor and issue the "hello-world"
command. It might be instructive to check the command's documentation with
HMP's "help" command.
Please, check the "-monitor" command-line option to know how to open a user
monitor.
== Writing a command that returns data ==
A QMP command is capable of returning any data the QAPI supports like integers,
strings, booleans, enumerations and user defined types.
In this section we will focus on user defined types. Please, check the QAPI
documentation for information about the other types.
=== User Defined Types ===
For this example we will write the query-alarm-clock command, which returns
information about QEMU's timer alarm. For more information about it, please
check the "-clock" command-line option.
We want to return two pieces of information. The first one is the alarm clock's
name. The second one is when the next alarm will fire. The former information is
returned as a string, the latter is an integer in nanoseconds (which is not
very useful in practice, as the timer has probably already fired when the
information reaches the client).
The best way to return that data is to create a new QAPI type, as shown below:
##
# @QemuAlarmClock
#
# QEMU alarm clock information.
#
# @clock-name: The alarm clock method's name.
#
# @next-deadline: #optional The time (in nanoseconds) the next alarm will fire.
#
# Since: 1.0
##
{ 'type': 'QemuAlarmClock',
'data': { 'clock-name': 'str', '*next-deadline': 'int' } }
The "type" keyword defines a new QAPI type. Its "data" member contains the
type's members. In this example our members are the "clock-name" and the
"next-deadline" one, which is optional.
Now let's define the query-alarm-clock command:
##
# @query-alarm-clock
#
# Return information about QEMU's alarm clock.
#
# Returns a @QemuAlarmClock instance describing the alarm clock method
# being currently used by QEMU (this is usually set by the '-clock'
# command-line option).
#
# Since: 1.0
##
{ 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' }
Notice the "returns" keyword. As its name suggests, it's used to define the
data returned by a command.
It's time to implement the qmp_query_alarm_clock() function, you can put it
in the qemu-timer.c file:
QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
{
QemuAlarmClock *clock;
int64_t deadline;
clock = g_malloc0(sizeof(*clock));
deadline = qemu_next_alarm_deadline();
if (deadline > 0) {
clock->has_next_deadline = true;
clock->next_deadline = deadline;
}
clock->clock_name = g_strdup(alarm_timer->name);
return clock;
}
There are a number of things to be noticed:
1. The QemuAlarmClock type is automatically generated by the QAPI framework,
its members correspond to the type's specification in the schema file
2. As specified in the schema file, the function returns a QemuAlarmClock
instance and takes no arguments (besides the "errp" one, which is mandatory
for all QMP functions)
3. The "clock" variable (which will point to our QAPI type instance) is
allocated by the regular g_malloc0() function. Note that we chose to
initialize the memory to zero. This is recomended for all QAPI types, as
it helps avoiding bad surprises (specially with booleans)
4. Remember that "next_deadline" is optional? All optional members have a
'has_TYPE_NAME' member that should be properly set by the implementation,
as shown above
5. Even static strings, such as "alarm_timer->name", should be dynamically
allocated by the implementation. This is so because the QAPI also generates
a function to free its types and it cannot distinguish between dynamically
or statically allocated strings
6. You have to include the "qmp-commands.h" header file in qemu-timer.c,
otherwise qemu won't build
The last step is to add the correspoding entry in the qmp-commands.hx file:
{
.name = "query-alarm-clock",
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_query_alarm_clock,
},
Time to test the new command. Build qemu, run it as described in the "Testing"
section and try this:
{ "execute": "query-alarm-clock" }
{
"return": {
"next-deadline": 2368219,
"clock-name": "dynticks"
}
}
==== The HMP command ====
Here's the HMP counterpart of the query-alarm-clock command:
void hmp_info_alarm_clock(Monitor *mon)
{
QemuAlarmClock *clock;
Error *errp = NULL;
clock = qmp_query_alarm_clock(&errp);
if (error_is_set(&errp)) {
monitor_printf(mon, "Could not query alarm clock information\n");
error_free(errp);
return;
}
monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock_name);
if (clock->has_next_deadline) {
monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseconds\n",
clock->next_deadline);
}
qapi_free_QemuAlarmClock(clock);
}
It's important to notice that hmp_info_alarm_clock() calls
qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock().
For user defined types, the QAPI will generate a qapi_free_QAPI_TYPE_NAME()
function and that's what you have to use to free the types you define and
qapi_free_QAPI_TYPE_NAMEList() for list types (explained in the next section).
If the QMP call returns a string, then you should g_free() to free it.
Also note that hmp_info_alarm_clock() performs error handling. That's not
strictly required if you're sure the QMP function doesn't return errors, but
it's good practice to always check for errors.
Another important detail is that HMP's "info" commands don't go into the
hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined
in the monitor.c file. The entry for the "info alarmclock" follows:
{
.name = "alarmclock",
.args_type = "",
.params = "",
.help = "show information about the alarm clock",
.mhandler.info = hmp_info_alarm_clock,
},
To test this, run qemu and type "info alarmclock" in the user monitor.
=== Returning Lists ===
For this example, we're going to return all available methods for the timer
alarm, which is pretty much what the command-line option "-clock ?" does,
except that we're also going to inform which method is in use.
This first step is to define a new type:
##
# @TimerAlarmMethod
#
# Timer alarm method information.
#
# @method-name: The method's name.
#
# @current: true if this alarm method is currently in use, false otherwise
#
# Since: 1.0
##
{ 'type': 'TimerAlarmMethod',
'data': { 'method-name': 'str', 'current': 'bool' } }
The command will be called "query-alarm-methods", here is its schema
specification:
##
# @query-alarm-methods
#
# Returns information about available alarm methods.
#
# Returns: a list of @TimerAlarmMethod for each method
#
# Since: 1.0
##
{ 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] }
Notice the syntax for returning lists "'returns': ['TimerAlarmMethod']", this
should be read as "returns a list of TimerAlarmMethod instances".
The C implementation follows:
TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
{
TimerAlarmMethodList *method_list = NULL;
const struct qemu_alarm_timer *p;
bool current = true;
for (p = alarm_timers; p->name; p++) {
TimerAlarmMethodList *info = g_malloc0(sizeof(*info));
info->value = g_malloc0(sizeof(*info->value));
info->value->method_name = g_strdup(p->name);
info->value->current = current;
current = false;
info->next = method_list;
method_list = info;
}
return method_list;
}
The most important difference from the previous examples is the
TimerAlarmMethodList type, which is automatically generated by the QAPI from
the TimerAlarmMethod type.
Each list node is represented by a TimerAlarmMethodList instance. We have to
allocate it, and that's done inside the for loop: the "info" pointer points to
an allocated node. We also have to allocate the node's contents, which is
stored in its "value" member. In our example, the "value" member is a pointer
to an TimerAlarmMethod instance.
Notice that the "current" variable is used as "true" only in the first
interation of the loop. That's because the alarm timer method in use is the
first element of the alarm_timers array. Also notice that QAPI lists are handled
by hand and we return the head of the list.
To test this you have to add the corresponding qmp-commands.hx entry:
{
.name = "query-alarm-methods",
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_query_alarm_methods,
},
Now Build qemu, run it as explained in the "Testing" section and try our new
command:
{ "execute": "query-alarm-methods" }
{
"return": [
{
"current": false,
"method-name": "unix"
},
{
"current": true,
"method-name": "dynticks"
}
]
}
The HMP counterpart is a bit more complex than previous examples because it
has to traverse the list, it's shown below for reference:
void hmp_info_alarm_methods(Monitor *mon)
{
TimerAlarmMethodList *method_list, *method;
Error *errp = NULL;
method_list = qmp_query_alarm_methods(&errp);
if (error_is_set(&errp)) {
monitor_printf(mon, "Could not query alarm methods\n");
error_free(errp);
return;
}
for (method = method_list; method; method = method->next) {
monitor_printf(mon, "%c %s\n", method->value->current ? '*' : ' ',
method->value->method_name);
}
qapi_free_TimerAlarmMethodList(method_list);
}

View File

@ -57,8 +57,7 @@ ETEXI
.args_type = "device:B,size:o",
.params = "device size",
.help = "resize a block image",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_block_resize,
.mhandler.cmd = hmp_block_resize,
},
STEXI
@ -304,8 +303,7 @@ ETEXI
.args_type = "",
.params = "",
.help = "resume emulation",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_cont,
.mhandler.cmd = hmp_cont,
},
STEXI
@ -689,8 +687,7 @@ ETEXI
.args_type = "val:l,size:i,filename:s",
.params = "addr size file",
.help = "save to disk virtual memory dump starting at 'addr' of size 'size'",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_memory_save,
.mhandler.cmd = hmp_memsave,
},
STEXI
@ -704,8 +701,7 @@ ETEXI
.args_type = "val:l,size:i,filename:s",
.params = "addr size file",
.help = "save to disk physical memory dump starting at 'addr' of size 'size'",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_physical_memory_save,
.mhandler.cmd = hmp_pmemsave,
},
STEXI
@ -739,8 +735,7 @@ ETEXI
.args_type = "",
.params = "",
.help = "inject an NMI on all guest's CPUs",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_inject_nmi,
.mhandler.cmd = hmp_inject_nmi,
},
#endif
STEXI
@ -776,8 +771,7 @@ ETEXI
.args_type = "",
.params = "",
.help = "cancel the current VM migration",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_migrate_cancel,
.mhandler.cmd = hmp_migrate_cancel,
},
STEXI
@ -792,8 +786,7 @@ ETEXI
.params = "value",
.help = "set maximum speed (in bytes) for migrations. "
"Defaults to MB if no size suffix is specified, ie. B/K/M/G/T",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_migrate_set_speed,
.mhandler.cmd = hmp_migrate_set_speed,
},
STEXI
@ -807,8 +800,7 @@ ETEXI
.args_type = "value:T",
.params = "value",
.help = "set maximum tolerated downtime (in seconds) for migrations",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_migrate_set_downtime,
.mhandler.cmd = hmp_migrate_set_downtime,
},
STEXI
@ -845,7 +837,7 @@ ETEXI
"If format is specified, the snapshot file will\n\t\t\t"
"be created in that format. Otherwise the\n\t\t\t"
"snapshot will be internal! (currently unsupported)",
.mhandler.cmd_new = do_snapshot_blkdev,
.mhandler.cmd = hmp_snapshot_blkdev,
},
STEXI
@ -1026,9 +1018,7 @@ ETEXI
.args_type = "value:M",
.params = "target",
.help = "request VM to change its memory allocation (in MB)",
.user_print = monitor_user_noop,
.mhandler.cmd_async = do_balloon,
.flags = MONITOR_CMD_ASYNC,
.mhandler.cmd = hmp_balloon,
},
STEXI
@ -1042,8 +1032,7 @@ ETEXI
.args_type = "name:s,up:b",
.params = "name on|off",
.help = "change the link status of a network adapter",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_set_link,
.mhandler.cmd = hmp_set_link,
},
STEXI
@ -1203,8 +1192,7 @@ ETEXI
.args_type = "device:B,password:s",
.params = "block_passwd device password",
.help = "set the password of encrypted block devices",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_block_set_passwd,
.mhandler.cmd = hmp_block_passwd,
},
STEXI

148
hmp.c
View File

@ -14,6 +14,14 @@
#include "hmp.h"
#include "qmp-commands.h"
static void hmp_handle_error(Monitor *mon, Error **errp)
{
if (error_is_set(errp)) {
monitor_printf(mon, "%s\n", error_get_pretty(*errp));
error_free(*errp);
}
}
void hmp_info_name(Monitor *mon)
{
NameInfo *info;
@ -531,3 +539,143 @@ void hmp_cpu(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "invalid CPU index\n");
}
}
void hmp_memsave(Monitor *mon, const QDict *qdict)
{
uint32_t size = qdict_get_int(qdict, "size");
const char *filename = qdict_get_str(qdict, "filename");
uint64_t addr = qdict_get_int(qdict, "val");
Error *errp = NULL;
qmp_memsave(addr, size, filename, true, monitor_get_cpu_index(), &errp);
hmp_handle_error(mon, &errp);
}
void hmp_pmemsave(Monitor *mon, const QDict *qdict)
{
uint32_t size = qdict_get_int(qdict, "size");
const char *filename = qdict_get_str(qdict, "filename");
uint64_t addr = qdict_get_int(qdict, "val");
Error *errp = NULL;
qmp_pmemsave(addr, size, filename, &errp);
hmp_handle_error(mon, &errp);
}
static void hmp_cont_cb(void *opaque, int err)
{
Monitor *mon = opaque;
if (!err) {
hmp_cont(mon, NULL);
}
}
void hmp_cont(Monitor *mon, const QDict *qdict)
{
Error *errp = NULL;
qmp_cont(&errp);
if (error_is_set(&errp)) {
if (error_is_type(errp, QERR_DEVICE_ENCRYPTED)) {
const char *device;
/* The device is encrypted. Ask the user for the password
and retry */
device = error_get_field(errp, "device");
assert(device != NULL);
monitor_read_block_device_key(mon, device, hmp_cont_cb, mon);
error_free(errp);
return;
}
hmp_handle_error(mon, &errp);
}
}
void hmp_inject_nmi(Monitor *mon, const QDict *qdict)
{
Error *errp = NULL;
qmp_inject_nmi(&errp);
hmp_handle_error(mon, &errp);
}
void hmp_set_link(Monitor *mon, const QDict *qdict)
{
const char *name = qdict_get_str(qdict, "name");
int up = qdict_get_bool(qdict, "up");
Error *errp = NULL;
qmp_set_link(name, up, &errp);
hmp_handle_error(mon, &errp);
}
void hmp_block_passwd(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *password = qdict_get_str(qdict, "password");
Error *errp = NULL;
qmp_block_passwd(device, password, &errp);
hmp_handle_error(mon, &errp);
}
void hmp_balloon(Monitor *mon, const QDict *qdict)
{
int64_t value = qdict_get_int(qdict, "value");
Error *errp = NULL;
qmp_balloon(value, &errp);
if (error_is_set(&errp)) {
monitor_printf(mon, "balloon: %s\n", error_get_pretty(errp));
error_free(errp);
}
}
void hmp_block_resize(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
int64_t size = qdict_get_int(qdict, "size");
Error *errp = NULL;
qmp_block_resize(device, size, &errp);
hmp_handle_error(mon, &errp);
}
void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *filename = qdict_get_try_str(qdict, "snapshot-file");
const char *format = qdict_get_try_str(qdict, "format");
Error *errp = NULL;
if (!filename) {
/* In the future, if 'snapshot-file' is not specified, the snapshot
will be taken internally. Today it's actually required. */
error_set(&errp, QERR_MISSING_PARAMETER, "snapshot-file");
hmp_handle_error(mon, &errp);
return;
}
qmp_blockdev_snapshot_sync(device, filename, !!format, format, &errp);
hmp_handle_error(mon, &errp);
}
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
{
qmp_migrate_cancel(NULL);
}
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict)
{
double value = qdict_get_double(qdict, "value");
qmp_migrate_set_downtime(value, NULL);
}
void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
{
int64_t value = qdict_get_int(qdict, "value");
qmp_migrate_set_speed(value, NULL);
}

12
hmp.h
View File

@ -37,5 +37,17 @@ void hmp_stop(Monitor *mon, const QDict *qdict);
void hmp_system_reset(Monitor *mon, const QDict *qdict);
void hmp_system_powerdown(Monitor *mon, const QDict *qdict);
void hmp_cpu(Monitor *mon, const QDict *qdict);
void hmp_memsave(Monitor *mon, const QDict *qdict);
void hmp_pmemsave(Monitor *mon, const QDict *qdict);
void hmp_cont(Monitor *mon, const QDict *qdict);
void hmp_inject_nmi(Monitor *mon, const QDict *qdict);
void hmp_set_link(Monitor *mon, const QDict *qdict);
void hmp_block_passwd(Monitor *mon, const QDict *qdict);
void hmp_balloon(Monitor *mon, const QDict *qdict);
void hmp_block_resize(Monitor *mon, const QDict *qdict);
void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
#endif

View File

@ -468,37 +468,27 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
return 0;
}
int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data)
void qmp_migrate_cancel(Error **errp)
{
migrate_fd_cancel(migrate_get_current());
return 0;
}
int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data)
void qmp_migrate_set_speed(int64_t value, Error **errp)
{
int64_t d;
MigrationState *s;
d = qdict_get_int(qdict, "value");
if (d < 0) {
d = 0;
if (value < 0) {
value = 0;
}
s = migrate_get_current();
s->bandwidth_limit = d;
s->bandwidth_limit = value;
qemu_file_set_rate_limit(s->file, s->bandwidth_limit);
return 0;
}
int do_migrate_set_downtime(Monitor *mon, const QDict *qdict,
QObject **ret_data)
void qmp_migrate_set_downtime(double value, Error **errp)
{
double d;
d = qdict_get_double(qdict, "value") * 1e9;
d = MAX(0, MIN(UINT64_MAX, d));
max_downtime = (uint64_t)d;
return 0;
value *= 1e9;
value = MAX(0, MIN(UINT64_MAX, value));
max_downtime = (uint64_t)value;
}

View File

@ -42,15 +42,8 @@ int qemu_start_incoming_migration(const char *uri);
int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data);
uint64_t migrate_max_downtime(void);
int do_migrate_set_downtime(Monitor *mon, const QDict *qdict,
QObject **ret_data);
void do_info_migrate_print(Monitor *mon, const QObject *data);
void do_info_migrate(Monitor *mon, QObject **ret_data);

201
monitor.c
View File

@ -513,10 +513,10 @@ static int do_qmp_capabilities(Monitor *mon, const QDict *params,
static void handle_user_command(Monitor *mon, const char *cmdline);
static int do_hmp_passthrough(Monitor *mon, const QDict *params,
QObject **ret_data)
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
int64_t cpu_index, Error **errp)
{
int ret = 0;
char *output = NULL;
Monitor *old_mon, hmp;
CharDriverState mchar;
@ -527,25 +527,30 @@ static int do_hmp_passthrough(Monitor *mon, const QDict *params,
old_mon = cur_mon;
cur_mon = &hmp;
if (qdict_haskey(params, "cpu-index")) {
ret = monitor_set_cpu(qdict_get_int(params, "cpu-index"));
if (has_cpu_index) {
int ret = monitor_set_cpu(cpu_index);
if (ret < 0) {
cur_mon = old_mon;
qerror_report(QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number");
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
"a CPU number");
goto out;
}
}
handle_user_command(&hmp, qdict_get_str(params, "command-line"));
handle_user_command(&hmp, command_line);
cur_mon = old_mon;
if (qemu_chr_mem_osize(hmp.chr) > 0) {
*ret_data = QOBJECT(qemu_chr_mem_to_qs(hmp.chr));
QString *str = qemu_chr_mem_to_qs(hmp.chr);
output = g_strdup(qstring_get_str(str));
QDECREF(str);
} else {
output = g_strdup("");
}
out:
qemu_chr_close_mem(hmp.chr);
return ret;
return output;
}
static int compare_cmd(const char *name, const char *list)
@ -1073,65 +1078,6 @@ static void do_singlestep(Monitor *mon, const QDict *qdict)
}
}
static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs);
struct bdrv_iterate_context {
Monitor *mon;
int err;
};
static void iostatus_bdrv_it(void *opaque, BlockDriverState *bs)
{
bdrv_iostatus_reset(bs);
}
/**
* do_cont(): Resume emulation.
*/
static int do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
struct bdrv_iterate_context context = { mon, 0 };
if (runstate_check(RUN_STATE_INMIGRATE)) {
qerror_report(QERR_MIGRATION_EXPECTED);
return -1;
} else if (runstate_check(RUN_STATE_INTERNAL_ERROR) ||
runstate_check(RUN_STATE_SHUTDOWN)) {
qerror_report(QERR_RESET_REQUIRED);
return -1;
}
bdrv_iterate(iostatus_bdrv_it, NULL);
bdrv_iterate(encrypted_bdrv_it, &context);
/* only resume the vm if all keys are set and valid */
if (!context.err) {
vm_start();
return 0;
} else {
return -1;
}
}
static void bdrv_key_cb(void *opaque, int err)
{
Monitor *mon = opaque;
/* another key was set successfully, retry to continue */
if (!err)
do_cont(mon, NULL, NULL);
}
static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs)
{
struct bdrv_iterate_context *context = opaque;
if (!context->err && bdrv_key_required(bs)) {
context->err = -EBUSY;
monitor_read_bdrv_key_start(context->mon, bs, bdrv_key_cb,
context->mon);
}
}
static void do_gdbserver(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_try_str(qdict, "device");
@ -1370,81 +1316,6 @@ static void do_print(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "\n");
}
static int do_memory_save(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
FILE *f;
uint32_t size = qdict_get_int(qdict, "size");
const char *filename = qdict_get_str(qdict, "filename");
target_long addr = qdict_get_int(qdict, "val");
uint32_t l;
CPUState *env;
uint8_t buf[1024];
int ret = -1;
env = mon_get_cpu();
f = fopen(filename, "wb");
if (!f) {
qerror_report(QERR_OPEN_FILE_FAILED, filename);
return -1;
}
while (size != 0) {
l = sizeof(buf);
if (l > size)
l = size;
cpu_memory_rw_debug(env, addr, buf, l, 0);
if (fwrite(buf, 1, l, f) != l) {
monitor_printf(mon, "fwrite() error in do_memory_save\n");
goto exit;
}
addr += l;
size -= l;
}
ret = 0;
exit:
fclose(f);
return ret;
}
static int do_physical_memory_save(Monitor *mon, const QDict *qdict,
QObject **ret_data)
{
FILE *f;
uint32_t l;
uint8_t buf[1024];
uint32_t size = qdict_get_int(qdict, "size");
const char *filename = qdict_get_str(qdict, "filename");
target_phys_addr_t addr = qdict_get_int(qdict, "val");
int ret = -1;
f = fopen(filename, "wb");
if (!f) {
qerror_report(QERR_OPEN_FILE_FAILED, filename);
return -1;
}
while (size != 0) {
l = sizeof(buf);
if (l > size)
l = size;
cpu_physical_memory_read(addr, buf, l);
if (fwrite(buf, 1, l, f) != l) {
monitor_printf(mon, "fwrite() error in do_physical_memory_save\n");
goto exit;
}
fflush(f);
addr += l;
size -= l;
}
ret = 0;
exit:
fclose(f);
return ret;
}
static void do_sum(Monitor *mon, const QDict *qdict)
{
uint32_t addr;
@ -1796,16 +1667,6 @@ static void do_boot_set(Monitor *mon, const QDict *qdict)
}
}
/**
* do_system_powerdown(): Issue a machine powerdown
*/
static int do_system_powerdown(Monitor *mon, const QDict *qdict,
QObject **ret_data)
{
qemu_system_powerdown_request();
return 0;
}
#if defined(TARGET_I386)
static void print_pte(Monitor *mon, target_phys_addr_t addr,
target_phys_addr_t pte,
@ -2348,25 +2209,6 @@ static void do_wav_capture(Monitor *mon, const QDict *qdict)
}
#endif
#if defined(TARGET_I386)
static int do_inject_nmi(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
CPUState *env;
for (env = first_cpu; env != NULL; env = env->next_cpu) {
cpu_interrupt(env, CPU_INTERRUPT_NMI);
}
return 0;
}
#else
static int do_inject_nmi(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
qerror_report(QERR_UNSUPPORTED);
return -1;
}
#endif
static qemu_acl *find_acl(Monitor *mon, const char *name)
{
qemu_acl *acl = qemu_acl_find(name);
@ -4943,3 +4785,18 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
return err;
}
int monitor_read_block_device_key(Monitor *mon, const char *device,
BlockDriverCompletionFunc *completion_cb,
void *opaque)
{
BlockDriverState *bs;
bs = bdrv_find(device);
if (!bs) {
monitor_printf(mon, "Device not found %s\n", device);
return -1;
}
return monitor_read_bdrv_key_start(mon, bs, completion_cb, opaque);
}

View File

@ -49,6 +49,9 @@ void monitor_resume(Monitor *mon);
int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
BlockDriverCompletionFunc *completion_cb,
void *opaque);
int monitor_read_block_device_key(Monitor *mon, const char *device,
BlockDriverCompletionFunc *completion_cb,
void *opaque);
int monitor_get_fd(Monitor *mon, const char *fdname);

10
net.c
View File

@ -34,6 +34,7 @@
#include "monitor.h"
#include "qemu-common.h"
#include "qemu_socket.h"
#include "qmp-commands.h"
#include "hw/qdev.h"
#include "iov.h"
@ -1258,12 +1259,10 @@ void do_info_network(Monitor *mon)
}
}
int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data)
void qmp_set_link(const char *name, bool up, Error **errp)
{
VLANState *vlan;
VLANClientState *vc = NULL;
const char *name = qdict_get_str(qdict, "name");
int up = qdict_get_bool(qdict, "up");
QTAILQ_FOREACH(vlan, &vlans, next) {
QTAILQ_FOREACH(vc, &vlan->clients, next) {
@ -1280,8 +1279,8 @@ int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data)
done:
if (!vc) {
qerror_report(QERR_DEVICE_NOT_FOUND, name);
return -1;
error_set(errp, QERR_DEVICE_NOT_FOUND, name);
return;
}
vc->link_down = !up;
@ -1300,7 +1299,6 @@ done:
if (vc->peer && vc->peer->info->link_status_changed) {
vc->peer->info->link_status_changed(vc->peer);
}
return 0;
}
void net_cleanup(void)

1
net.h
View File

@ -122,7 +122,6 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
const char *default_model);
void do_info_network(Monitor *mon);
int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data);
/* NIC info */

View File

@ -16,6 +16,12 @@
'dict': { 'userdef': 'UserDefOne', 'string': 'str' },
'*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } }
{ 'type': 'UserDefNested',
'data': { 'string0': 'str',
'dict1': { 'string1': 'str',
'dict2': { 'userdef1': 'UserDefOne', 'string2': 'str' },
'*dict3': { 'userdef2': 'UserDefOne', 'string3': 'str' } } } }
# testing commands
{ 'command': 'user_def_cmd', 'data': {} }
{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }

View File

@ -901,3 +901,270 @@
# Notes: Do not use this command.
##
{ 'command': 'cpu', 'data': {'index': 'int'} }
##
# @memsave:
#
# Save a portion of guest memory to a file.
#
# @val: the virtual address of the guest to start from
#
# @size: the size of memory region to save
#
# @filename: the file to save the memory to as binary data
#
# @cpu-index: #optional the index of the virtual CPU to use for translating the
# virtual address (defaults to CPU 0)
#
# Returns: Nothing on success
# If @cpu is not a valid VCPU, InvalidParameterValue
# If @filename cannot be opened, OpenFileFailed
# If an I/O error occurs while writing the file, IOError
#
# Since: 0.14.0
#
# Notes: Errors were not reliably returned until 1.1
##
{ 'command': 'memsave',
'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} }
##
# @pmemsave:
#
# Save a portion of guest physical memory to a file.
#
# @val: the physical address of the guest to start from
#
# @size: the size of memory region to save
#
# @filename: the file to save the memory to as binary data
#
# Returns: Nothing on success
# If @filename cannot be opened, OpenFileFailed
# If an I/O error occurs while writing the file, IOError
#
# Since: 0.14.0
#
# Notes: Errors were not reliably returned until 1.1
##
{ 'command': 'pmemsave',
'data': {'val': 'int', 'size': 'int', 'filename': 'str'} }
##
# @cont:
#
# Resume guest VCPU execution.
#
# Since: 0.14.0
#
# Returns: If successful, nothing
# If the QEMU is waiting for an incoming migration, MigrationExpected
# If QEMU was started with an encrypted block device and a key has
# not yet been set, DeviceEncrypted.
#
# Notes: This command will succeed if the guest is currently running.
##
{ 'command': 'cont' }
##
# @inject-nmi:
#
# Injects an Non-Maskable Interrupt into all guest's VCPUs.
#
# Returns: If successful, nothing
# If the Virtual Machine doesn't support NMI injection, Unsupported
#
# Since: 0.14.0
#
# Notes: Only x86 Virtual Machines support this command.
##
{ 'command': 'inject-nmi' }
##
# @set_link:
#
# Sets the link status of a virtual network adapter.
#
# @name: the device name of the virtual network adapter
#
# @up: true to set the link status to be up
#
# Returns: Nothing on success
# If @name is not a valid network device, DeviceNotFound
#
# Since: 0.14.0
#
# Notes: Not all network adapters support setting link status. This command
# will succeed even if the network adapter does not support link status
# notification.
##
{ 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} }
##
# @block_passwd:
#
# This command sets the password of a block device that has not been open
# with a password and requires one.
#
# The two cases where this can happen are a block device is created through
# QEMU's initial command line or a block device is changed through the legacy
# @change interface.
#
# In the event that the block device is created through the initial command
# line, the VM will start in the stopped state regardless of whether '-S' is
# used. The intention is for a management tool to query the block devices to
# determine which ones are encrypted, set the passwords with this command, and
# then start the guest with the @cont command.
#
# @device: the name of the device to set the password on
#
# @password: the password to use for the device
#
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
# If @device is not encrypted, DeviceNotEncrypted
# If @password is not valid for this device, InvalidPassword
#
# Notes: Not all block formats support encryption and some that do are not
# able to validate that a password is correct. Disk corruption may
# occur if an invalid password is specified.
#
# Since: 0.14.0
##
{ 'command': 'block_passwd', 'data': {'device': 'str', 'password': 'str'} }
##
# @balloon:
#
# Request the balloon driver to change its balloon size.
#
# @value: the target size of the balloon in bytes
#
# Returns: Nothing on success
# If the balloon driver is enabled but not functional because the KVM
# kernel module cannot support it, KvmMissingCap
# If no balloon device is present, DeviceNotActive
#
# Notes: This command just issues a request to the guest. When it returns,
# the balloon size may not have changed. A guest can change the balloon
# size independent of this command.
#
# Since: 0.14.0
##
{ 'command': 'balloon', 'data': {'value': 'int'} }
##
# @block_resize
#
# Resize a block image while a guest is running.
#
# @device: the name of the device to get the image resized
#
# @size: new image size in bytes
#
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
#
# Notes: This command returns UndefinedError in a number of error conditions.
#
# Since: 0.14.0
##
{ 'command': 'block_resize', 'data': { 'device': 'str', 'size': 'int' }}
##
# @blockdev-snapshot-sync
#
# Generates a synchronous snapshot of a block device.
#
# @device: the name of the device to generate the snapshot from.
#
# @snapshot-file: the target of the new image. If the file exists, or if it
# is a device, the snapshot will be created in the existing
# file/device. If does not exist, a new file will be created.
#
# @format: #optional the format of the snapshot image, default is 'qcow2'.
#
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
# If @snapshot-file can't be opened, OpenFileFailed
# If @format is invalid, InvalidBlockFormat
#
# Notes: One of the last steps taken by this command is to close the current
# image being used by @device and open the @snapshot-file one. If that
# fails, the command will try to reopen the original image file. If
# that also fails OpenFileFailed will be returned and the guest may get
# unexpected errors.
#
# Since 0.14.0
##
{ 'command': 'blockdev-snapshot-sync',
'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str' } }
##
# @human-monitor-command:
#
# Execute a command on the human monitor and return the output.
#
# @command-line: the command to execute in the human monitor
#
# @cpu-index: #optional The CPU to use for commands that require an implicit CPU
#
# Returns: the output of the command as a string
#
# Since: 0.14.0
#
# Notes: This command only exists as a stop-gap. It's use is highly
# discouraged. The semantics of this command are not guaranteed.
#
# Known limitations:
#
# o This command is stateless, this means that commands that depend
# on state information (such as getfd) might not work
#
# o Commands that prompt the user for data (eg. 'cont' when the block
# device is encrypted) don't currently work
##
{ 'command': 'human-monitor-command',
'data': {'command-line': 'str', '*cpu-index': 'int'},
'returns': 'str' }
##
# @migrate_cancel
#
# Cancel the current executing migration process.
#
# Returns: nothing on success
#
# Notes: This command succeeds even if there is no migration process running.
#
# Since: 0.14.0
##
{ 'command': 'migrate_cancel' }
##
# @migrate_set_downtime
#
# Set maximum tolerated downtime for migration.
#
# @value: maximum downtime in seconds
#
# Returns: nothing on success
#
# Since: 0.14.0
##
{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'} }
##
# @migrate_set_speed
#
# Set maximum speed for migration.
#
# @value: maximum speed in bytes.
#
# Returns: nothing on success
#
# Notes: A value lesser than zero will be automatically round up to zero.
#
# Since: 0.14.0
##
{ 'command': 'migrate_set_speed', 'data': {'value': 'int'} }

View File

@ -148,6 +148,10 @@ static const QErrorStringTable qerror_table[] = {
.error_fmt = QERR_INVALID_PASSWORD,
.desc = "Password incorrect",
},
{
.error_fmt = QERR_IO_ERROR,
.desc = "An IO error has occurred",
},
{
.error_fmt = QERR_JSON_PARSING,
.desc = "Invalid JSON syntax",

View File

@ -126,6 +126,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_INVALID_PASSWORD \
"{ 'class': 'InvalidPassword', 'data': {} }"
#define QERR_IO_ERROR \
"{ 'class': 'IOError', 'data': {} }"
#define QERR_JSON_PARSING \
"{ 'class': 'JSONParsing', 'data': {} }"

View File

@ -199,10 +199,7 @@ EQMP
{
.name = "cont",
.args_type = "",
.params = "",
.help = "resume emulation",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_cont,
.mhandler.cmd_new = qmp_marshal_input_cont,
},
SQMP
@ -244,10 +241,7 @@ EQMP
{
.name = "system_powerdown",
.args_type = "",
.params = "",
.help = "send system power down event",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_system_powerdown,
.mhandler.cmd_new = qmp_marshal_input_system_powerdown,
},
SQMP
@ -355,11 +349,8 @@ EQMP
{
.name = "memsave",
.args_type = "val:l,size:i,filename:s",
.params = "addr size file",
.help = "save to disk virtual memory dump starting at 'addr' of size 'size'",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_memory_save,
.args_type = "val:l,size:i,filename:s,cpu:i?",
.mhandler.cmd_new = qmp_marshal_input_memsave,
},
SQMP
@ -373,6 +364,7 @@ Arguments:
- "val": the starting address (json-int)
- "size": the memory size, in bytes (json-int)
- "filename": file path (json-string)
- "cpu": virtual CPU index (json-int, optional)
Example:
@ -382,17 +374,12 @@ Example:
"filename": "/tmp/virtual-mem-dump" } }
<- { "return": {} }
Note: Depends on the current CPU.
EQMP
{
.name = "pmemsave",
.args_type = "val:l,size:i,filename:s",
.params = "addr size file",
.help = "save to disk physical memory dump starting at 'addr' of size 'size'",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_physical_memory_save,
.mhandler.cmd_new = qmp_marshal_input_pmemsave,
},
SQMP
@ -420,10 +407,7 @@ EQMP
{
.name = "inject-nmi",
.args_type = "",
.params = "",
.help = "",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_inject_nmi,
.mhandler.cmd_new = qmp_marshal_input_inject_nmi,
},
SQMP
@ -487,10 +471,7 @@ EQMP
{
.name = "migrate_cancel",
.args_type = "",
.params = "",
.help = "cancel the current VM migration",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_migrate_cancel,
.mhandler.cmd_new = qmp_marshal_input_migrate_cancel,
},
SQMP
@ -511,10 +492,7 @@ EQMP
{
.name = "migrate_set_speed",
.args_type = "value:o",
.params = "value",
.help = "set maximum speed (in bytes) for migrations",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_migrate_set_speed,
.mhandler.cmd_new = qmp_marshal_input_migrate_set_speed,
},
SQMP
@ -537,10 +515,7 @@ EQMP
{
.name = "migrate_set_downtime",
.args_type = "value:T",
.params = "value",
.help = "set maximum tolerated downtime (in seconds) for migrations",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_migrate_set_downtime,
.mhandler.cmd_new = qmp_marshal_input_migrate_set_downtime,
},
SQMP
@ -658,10 +633,7 @@ EQMP
{
.name = "block_resize",
.args_type = "device:B,size:o",
.params = "device size",
.help = "resize a block image",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_block_resize,
.mhandler.cmd_new = qmp_marshal_input_block_resize,
},
SQMP
@ -684,10 +656,8 @@ EQMP
{
.name = "blockdev-snapshot-sync",
.args_type = "device:B,snapshot-file:s?,format:s?",
.params = "device [new-image-file] [format]",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_snapshot_blkdev,
.args_type = "device:B,snapshot-file:s,format:s?",
.mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync,
},
SQMP
@ -719,11 +689,7 @@ EQMP
{
.name = "balloon",
.args_type = "value:M",
.params = "target",
.help = "request VM to change its memory allocation (in MB)",
.user_print = monitor_user_noop,
.mhandler.cmd_async = do_balloon,
.flags = MONITOR_CMD_ASYNC,
.mhandler.cmd_new = qmp_marshal_input_balloon,
},
SQMP
@ -746,10 +712,7 @@ EQMP
{
.name = "set_link",
.args_type = "name:s,up:b",
.params = "name on|off",
.help = "change the link status of a network adapter",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_set_link,
.mhandler.cmd_new = qmp_marshal_input_set_link,
},
SQMP
@ -825,10 +788,7 @@ EQMP
{
.name = "block_passwd",
.args_type = "device:B,password:s",
.params = "block_passwd device password",
.help = "set the password of encrypted block devices",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_block_set_passwd,
.mhandler.cmd_new = qmp_marshal_input_block_passwd,
},
SQMP
@ -1001,10 +961,7 @@ EQMP
{
.name = "human-monitor-command",
.args_type = "command-line:s,cpu-index:i?",
.params = "",
.help = "",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_hmp_passthrough,
.mhandler.cmd_new = qmp_marshal_input_human_monitor_command,
},
SQMP

37
qmp.c
View File

@ -117,3 +117,40 @@ SpiceInfo *qmp_query_spice(Error **errp)
return NULL;
};
#endif
static void iostatus_bdrv_it(void *opaque, BlockDriverState *bs)
{
bdrv_iostatus_reset(bs);
}
static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs)
{
Error **err = opaque;
if (!error_is_set(err) && bdrv_key_required(bs)) {
error_set(err, QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs));
}
}
void qmp_cont(Error **errp)
{
Error *local_err = NULL;
if (runstate_check(RUN_STATE_INMIGRATE)) {
error_set(errp, QERR_MIGRATION_EXPECTED);
return;
} else if (runstate_check(RUN_STATE_INTERNAL_ERROR) ||
runstate_check(RUN_STATE_SHUTDOWN)) {
error_set(errp, QERR_RESET_REQUIRED);
return;
}
bdrv_iterate(iostatus_bdrv_it, NULL);
bdrv_iterate(encrypted_bdrv_it, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
vm_start();
}

270
test-qmp-input-visitor.c Normal file
View File

@ -0,0 +1,270 @@
/*
* QMP Input Visitor unit-tests.
*
* Copyright (C) 2011 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include <glib.h>
#include <stdarg.h>
#include "qapi/qmp-input-visitor.h"
#include "test-qapi-types.h"
#include "test-qapi-visit.h"
#include "qemu-objects.h"
typedef struct TestInputVisitorData {
QObject *obj;
QmpInputVisitor *qiv;
} TestInputVisitorData;
static void visitor_input_teardown(TestInputVisitorData *data,
const void *unused)
{
qobject_decref(data->obj);
data->obj = NULL;
if (data->qiv) {
qmp_input_visitor_cleanup(data->qiv);
data->qiv = NULL;
}
}
/* This is provided instead of a test setup function so that the JSON
string used by the tests are kept in the test functions (and not
int main()) */
static Visitor *visitor_input_test_init(TestInputVisitorData *data,
const char *json_string, ...)
{
Visitor *v;
va_list ap;
va_start(ap, json_string);
data->obj = qobject_from_jsonv(json_string, &ap);
va_end(ap);
g_assert(data->obj != NULL);
data->qiv = qmp_input_visitor_new(data->obj);
g_assert(data->qiv != NULL);
v = qmp_input_get_visitor(data->qiv);
g_assert(v != NULL);
return v;
}
static void test_visitor_in_int(TestInputVisitorData *data,
const void *unused)
{
int64_t res = 0, value = -42;
Error *errp = NULL;
Visitor *v;
v = visitor_input_test_init(data, "%d", value);
visit_type_int(v, &res, NULL, &errp);
g_assert(!error_is_set(&errp));
g_assert_cmpint(res, ==, value);
}
static void test_visitor_in_bool(TestInputVisitorData *data,
const void *unused)
{
Error *errp = NULL;
bool res = false;
Visitor *v;
v = visitor_input_test_init(data, "true");
visit_type_bool(v, &res, NULL, &errp);
g_assert(!error_is_set(&errp));
g_assert_cmpint(res, ==, true);
}
static void test_visitor_in_number(TestInputVisitorData *data,
const void *unused)
{
double res = 0, value = 3.14;
Error *errp = NULL;
Visitor *v;
v = visitor_input_test_init(data, "%f", value);
visit_type_number(v, &res, NULL, &errp);
g_assert(!error_is_set(&errp));
g_assert_cmpfloat(res, ==, value);
}
static void test_visitor_in_string(TestInputVisitorData *data,
const void *unused)
{
char *res = NULL, *value = (char *) "Q E M U";
Error *errp = NULL;
Visitor *v;
v = visitor_input_test_init(data, "%s", value);
visit_type_str(v, &res, NULL, &errp);
g_assert(!error_is_set(&errp));
g_assert_cmpstr(res, ==, value);
g_free(res);
}
static void test_visitor_in_enum(TestInputVisitorData *data,
const void *unused)
{
Error *errp = NULL;
Visitor *v;
EnumOne i;
for (i = 0; EnumOne_lookup[i]; i++) {
EnumOne res = -1;
v = visitor_input_test_init(data, "%s", EnumOne_lookup[i]);
visit_type_EnumOne(v, &res, NULL, &errp);
g_assert(!error_is_set(&errp));
g_assert_cmpint(i, ==, res);
visitor_input_teardown(data, NULL);
}
data->obj = NULL;
data->qiv = NULL;
}
typedef struct TestStruct
{
int64_t integer;
bool boolean;
char *string;
} TestStruct;
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
const char *name, Error **errp)
{
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
errp);
visit_type_int(v, &(*obj)->integer, "integer", errp);
visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
visit_type_str(v, &(*obj)->string, "string", errp);
visit_end_struct(v, errp);
}
static void test_visitor_in_struct(TestInputVisitorData *data,
const void *unused)
{
TestStruct *p = NULL;
Error *errp = NULL;
Visitor *v;
v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
visit_type_TestStruct(v, &p, NULL, &errp);
g_assert(!error_is_set(&errp));
g_assert_cmpint(p->integer, ==, -42);
g_assert(p->boolean == true);
g_assert_cmpstr(p->string, ==, "foo");
g_free(p->string);
g_free(p);
}
static void check_and_free_str(char *str, const char *cmp)
{
g_assert_cmpstr(str, ==, cmp);
g_free(str);
}
static void test_visitor_in_struct_nested(TestInputVisitorData *data,
const void *unused)
{
UserDefNested *udp = NULL;
Error *errp = NULL;
Visitor *v;
v = visitor_input_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}");
visit_type_UserDefNested(v, &udp, NULL, &errp);
g_assert(!error_is_set(&errp));
check_and_free_str(udp->string0, "string0");
check_and_free_str(udp->dict1.string1, "string1");
g_assert_cmpint(udp->dict1.dict2.userdef1->integer, ==, 42);
check_and_free_str(udp->dict1.dict2.userdef1->string, "string");
check_and_free_str(udp->dict1.dict2.string2, "string2");
g_assert(udp->dict1.has_dict3 == false);
g_free(udp->dict1.dict2.userdef1);
g_free(udp);
}
static void test_visitor_in_list(TestInputVisitorData *data,
const void *unused)
{
UserDefOneList *item, *head = NULL;
Error *errp = NULL;
Visitor *v;
int i;
v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");
visit_type_UserDefOneList(v, &head, NULL, &errp);
g_assert(!error_is_set(&errp));
g_assert(head != NULL);
for (i = 0, item = head; item; item = item->next, i++) {
char string[12];
snprintf(string, sizeof(string), "string%d", i);
g_assert_cmpstr(item->value->string, ==, string);
g_assert_cmpint(item->value->integer, ==, 42 + i);
}
qapi_free_UserDefOneList(head);
}
static void input_visitor_test_add(const char *testpath,
TestInputVisitorData *data,
void (*test_func)(TestInputVisitorData *data, const void *user_data))
{
g_test_add(testpath, TestInputVisitorData, data, NULL, test_func,
visitor_input_teardown);
}
int main(int argc, char **argv)
{
TestInputVisitorData in_visitor_data;
g_test_init(&argc, &argv, NULL);
input_visitor_test_add("/visitor/input/int",
&in_visitor_data, test_visitor_in_int);
input_visitor_test_add("/visitor/input/bool",
&in_visitor_data, test_visitor_in_bool);
input_visitor_test_add("/visitor/input/number",
&in_visitor_data, test_visitor_in_number);
input_visitor_test_add("/visitor/input/string",
&in_visitor_data, test_visitor_in_string);
input_visitor_test_add("/visitor/input/enum",
&in_visitor_data, test_visitor_in_enum);
input_visitor_test_add("/visitor/input/struct",
&in_visitor_data, test_visitor_in_struct);
input_visitor_test_add("/visitor/input/struct-nested",
&in_visitor_data, test_visitor_in_struct_nested);
input_visitor_test_add("/visitor/input/list",
&in_visitor_data, test_visitor_in_list);
g_test_run();
return 0;
}

423
test-qmp-output-visitor.c Normal file
View File

@ -0,0 +1,423 @@
/*
* QMP Output Visitor unit-tests.
*
* Copyright (C) 2011 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include <glib.h>
#include "qapi/qmp-output-visitor.h"
#include "test-qapi-types.h"
#include "test-qapi-visit.h"
#include "qemu-objects.h"
typedef struct TestOutputVisitorData {
QmpOutputVisitor *qov;
Visitor *ov;
} TestOutputVisitorData;
static void visitor_output_setup(TestOutputVisitorData *data,
const void *unused)
{
data->qov = qmp_output_visitor_new();
g_assert(data->qov != NULL);
data->ov = qmp_output_get_visitor(data->qov);
g_assert(data->ov != NULL);
}
static void visitor_output_teardown(TestOutputVisitorData *data,
const void *unused)
{
qmp_output_visitor_cleanup(data->qov);
data->qov = NULL;
data->ov = NULL;
}
static void test_visitor_out_int(TestOutputVisitorData *data,
const void *unused)
{
int64_t value = -42;
Error *errp = NULL;
QObject *obj;
visit_type_int(data->ov, &value, NULL, &errp);
g_assert(error_is_set(&errp) == 0);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QINT);
g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, value);
qobject_decref(obj);
}
static void test_visitor_out_bool(TestOutputVisitorData *data,
const void *unused)
{
Error *errp = NULL;
bool value = true;
QObject *obj;
visit_type_bool(data->ov, &value, NULL, &errp);
g_assert(error_is_set(&errp) == 0);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
g_assert(qbool_get_int(qobject_to_qbool(obj)) == value);
qobject_decref(obj);
}
static void test_visitor_out_number(TestOutputVisitorData *data,
const void *unused)
{
double value = 3.14;
Error *errp = NULL;
QObject *obj;
visit_type_number(data->ov, &value, NULL, &errp);
g_assert(error_is_set(&errp) == 0);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QFLOAT);
g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value);
qobject_decref(obj);
}
static void test_visitor_out_string(TestOutputVisitorData *data,
const void *unused)
{
char *string = (char *) "Q E M U";
Error *errp = NULL;
QObject *obj;
visit_type_str(data->ov, &string, NULL, &errp);
g_assert(error_is_set(&errp) == 0);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QSTRING);
g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
qobject_decref(obj);
}
static void test_visitor_out_no_string(TestOutputVisitorData *data,
const void *unused)
{
char *string = NULL;
Error *errp = NULL;
QObject *obj;
/* A null string should return "" */
visit_type_str(data->ov, &string, NULL, &errp);
g_assert(error_is_set(&errp) == 0);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QSTRING);
g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, "");
qobject_decref(obj);
}
static void test_visitor_out_enum(TestOutputVisitorData *data,
const void *unused)
{
Error *errp = NULL;
QObject *obj;
EnumOne i;
for (i = 0; i < ENUM_ONE_MAX; i++) {
visit_type_EnumOne(data->ov, &i, "unused", &errp);
g_assert(!error_is_set(&errp));
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QSTRING);
g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
EnumOne_lookup[i]);
qobject_decref(obj);
}
}
static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
const void *unused)
{
EnumOne i, bad_values[] = { ENUM_ONE_MAX, -1 };
Error *errp;
for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
errp = NULL;
visit_type_EnumOne(data->ov, &bad_values[i], "unused", &errp);
g_assert(error_is_set(&errp) == true);
error_free(errp);
}
}
typedef struct TestStruct
{
int64_t integer;
bool boolean;
char *string;
} TestStruct;
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
const char *name, Error **errp)
{
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
errp);
visit_type_int(v, &(*obj)->integer, "integer", errp);
visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
visit_type_str(v, &(*obj)->string, "string", errp);
visit_end_struct(v, errp);
}
static void test_visitor_out_struct(TestOutputVisitorData *data,
const void *unused)
{
TestStruct test_struct = { .integer = 42,
.boolean = false,
.string = (char *) "foo"};
TestStruct *p = &test_struct;
Error *errp = NULL;
QObject *obj;
QDict *qdict;
visit_type_TestStruct(data->ov, &p, NULL, &errp);
g_assert(!error_is_set(&errp));
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QDICT);
qdict = qobject_to_qdict(obj);
g_assert_cmpint(qdict_size(qdict), ==, 3);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, 0);
g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo");
QDECREF(qdict);
}
static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
const void *unused)
{
int64_t value = 42;
Error *errp = NULL;
UserDefNested *ud2;
QObject *obj;
QDict *qdict, *dict1, *dict2, *dict3, *userdef;
const char *string = "user def string";
const char *strings[] = { "fourty two", "fourty three", "fourty four",
"fourty five" };
ud2 = g_malloc0(sizeof(*ud2));
ud2->string0 = g_strdup(strings[0]);
ud2->dict1.string1 = g_strdup(strings[1]);
ud2->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
ud2->dict1.dict2.userdef1->string = g_strdup(string);
ud2->dict1.dict2.userdef1->integer = value;
ud2->dict1.dict2.string2 = g_strdup(strings[2]);
ud2->dict1.has_dict3 = true;
ud2->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
ud2->dict1.dict3.userdef2->string = g_strdup(string);
ud2->dict1.dict3.userdef2->integer = value;
ud2->dict1.dict3.string3 = g_strdup(strings[3]);
visit_type_UserDefNested(data->ov, &ud2, "unused", &errp);
g_assert(!error_is_set(&errp));
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QDICT);
qdict = qobject_to_qdict(obj);
g_assert_cmpint(qdict_size(qdict), ==, 2);
g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]);
dict1 = qdict_get_qdict(qdict, "dict1");
g_assert_cmpint(qdict_size(dict1), ==, 3);
g_assert_cmpstr(qdict_get_str(dict1, "string1"), ==, strings[1]);
dict2 = qdict_get_qdict(dict1, "dict2");
g_assert_cmpint(qdict_size(dict2), ==, 2);
g_assert_cmpstr(qdict_get_str(dict2, "string2"), ==, strings[2]);
userdef = qdict_get_qdict(dict2, "userdef1");
g_assert_cmpint(qdict_size(userdef), ==, 2);
g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
dict3 = qdict_get_qdict(dict1, "dict3");
g_assert_cmpint(qdict_size(dict3), ==, 2);
g_assert_cmpstr(qdict_get_str(dict3, "string3"), ==, strings[3]);
userdef = qdict_get_qdict(dict3, "userdef2");
g_assert_cmpint(qdict_size(userdef), ==, 2);
g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
QDECREF(qdict);
qapi_free_UserDefNested(ud2);
}
typedef struct TestStructList
{
TestStruct *value;
struct TestStructList *next;
} TestStructList;
static void visit_type_TestStructList(Visitor *v, TestStructList **obj,
const char *name, Error **errp)
{
GenericList *i, **head = (GenericList **)obj;
visit_start_list(v, name, errp);
for (*head = i = visit_next_list(v, head, errp); i; i = visit_next_list(v, &i, errp)) {
TestStructList *native_i = (TestStructList *)i;
visit_type_TestStruct(v, &native_i->value, NULL, errp);
}
visit_end_list(v, errp);
}
static void test_visitor_out_list(TestOutputVisitorData *data,
const void *unused)
{
char *value_str = (char *) "list value";
TestStructList *p, *head = NULL;
const int max_items = 10;
bool value_bool = true;
int value_int = 10;
Error *errp = NULL;
QListEntry *entry;
QObject *obj;
QList *qlist;
int i;
for (i = 0; i < max_items; i++) {
p = g_malloc0(sizeof(*p));
p->value = g_malloc0(sizeof(*p->value));
p->value->integer = value_int;
p->value->boolean = value_bool;
p->value->string = value_str;
p->next = head;
head = p;
}
visit_type_TestStructList(data->ov, &head, NULL, &errp);
g_assert(!error_is_set(&errp));
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QLIST);
qlist = qobject_to_qlist(obj);
g_assert(!qlist_empty(qlist));
i = 0;
QLIST_FOREACH_ENTRY(qlist, entry) {
QDict *qdict;
g_assert(qobject_type(entry->value) == QTYPE_QDICT);
qdict = qobject_to_qdict(entry->value);
g_assert_cmpint(qdict_size(qdict), ==, 3);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int);
g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool);
g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str);
i++;
}
g_assert_cmpint(i, ==, max_items);
QDECREF(qlist);
for (p = head; p;) {
TestStructList *tmp = p->next;
g_free(p->value);
g_free(p);
p = tmp;
}
}
static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
const void *unused)
{
UserDefNestedList *p, *head = NULL;
const char string[] = "foo bar";
int i, max_count = 1024;
for (i = 0; i < max_count; i++) {
p = g_malloc0(sizeof(*p));
p->value = g_malloc0(sizeof(*p->value));
p->value->string0 = g_strdup(string);
p->value->dict1.string1 = g_strdup(string);
p->value->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
p->value->dict1.dict2.userdef1->string = g_strdup(string);
p->value->dict1.dict2.userdef1->integer = 42;
p->value->dict1.dict2.string2 = g_strdup(string);
p->value->dict1.has_dict3 = false;
p->next = head;
head = p;
}
qapi_free_UserDefNestedList(head);
}
static void output_visitor_test_add(const char *testpath,
TestOutputVisitorData *data,
void (*test_func)(TestOutputVisitorData *data, const void *user_data))
{
g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup,
test_func, visitor_output_teardown);
}
int main(int argc, char **argv)
{
TestOutputVisitorData out_visitor_data;
g_test_init(&argc, &argv, NULL);
output_visitor_test_add("/visitor/output/int",
&out_visitor_data, test_visitor_out_int);
output_visitor_test_add("/visitor/output/bool",
&out_visitor_data, test_visitor_out_bool);
output_visitor_test_add("/visitor/output/number",
&out_visitor_data, test_visitor_out_number);
output_visitor_test_add("/visitor/output/string",
&out_visitor_data, test_visitor_out_string);
output_visitor_test_add("/visitor/output/no-string",
&out_visitor_data, test_visitor_out_no_string);
output_visitor_test_add("/visitor/output/enum",
&out_visitor_data, test_visitor_out_enum);
output_visitor_test_add("/visitor/output/enum-errors",
&out_visitor_data, test_visitor_out_enum_errors);
output_visitor_test_add("/visitor/output/struct",
&out_visitor_data, test_visitor_out_struct);
output_visitor_test_add("/visitor/output/struct-nested",
&out_visitor_data, test_visitor_out_struct_nested);
output_visitor_test_add("/visitor/output/list",
&out_visitor_data, test_visitor_out_list);
output_visitor_test_add("/visitor/output/list-qapi-free",
&out_visitor_data, test_visitor_out_list_qapi_free);
g_test_run();
return 0;
}

View File

@ -1,338 +0,0 @@
#include <glib.h>
#include "qapi/qmp-output-visitor.h"
#include "qapi/qmp-input-visitor.h"
#include "test-qapi-types.h"
#include "test-qapi-visit.h"
#include "qemu-objects.h"
typedef struct TestStruct
{
int64_t x;
int64_t y;
} TestStruct;
typedef struct TestStructList
{
TestStruct *value;
struct TestStructList *next;
} TestStructList;
static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp)
{
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), errp);
visit_type_int(v, &(*obj)->x, "x", errp);
visit_type_int(v, &(*obj)->y, "y", errp);
visit_end_struct(v, errp);
}
static void visit_type_TestStructList(Visitor *m, TestStructList ** obj, const char *name, Error **errp)
{
GenericList *i, **head = (GenericList **)obj;
visit_start_list(m, name, errp);
for (*head = i = visit_next_list(m, head, errp); i; i = visit_next_list(m, &i, errp)) {
TestStructList *native_i = (TestStructList *)i;
visit_type_TestStruct(m, &native_i->value, NULL, errp);
}
visit_end_list(m, errp);
}
/* test core visitor methods */
static void test_visitor_core(void)
{
QmpOutputVisitor *mo;
QmpInputVisitor *mi;
Visitor *v;
TestStruct ts = { 42, 82 };
TestStruct *pts = &ts;
TestStructList *lts = NULL;
Error *err = NULL;
QObject *obj;
QList *qlist;
QDict *qdict;
QString *str;
int64_t value = 0;
mo = qmp_output_visitor_new();
v = qmp_output_get_visitor(mo);
visit_type_TestStruct(v, &pts, NULL, &err);
obj = qmp_output_get_qobject(mo);
str = qobject_to_json(obj);
printf("%s\n", qstring_get_str(str));
QDECREF(str);
obj = QOBJECT(qint_from_int(0x42));
mi = qmp_input_visitor_new(obj);
v = qmp_input_get_visitor(mi);
visit_type_int(v, &value, NULL, &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
g_assert(value == 0x42);
qobject_decref(obj);
obj = qobject_from_json("{'x': 42, 'y': 84}");
mi = qmp_input_visitor_new(obj);
v = qmp_input_get_visitor(mi);
pts = NULL;
visit_type_TestStruct(v, &pts, NULL, &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
g_assert(pts != NULL);
g_assert(pts->x == 42);
g_assert(pts->y == 84);
qobject_decref(obj);
g_free(pts);
/* test list input visitor */
obj = qobject_from_json("[{'x': 42, 'y': 84}, {'x': 12, 'y': 24}]");
mi = qmp_input_visitor_new(obj);
v = qmp_input_get_visitor(mi);
visit_type_TestStructList(v, &lts, NULL, &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
g_assert(lts != NULL);
g_assert(lts->value->x == 42);
g_assert(lts->value->y == 84);
g_assert(lts->next != NULL);
g_assert(lts->next->value->x == 12);
g_assert(lts->next->value->y == 24);
g_assert(lts->next->next == NULL);
qobject_decref(obj);
/* test list output visitor */
mo = qmp_output_visitor_new();
v = qmp_output_get_visitor(mo);
visit_type_TestStructList(v, &lts, NULL, &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
obj = qmp_output_get_qobject(mo);
g_print("obj: %s\n", qstring_get_str(qobject_to_json(obj)));
qlist = qobject_to_qlist(obj);
assert(qlist);
obj = qlist_pop(qlist);
qdict = qobject_to_qdict(obj);
assert(qdict);
assert(qdict_get_int(qdict, "x") == 42);
assert(qdict_get_int(qdict, "y") == 84);
qobject_decref(obj);
obj = qlist_pop(qlist);
qdict = qobject_to_qdict(obj);
assert(qdict);
assert(qdict_get_int(qdict, "x") == 12);
assert(qdict_get_int(qdict, "y") == 24);
qobject_decref(obj);
qmp_output_visitor_cleanup(mo);
QDECREF(qlist);
}
/* test deep nesting with refs to other user-defined types */
static void test_nested_structs(void)
{
QmpOutputVisitor *mo;
QmpInputVisitor *mi;
Visitor *v;
UserDefOne ud1;
UserDefOne *ud1_p = &ud1, *ud1c_p = NULL;
UserDefTwo ud2;
UserDefTwo *ud2_p = &ud2, *ud2c_p = NULL;
Error *err = NULL;
QObject *obj;
QString *str;
ud1.integer = 42;
ud1.string = strdup("forty two");
/* sanity check */
mo = qmp_output_visitor_new();
v = qmp_output_get_visitor(mo);
visit_type_UserDefOne(v, &ud1_p, "o_O", &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
obj = qmp_output_get_qobject(mo);
g_assert(obj);
qobject_decref(obj);
ud2.string = strdup("forty three");
ud2.dict.string = strdup("forty four");
ud2.dict.dict.userdef = ud1_p;
ud2.dict.dict.string = strdup("forty five");
ud2.dict.has_dict2 = true;
ud2.dict.dict2.userdef = ud1_p;
ud2.dict.dict2.string = strdup("forty six");
/* c type -> qobject */
mo = qmp_output_visitor_new();
v = qmp_output_get_visitor(mo);
visit_type_UserDefTwo(v, &ud2_p, "unused", &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
obj = qmp_output_get_qobject(mo);
g_assert(obj);
str = qobject_to_json_pretty(obj);
g_print("%s\n", qstring_get_str(str));
QDECREF(str);
/* qobject -> c type, should match original struct */
mi = qmp_input_visitor_new(obj);
v = qmp_input_get_visitor(mi);
visit_type_UserDefTwo(v, &ud2c_p, NULL, &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
g_assert(!g_strcmp0(ud2c_p->string, ud2.string));
g_assert(!g_strcmp0(ud2c_p->dict.string, ud2.dict.string));
ud1c_p = ud2c_p->dict.dict.userdef;
g_assert(ud1c_p->integer == ud1_p->integer);
g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
g_assert(!g_strcmp0(ud2c_p->dict.dict.string, ud2.dict.dict.string));
ud1c_p = ud2c_p->dict.dict2.userdef;
g_assert(ud1c_p->integer == ud1_p->integer);
g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
g_assert(!g_strcmp0(ud2c_p->dict.dict2.string, ud2.dict.dict2.string));
g_free(ud1.string);
g_free(ud2.string);
g_free(ud2.dict.string);
g_free(ud2.dict.dict.string);
g_free(ud2.dict.dict2.string);
qapi_free_UserDefTwo(ud2c_p);
qobject_decref(obj);
}
/* test enum values */
static void test_enums(void)
{
QmpOutputVisitor *mo;
QmpInputVisitor *mi;
Visitor *v;
EnumOne enum1 = ENUM_ONE_VALUE2, enum1_cpy = ENUM_ONE_VALUE1;
Error *err = NULL;
QObject *obj;
QString *str;
/* C type -> QObject */
mo = qmp_output_visitor_new();
v = qmp_output_get_visitor(mo);
visit_type_EnumOne(v, &enum1, "unused", &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
obj = qmp_output_get_qobject(mo);
g_assert(obj);
str = qobject_to_json_pretty(obj);
g_print("%s\n", qstring_get_str(str));
QDECREF(str);
g_assert(g_strcmp0(qstring_get_str(qobject_to_qstring(obj)), "value2") == 0);
/* QObject -> C type */
mi = qmp_input_visitor_new(obj);
v = qmp_input_get_visitor(mi);
visit_type_EnumOne(v, &enum1_cpy, "unused", &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
g_debug("enum1_cpy, enum1: %d, %d", enum1_cpy, enum1);
g_assert(enum1_cpy == enum1);
qobject_decref(obj);
}
/* test enum values nested in schema-defined structs */
static void test_nested_enums(void)
{
QmpOutputVisitor *mo;
QmpInputVisitor *mi;
Visitor *v;
NestedEnumsOne *nested_enums, *nested_enums_cpy = NULL;
Error *err = NULL;
QObject *obj;
QString *str;
nested_enums = g_malloc0(sizeof(NestedEnumsOne));
nested_enums->enum1 = ENUM_ONE_VALUE1;
nested_enums->enum2 = ENUM_ONE_VALUE2;
nested_enums->enum3 = ENUM_ONE_VALUE3;
nested_enums->enum4 = ENUM_ONE_VALUE3;
nested_enums->has_enum2 = false;
nested_enums->has_enum4 = true;
/* C type -> QObject */
mo = qmp_output_visitor_new();
v = qmp_output_get_visitor(mo);
visit_type_NestedEnumsOne(v, &nested_enums, NULL, &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
obj = qmp_output_get_qobject(mo);
g_assert(obj);
str = qobject_to_json_pretty(obj);
g_print("%s\n", qstring_get_str(str));
QDECREF(str);
/* QObject -> C type */
mi = qmp_input_visitor_new(obj);
v = qmp_input_get_visitor(mi);
visit_type_NestedEnumsOne(v, &nested_enums_cpy, NULL, &err);
if (err) {
g_error("%s", error_get_pretty(err));
}
g_assert(nested_enums_cpy);
g_assert(nested_enums_cpy->enum1 == nested_enums->enum1);
g_assert(nested_enums_cpy->enum3 == nested_enums->enum3);
g_assert(nested_enums_cpy->enum4 == nested_enums->enum4);
g_assert(nested_enums_cpy->has_enum2 == false);
g_assert(nested_enums_cpy->has_enum4 == true);
qmp_output_visitor_cleanup(mo);
qmp_input_visitor_cleanup(mi);
qapi_free_NestedEnumsOne(nested_enums);
qapi_free_NestedEnumsOne(nested_enums_cpy);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/0.15/visitor_core", test_visitor_core);
g_test_add_func("/0.15/nested_structs", test_nested_structs);
g_test_add_func("/0.15/enums", test_enums);
g_test_add_func("/0.15/nested_enums", test_nested_enums);
g_test_run();
return 0;
}