Merge remote branch 'qmp/for-anthony' into staging
This commit is contained in:
commit
02d0ba1420
@ -42,6 +42,7 @@ def main():
|
||||
|
||||
qemu = qmp.QEMUMonitorProtocol(argv[1])
|
||||
qemu.connect()
|
||||
qemu.send("qmp_capabilities")
|
||||
|
||||
print 'Connected!'
|
||||
|
||||
|
@ -63,10 +63,14 @@ class QEMUMonitorProtocol:
|
||||
|
||||
def __json_read(self):
|
||||
try:
|
||||
return json.loads(self.sock.recv(1024))
|
||||
while True:
|
||||
line = json.loads(self.sockfile.readline())
|
||||
if not 'event' in line:
|
||||
return line
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sockfile = self.sock.makefile()
|
||||
|
@ -24,6 +24,7 @@ def main():
|
||||
|
||||
qemu = qmp.QEMUMonitorProtocol(argv[1])
|
||||
qemu.connect()
|
||||
qemu.send("qmp_capabilities")
|
||||
|
||||
for cmd in [ 'version', 'kvm', 'status', 'uuid', 'balloon' ]:
|
||||
print cmd + ': ' + str(qemu.send('query-' + cmd))
|
||||
|
@ -521,7 +521,7 @@ static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
|
||||
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
int force = qdict_get_int(qdict, "force");
|
||||
int force = qdict_get_try_bool(qdict, "force", 0);
|
||||
const char *filename = qdict_get_str(qdict, "device");
|
||||
|
||||
bs = bdrv_find(filename);
|
||||
|
@ -50,7 +50,7 @@ START_TEST(qdict_put_obj_test)
|
||||
qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num)));
|
||||
|
||||
fail_unless(qdict_size(qdict) == 1);
|
||||
ent = QLIST_FIRST(&qdict->table[12345 % QDICT_HASH_SIZE]);
|
||||
ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]);
|
||||
qi = qobject_to_qint(ent->value);
|
||||
fail_unless(qint_get_int(qi) == num);
|
||||
|
||||
@ -194,6 +194,36 @@ START_TEST(qobject_to_qdict_test)
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(qdict_iterapi_test)
|
||||
{
|
||||
int count;
|
||||
const QDictEntry *ent;
|
||||
|
||||
fail_unless(qdict_first(tests_dict) == NULL);
|
||||
|
||||
qdict_put(tests_dict, "key1", qint_from_int(1));
|
||||
qdict_put(tests_dict, "key2", qint_from_int(2));
|
||||
qdict_put(tests_dict, "key3", qint_from_int(3));
|
||||
|
||||
count = 0;
|
||||
for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
|
||||
fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
|
||||
count++;
|
||||
}
|
||||
|
||||
fail_unless(count == qdict_size(tests_dict));
|
||||
|
||||
/* Do it again to test restarting */
|
||||
count = 0;
|
||||
for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
|
||||
fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
|
||||
count++;
|
||||
}
|
||||
|
||||
fail_unless(count == qdict_size(tests_dict));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*
|
||||
* Errors test-cases
|
||||
*/
|
||||
@ -338,6 +368,7 @@ static Suite *qdict_suite(void)
|
||||
tcase_add_test(qdict_public2_tcase, qdict_haskey_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_del_test);
|
||||
tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test);
|
||||
tcase_add_test(qdict_public2_tcase, qdict_iterapi_test);
|
||||
|
||||
qdict_errors_tcase = tcase_create("Errors");
|
||||
suite_add_tcase(s, qdict_errors_tcase);
|
||||
|
16
migration.c
16
migration.c
@ -75,7 +75,9 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
MigrationState *s = NULL;
|
||||
const char *p;
|
||||
int detach = qdict_get_int(qdict, "detach");
|
||||
int detach = qdict_get_try_bool(qdict, "detach", 0);
|
||||
int blk = qdict_get_try_bool(qdict, "blk", 0);
|
||||
int inc = qdict_get_try_bool(qdict, "inc", 0);
|
||||
const char *uri = qdict_get_str(qdict, "uri");
|
||||
|
||||
if (current_migration &&
|
||||
@ -86,21 +88,17 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
|
||||
if (strstart(uri, "tcp:", &p)) {
|
||||
s = tcp_start_outgoing_migration(mon, p, max_throttle, detach,
|
||||
(int)qdict_get_int(qdict, "blk"),
|
||||
(int)qdict_get_int(qdict, "inc"));
|
||||
blk, inc);
|
||||
#if !defined(WIN32)
|
||||
} else if (strstart(uri, "exec:", &p)) {
|
||||
s = exec_start_outgoing_migration(mon, p, max_throttle, detach,
|
||||
(int)qdict_get_int(qdict, "blk"),
|
||||
(int)qdict_get_int(qdict, "inc"));
|
||||
blk, inc);
|
||||
} else if (strstart(uri, "unix:", &p)) {
|
||||
s = unix_start_outgoing_migration(mon, p, max_throttle, detach,
|
||||
(int)qdict_get_int(qdict, "blk"),
|
||||
(int)qdict_get_int(qdict, "inc"));
|
||||
blk, inc);
|
||||
} else if (strstart(uri, "fd:", &p)) {
|
||||
s = fd_start_outgoing_migration(mon, p, max_throttle, detach,
|
||||
(int)qdict_get_int(qdict, "blk"),
|
||||
(int)qdict_get_int(qdict, "inc"));
|
||||
blk, inc);
|
||||
#endif
|
||||
} else {
|
||||
monitor_printf(mon, "unknown migration protocol: %s\n", uri);
|
||||
|
431
monitor.c
431
monitor.c
@ -113,7 +113,7 @@ typedef struct mon_cmd_t {
|
||||
int (*cmd_async)(Monitor *mon, const QDict *params,
|
||||
MonitorCompletion *cb, void *opaque);
|
||||
} mhandler;
|
||||
int async;
|
||||
int flags;
|
||||
} mon_cmd_t;
|
||||
|
||||
/* file descriptors passed via SCM_RIGHTS */
|
||||
@ -178,6 +178,9 @@ static inline void mon_print_count_init(Monitor *mon) { }
|
||||
static inline int mon_print_count_get(const Monitor *mon) { return 0; }
|
||||
#endif /* CONFIG_DEBUG_MONITOR */
|
||||
|
||||
/* QMP checker flags */
|
||||
#define QMP_ACCEPT_UNKNOWNS 1
|
||||
|
||||
static QLIST_HEAD(mon_list, Monitor) mon_list;
|
||||
|
||||
static const mon_cmd_t mon_cmds[];
|
||||
@ -328,7 +331,12 @@ static inline int monitor_handler_ported(const mon_cmd_t *cmd)
|
||||
|
||||
static inline bool monitor_handler_is_async(const mon_cmd_t *cmd)
|
||||
{
|
||||
return cmd->async != 0;
|
||||
return cmd->flags & MONITOR_CMD_ASYNC;
|
||||
}
|
||||
|
||||
static inline bool monitor_cmd_user_only(const mon_cmd_t *cmd)
|
||||
{
|
||||
return (cmd->flags & MONITOR_CMD_USER_ONLY);
|
||||
}
|
||||
|
||||
static inline int monitor_has_error(const Monitor *mon)
|
||||
@ -547,10 +555,10 @@ static void qmp_monitor_complete(void *opaque, QObject *ret_data)
|
||||
monitor_protocol_emitter(opaque, ret_data);
|
||||
}
|
||||
|
||||
static void qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
|
||||
static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
|
||||
const QDict *params)
|
||||
{
|
||||
cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
|
||||
return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
|
||||
}
|
||||
|
||||
static void qmp_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
|
||||
@ -613,6 +621,11 @@ static int do_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
goto help;
|
||||
}
|
||||
|
||||
if (monitor_ctrl_mode(mon) && monitor_cmd_user_only(cmd)) {
|
||||
qerror_report(QERR_COMMAND_NOT_FOUND, item);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (monitor_handler_is_async(cmd)) {
|
||||
if (monitor_ctrl_mode(mon)) {
|
||||
qmp_async_info_handler(mon, cmd);
|
||||
@ -710,13 +723,14 @@ static void do_info_commands(Monitor *mon, QObject **ret_data)
|
||||
cmd_list = qlist_new();
|
||||
|
||||
for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
||||
if (monitor_handler_ported(cmd) && !compare_cmd(cmd->name, "info")) {
|
||||
if (monitor_handler_ported(cmd) && !monitor_cmd_user_only(cmd) &&
|
||||
!compare_cmd(cmd->name, "info")) {
|
||||
qlist_append_obj(cmd_list, get_cmd_dict(cmd->name));
|
||||
}
|
||||
}
|
||||
|
||||
for (cmd = info_cmds; cmd->name != NULL; cmd++) {
|
||||
if (monitor_handler_ported(cmd)) {
|
||||
if (monitor_handler_ported(cmd) && !monitor_cmd_user_only(cmd)) {
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "query-%s", cmd->name);
|
||||
qlist_append_obj(cmd_list, get_cmd_dict(buf));
|
||||
@ -2537,7 +2551,7 @@ static const mon_cmd_t info_cmds[] = {
|
||||
.help = "show balloon information",
|
||||
.user_print = monitor_print_balloon,
|
||||
.mhandler.info_async = do_info_balloon,
|
||||
.async = 1,
|
||||
.flags = MONITOR_CMD_ASYNC,
|
||||
},
|
||||
{
|
||||
.name = "qtree",
|
||||
@ -3566,7 +3580,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
|
||||
case '-':
|
||||
{
|
||||
const char *tmp = p;
|
||||
int has_option, skip_key = 0;
|
||||
int skip_key = 0;
|
||||
/* option */
|
||||
|
||||
c = *typestr++;
|
||||
@ -3574,7 +3588,6 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
|
||||
goto bad_type;
|
||||
while (qemu_isspace(*p))
|
||||
p++;
|
||||
has_option = 0;
|
||||
if (*p == '-') {
|
||||
p++;
|
||||
if(c != *p) {
|
||||
@ -3590,11 +3603,11 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
|
||||
if(skip_key) {
|
||||
p = tmp;
|
||||
} else {
|
||||
/* has option */
|
||||
p++;
|
||||
has_option = 1;
|
||||
qdict_put(qdict, key, qbool_from_int(1));
|
||||
}
|
||||
}
|
||||
qdict_put(qdict, key, qint_from_int(has_option));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -3883,8 +3896,9 @@ static void monitor_find_completion(const char *cmdline)
|
||||
next arg */
|
||||
len = strlen(cmdline);
|
||||
if (len > 0 && qemu_isspace(cmdline[len - 1])) {
|
||||
if (nb_args >= MAX_ARGS)
|
||||
return;
|
||||
if (nb_args >= MAX_ARGS) {
|
||||
goto cleanup;
|
||||
}
|
||||
args[nb_args++] = qemu_strdup("");
|
||||
}
|
||||
if (nb_args <= 1) {
|
||||
@ -3900,11 +3914,14 @@ static void monitor_find_completion(const char *cmdline)
|
||||
} else {
|
||||
/* find the command */
|
||||
for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
|
||||
if (compare_cmd(args[0], cmd->name))
|
||||
goto found;
|
||||
if (compare_cmd(args[0], cmd->name)) {
|
||||
break;
|
||||
}
|
||||
return;
|
||||
found:
|
||||
}
|
||||
if (!cmd->name) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ptype = next_arg_type(cmd->args_type);
|
||||
for(i = 0; i < nb_args - 2; i++) {
|
||||
if (*ptype != '\0') {
|
||||
@ -3915,7 +3932,7 @@ static void monitor_find_completion(const char *cmdline)
|
||||
}
|
||||
str = args[nb_args - 1];
|
||||
if (*ptype == '-' && ptype[1] != '\0') {
|
||||
ptype += 2;
|
||||
ptype = next_arg_type(ptype);
|
||||
}
|
||||
switch(*ptype) {
|
||||
case 'F':
|
||||
@ -3954,9 +3971,12 @@ static void monitor_find_completion(const char *cmdline)
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(i = 0; i < nb_args; i++)
|
||||
|
||||
cleanup:
|
||||
for (i = 0; i < nb_args; i++) {
|
||||
qemu_free(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int monitor_can_read(void *opaque)
|
||||
{
|
||||
@ -3965,191 +3985,256 @@ static int monitor_can_read(void *opaque)
|
||||
return (mon->suspend_cnt == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
typedef struct CmdArgs {
|
||||
QString *name;
|
||||
int type;
|
||||
int flag;
|
||||
int optional;
|
||||
} CmdArgs;
|
||||
|
||||
static int check_opt(const CmdArgs *cmd_args, const char *name, QDict *args)
|
||||
static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name)
|
||||
{
|
||||
if (!cmd_args->optional) {
|
||||
qerror_report(QERR_MISSING_PARAMETER, name);
|
||||
int is_cap = compare_cmd(cmd_name, "qmp_capabilities");
|
||||
return (qmp_cmd_mode(mon) ? is_cap : !is_cap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Argument validation rules:
|
||||
*
|
||||
* 1. The argument must exist in cmd_args qdict
|
||||
* 2. The argument type must be the expected one
|
||||
*
|
||||
* Special case: If the argument doesn't exist in cmd_args and
|
||||
* the QMP_ACCEPT_UNKNOWNS flag is set, then the
|
||||
* checking is skipped for it.
|
||||
*/
|
||||
static int check_client_args_type(const QDict *client_args,
|
||||
const QDict *cmd_args, int flags)
|
||||
{
|
||||
const QDictEntry *ent;
|
||||
|
||||
for (ent = qdict_first(client_args); ent;ent = qdict_next(client_args,ent)){
|
||||
QObject *obj;
|
||||
QString *arg_type;
|
||||
const QObject *client_arg = qdict_entry_value(ent);
|
||||
const char *client_arg_name = qdict_entry_key(ent);
|
||||
|
||||
obj = qdict_get(cmd_args, client_arg_name);
|
||||
if (!obj) {
|
||||
if (flags & QMP_ACCEPT_UNKNOWNS) {
|
||||
/* handler accepts unknowns */
|
||||
continue;
|
||||
}
|
||||
/* client arg doesn't exist */
|
||||
qerror_report(QERR_INVALID_PARAMETER, client_arg_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmd_args->type == '-') {
|
||||
/* handlers expect a value, they need to be changed */
|
||||
qdict_put(args, name, qint_from_int(0));
|
||||
}
|
||||
arg_type = qobject_to_qstring(obj);
|
||||
assert(arg_type != NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_arg(const CmdArgs *cmd_args, QDict *args)
|
||||
{
|
||||
QObject *value;
|
||||
const char *name;
|
||||
|
||||
name = qstring_get_str(cmd_args->name);
|
||||
|
||||
if (!args) {
|
||||
return check_opt(cmd_args, name, args);
|
||||
}
|
||||
|
||||
value = qdict_get(args, name);
|
||||
if (!value) {
|
||||
return check_opt(cmd_args, name, args);
|
||||
}
|
||||
|
||||
switch (cmd_args->type) {
|
||||
/* check if argument's type is correct */
|
||||
switch (qstring_get_str(arg_type)[0]) {
|
||||
case 'F':
|
||||
case 'B':
|
||||
case 's':
|
||||
if (qobject_type(value) != QTYPE_QSTRING) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "string");
|
||||
if (qobject_type(client_arg) != QTYPE_QSTRING) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
||||
"string");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case '/': {
|
||||
int i;
|
||||
const char *keys[] = { "count", "format", "size", NULL };
|
||||
|
||||
for (i = 0; keys[i]; i++) {
|
||||
QObject *obj = qdict_get(args, keys[i]);
|
||||
if (!obj) {
|
||||
qerror_report(QERR_MISSING_PARAMETER, name);
|
||||
return -1;
|
||||
}
|
||||
if (qobject_type(obj) != QTYPE_QINT) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "int");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
case 'l':
|
||||
case 'M':
|
||||
if (qobject_type(value) != QTYPE_QINT) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "int");
|
||||
if (qobject_type(client_arg) != QTYPE_QINT) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
||||
"int");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case 'T':
|
||||
if (qobject_type(value) != QTYPE_QINT && qobject_type(value) != QTYPE_QFLOAT) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "number");
|
||||
if (qobject_type(client_arg) != QTYPE_QINT &&
|
||||
qobject_type(client_arg) != QTYPE_QFLOAT) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
||||
"number");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
if (qobject_type(value) != QTYPE_QBOOL) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "bool");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
if (qobject_type(value) != QTYPE_QINT &&
|
||||
qobject_type(value) != QTYPE_QBOOL) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "bool");
|
||||
if (qobject_type(client_arg) != QTYPE_QBOOL) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
|
||||
"bool");
|
||||
return -1;
|
||||
}
|
||||
if (qobject_type(value) == QTYPE_QBOOL) {
|
||||
/* handlers expect a QInt, they need to be changed */
|
||||
qdict_put(args, name,
|
||||
qint_from_int(qbool_get_int(qobject_to_qbool(value))));
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
assert(flags & QMP_ACCEPT_UNKNOWNS);
|
||||
break;
|
||||
case '/':
|
||||
case '.':
|
||||
/*
|
||||
* These types are not supported by QMP and thus are not
|
||||
* handled here. Fall through.
|
||||
*/
|
||||
default:
|
||||
/* impossible */
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cmd_args_init(CmdArgs *cmd_args)
|
||||
{
|
||||
cmd_args->name = qstring_new();
|
||||
cmd_args->type = cmd_args->flag = cmd_args->optional = 0;
|
||||
}
|
||||
|
||||
static int check_opts(QemuOptsList *opts_list, QDict *args)
|
||||
{
|
||||
assert(!opts_list->desc->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is not trivial, we have to parse Monitor command's argument
|
||||
* type syntax to be able to check the arguments provided by clients.
|
||||
*
|
||||
* In the near future we will be using an array for that and will be
|
||||
* able to drop all this parsing...
|
||||
* - Check if the client has passed all mandatory args
|
||||
* - Set special flags for argument validation
|
||||
*/
|
||||
static int monitor_check_qmp_args(const mon_cmd_t *cmd, QDict *args)
|
||||
static int check_mandatory_args(const QDict *cmd_args,
|
||||
const QDict *client_args, int *flags)
|
||||
{
|
||||
int err;
|
||||
const char *p;
|
||||
CmdArgs cmd_args;
|
||||
QemuOptsList *opts_list;
|
||||
const QDictEntry *ent;
|
||||
|
||||
if (cmd->args_type == NULL) {
|
||||
return (qdict_size(args) == 0 ? 0 : -1);
|
||||
for (ent = qdict_first(cmd_args); ent; ent = qdict_next(cmd_args, ent)) {
|
||||
const char *cmd_arg_name = qdict_entry_key(ent);
|
||||
QString *type = qobject_to_qstring(qdict_entry_value(ent));
|
||||
assert(type != NULL);
|
||||
|
||||
if (qstring_get_str(type)[0] == 'O') {
|
||||
assert((*flags & QMP_ACCEPT_UNKNOWNS) == 0);
|
||||
*flags |= QMP_ACCEPT_UNKNOWNS;
|
||||
} else if (qstring_get_str(type)[0] != '-' &&
|
||||
qstring_get_str(type)[1] != '?' &&
|
||||
!qdict_haskey(client_args, cmd_arg_name)) {
|
||||
qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
cmd_args_init(&cmd_args);
|
||||
opts_list = NULL;
|
||||
|
||||
for (p = cmd->args_type;; p++) {
|
||||
if (*p == ':') {
|
||||
cmd_args.type = *++p;
|
||||
p++;
|
||||
if (cmd_args.type == '-') {
|
||||
cmd_args.flag = *p++;
|
||||
cmd_args.optional = 1;
|
||||
} else if (cmd_args.type == 'O') {
|
||||
opts_list = qemu_find_opts(qstring_get_str(cmd_args.name));
|
||||
assert(opts_list);
|
||||
} else if (*p == '?') {
|
||||
cmd_args.optional = 1;
|
||||
p++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(*p == ',' || *p == '\0');
|
||||
if (opts_list) {
|
||||
err = check_opts(opts_list, args);
|
||||
opts_list = NULL;
|
||||
} else {
|
||||
err = check_arg(&cmd_args, args);
|
||||
QDECREF(cmd_args.name);
|
||||
cmd_args_init(&cmd_args);
|
||||
static QDict *qdict_from_args_type(const char *args_type)
|
||||
{
|
||||
int i;
|
||||
QDict *qdict;
|
||||
QString *key, *type, *cur_qs;
|
||||
|
||||
assert(args_type != NULL);
|
||||
|
||||
qdict = qdict_new();
|
||||
|
||||
if (args_type == NULL || args_type[0] == '\0') {
|
||||
/* no args, empty qdict */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
key = qstring_new();
|
||||
type = qstring_new();
|
||||
|
||||
cur_qs = key;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
switch (args_type[i]) {
|
||||
case ',':
|
||||
case '\0':
|
||||
qdict_put(qdict, qstring_get_str(key), type);
|
||||
QDECREF(key);
|
||||
if (args_type[i] == '\0') {
|
||||
goto out;
|
||||
}
|
||||
type = qstring_new(); /* qdict has ref */
|
||||
cur_qs = key = qstring_new();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
qstring_append_chr(cmd_args.name, *p);
|
||||
}
|
||||
|
||||
if (*p == '\0') {
|
||||
case ':':
|
||||
cur_qs = type;
|
||||
break;
|
||||
default:
|
||||
qstring_append_chr(cur_qs, args_type[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QDECREF(cmd_args.name);
|
||||
out:
|
||||
return qdict;
|
||||
}
|
||||
|
||||
/*
|
||||
* Client argument checking rules:
|
||||
*
|
||||
* 1. Client must provide all mandatory arguments
|
||||
* 2. Each argument provided by the client must be expected
|
||||
* 3. Each argument provided by the client must have the type expected
|
||||
* by the command
|
||||
*/
|
||||
static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args)
|
||||
{
|
||||
int flags, err;
|
||||
QDict *cmd_args;
|
||||
|
||||
cmd_args = qdict_from_args_type(cmd->args_type);
|
||||
|
||||
flags = 0;
|
||||
err = check_mandatory_args(cmd_args, client_args, &flags);
|
||||
if (err) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = check_client_args_type(client_args, cmd_args, flags);
|
||||
|
||||
out:
|
||||
QDECREF(cmd_args);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name)
|
||||
/*
|
||||
* Input object checking rules
|
||||
*
|
||||
* 1. Input object must be a dict
|
||||
* 2. The "execute" key must exist
|
||||
* 3. The "execute" key must be a string
|
||||
* 4. If the "arguments" key exists, it must be a dict
|
||||
* 5. If the "id" key exists, it can be anything (ie. json-value)
|
||||
* 6. Any argument not listed above is considered invalid
|
||||
*/
|
||||
static QDict *qmp_check_input_obj(QObject *input_obj)
|
||||
{
|
||||
int is_cap = compare_cmd(cmd_name, "qmp_capabilities");
|
||||
return (qmp_cmd_mode(mon) ? is_cap : !is_cap);
|
||||
const QDictEntry *ent;
|
||||
int has_exec_key = 0;
|
||||
QDict *input_dict;
|
||||
|
||||
if (qobject_type(input_obj) != QTYPE_QDICT) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
input_dict = qobject_to_qdict(input_obj);
|
||||
|
||||
for (ent = qdict_first(input_dict); ent; ent = qdict_next(input_dict, ent)){
|
||||
const char *arg_name = qdict_entry_key(ent);
|
||||
const QObject *arg_obj = qdict_entry_value(ent);
|
||||
|
||||
if (!strcmp(arg_name, "execute")) {
|
||||
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
|
||||
"string");
|
||||
return NULL;
|
||||
}
|
||||
has_exec_key = 1;
|
||||
} else if (!strcmp(arg_name, "arguments")) {
|
||||
if (qobject_type(arg_obj) != QTYPE_QDICT) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments",
|
||||
"object");
|
||||
return NULL;
|
||||
}
|
||||
} else if (!strcmp(arg_name, "id")) {
|
||||
/* FIXME: check duplicated IDs for async commands */
|
||||
} else {
|
||||
qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_exec_key) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return input_dict;
|
||||
}
|
||||
|
||||
static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
|
||||
@ -4161,38 +4246,28 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
|
||||
Monitor *mon = cur_mon;
|
||||
const char *cmd_name, *info_item;
|
||||
|
||||
args = NULL;
|
||||
args = input = NULL;
|
||||
|
||||
obj = json_parser_parse(tokens, NULL);
|
||||
if (!obj) {
|
||||
// FIXME: should be triggered in json_parser_parse()
|
||||
qerror_report(QERR_JSON_PARSING);
|
||||
goto err_out;
|
||||
} else if (qobject_type(obj) != QTYPE_QDICT) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object");
|
||||
}
|
||||
|
||||
input = qmp_check_input_obj(obj);
|
||||
if (!input) {
|
||||
qobject_decref(obj);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
input = qobject_to_qdict(obj);
|
||||
|
||||
mon->mc->id = qdict_get(input, "id");
|
||||
qobject_incref(mon->mc->id);
|
||||
|
||||
obj = qdict_get(input, "execute");
|
||||
if (!obj) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute");
|
||||
goto err_input;
|
||||
} else if (qobject_type(obj) != QTYPE_QSTRING) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", "string");
|
||||
goto err_input;
|
||||
}
|
||||
|
||||
cmd_name = qstring_get_str(qobject_to_qstring(obj));
|
||||
|
||||
cmd_name = qdict_get_str(input, "execute");
|
||||
if (invalid_qmp_mode(mon, cmd_name)) {
|
||||
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
|
||||
goto err_input;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4201,49 +4276,49 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
|
||||
*/
|
||||
if (compare_cmd(cmd_name, "info")) {
|
||||
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
|
||||
goto err_input;
|
||||
goto err_out;
|
||||
} else if (strstart(cmd_name, "query-", &info_item)) {
|
||||
cmd = monitor_find_command("info");
|
||||
qdict_put_obj(input, "arguments",
|
||||
qobject_from_jsonf("{ 'item': %s }", info_item));
|
||||
} else {
|
||||
cmd = monitor_find_command(cmd_name);
|
||||
if (!cmd || !monitor_handler_ported(cmd)) {
|
||||
if (!cmd || !monitor_handler_ported(cmd)
|
||||
|| monitor_cmd_user_only(cmd)) {
|
||||
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
|
||||
goto err_input;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
obj = qdict_get(input, "arguments");
|
||||
if (!obj) {
|
||||
args = qdict_new();
|
||||
} else if (qobject_type(obj) != QTYPE_QDICT) {
|
||||
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments", "object");
|
||||
goto err_input;
|
||||
} else {
|
||||
args = qobject_to_qdict(obj);
|
||||
QINCREF(args);
|
||||
}
|
||||
|
||||
QDECREF(input);
|
||||
|
||||
err = monitor_check_qmp_args(cmd, args);
|
||||
err = qmp_check_client_args(cmd, args);
|
||||
if (err < 0) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (monitor_handler_is_async(cmd)) {
|
||||
qmp_async_cmd_handler(mon, cmd, args);
|
||||
err = qmp_async_cmd_handler(mon, cmd, args);
|
||||
if (err) {
|
||||
/* emit the error response */
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
monitor_call_handler(mon, cmd, args);
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
err_input:
|
||||
QDECREF(input);
|
||||
err_out:
|
||||
monitor_protocol_emitter(mon, NULL);
|
||||
out:
|
||||
QDECREF(input);
|
||||
QDECREF(args);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,10 @@ extern Monitor *default_mon;
|
||||
#define MONITOR_USE_READLINE 0x02
|
||||
#define MONITOR_USE_CONTROL 0x04
|
||||
|
||||
/* flags for monitor commands */
|
||||
#define MONITOR_CMD_ASYNC 0x0001
|
||||
#define MONITOR_CMD_USER_ONLY 0x0002
|
||||
|
||||
/* QMP events */
|
||||
typedef enum MonitorEvent {
|
||||
QEVENT_SHUTDOWN,
|
||||
|
4
net.c
4
net.c
@ -1208,6 +1208,10 @@ int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
}
|
||||
|
||||
res = net_client_init(mon, opts, 1);
|
||||
if (res < 0) {
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
106
qdict.c
106
qdict.c
@ -82,15 +82,36 @@ static QDictEntry *alloc_entry(const char *key, QObject *value)
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_entry_value(): Return qdict entry value
|
||||
*
|
||||
* Return weak reference.
|
||||
*/
|
||||
QObject *qdict_entry_value(const QDictEntry *entry)
|
||||
{
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_entry_key(): Return qdict entry key
|
||||
*
|
||||
* Return a *pointer* to the string, it has to be duplicated before being
|
||||
* stored.
|
||||
*/
|
||||
const char *qdict_entry_key(const QDictEntry *entry)
|
||||
{
|
||||
return entry->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_find(): List lookup function
|
||||
*/
|
||||
static QDictEntry *qdict_find(const QDict *qdict,
|
||||
const char *key, unsigned int hash)
|
||||
const char *key, unsigned int bucket)
|
||||
{
|
||||
QDictEntry *entry;
|
||||
|
||||
QLIST_FOREACH(entry, &qdict->table[hash], next)
|
||||
QLIST_FOREACH(entry, &qdict->table[bucket], next)
|
||||
if (!strcmp(entry->key, key))
|
||||
return entry;
|
||||
|
||||
@ -110,11 +131,11 @@ static QDictEntry *qdict_find(const QDict *qdict,
|
||||
*/
|
||||
void qdict_put_obj(QDict *qdict, const char *key, QObject *value)
|
||||
{
|
||||
unsigned int hash;
|
||||
unsigned int bucket;
|
||||
QDictEntry *entry;
|
||||
|
||||
hash = tdb_hash(key) % QDICT_HASH_SIZE;
|
||||
entry = qdict_find(qdict, key, hash);
|
||||
bucket = tdb_hash(key) % QDICT_BUCKET_MAX;
|
||||
entry = qdict_find(qdict, key, bucket);
|
||||
if (entry) {
|
||||
/* replace key's value */
|
||||
qobject_decref(entry->value);
|
||||
@ -122,7 +143,7 @@ void qdict_put_obj(QDict *qdict, const char *key, QObject *value)
|
||||
} else {
|
||||
/* allocate a new entry */
|
||||
entry = alloc_entry(key, value);
|
||||
QLIST_INSERT_HEAD(&qdict->table[hash], entry, next);
|
||||
QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next);
|
||||
qdict->size++;
|
||||
}
|
||||
}
|
||||
@ -137,7 +158,7 @@ QObject *qdict_get(const QDict *qdict, const char *key)
|
||||
{
|
||||
QDictEntry *entry;
|
||||
|
||||
entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_HASH_SIZE);
|
||||
entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX);
|
||||
return (entry == NULL ? NULL : entry->value);
|
||||
}
|
||||
|
||||
@ -148,8 +169,8 @@ QObject *qdict_get(const QDict *qdict, const char *key)
|
||||
*/
|
||||
int qdict_haskey(const QDict *qdict, const char *key)
|
||||
{
|
||||
unsigned int hash = tdb_hash(key) % QDICT_HASH_SIZE;
|
||||
return (qdict_find(qdict, key, hash) == NULL ? 0 : 1);
|
||||
unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX;
|
||||
return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,20 +293,38 @@ const char *qdict_get_str(const QDict *qdict, const char *key)
|
||||
*
|
||||
* Return integer mapped by 'key', if it is not present in
|
||||
* the dictionary or if the stored object is not of QInt type
|
||||
* 'err_value' will be returned.
|
||||
* 'def_value' will be returned.
|
||||
*/
|
||||
int64_t qdict_get_try_int(const QDict *qdict, const char *key,
|
||||
int64_t err_value)
|
||||
int64_t def_value)
|
||||
{
|
||||
QObject *obj;
|
||||
|
||||
obj = qdict_get(qdict, key);
|
||||
if (!obj || qobject_type(obj) != QTYPE_QINT)
|
||||
return err_value;
|
||||
return def_value;
|
||||
|
||||
return qint_get_int(qobject_to_qint(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_get_try_bool(): Try to get a bool mapped by 'key'
|
||||
*
|
||||
* Return bool mapped by 'key', if it is not present in the
|
||||
* dictionary or if the stored object is not of QBool type
|
||||
* 'def_value' will be returned.
|
||||
*/
|
||||
int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value)
|
||||
{
|
||||
QObject *obj;
|
||||
|
||||
obj = qdict_get(qdict, key);
|
||||
if (!obj || qobject_type(obj) != QTYPE_QBOOL)
|
||||
return def_value;
|
||||
|
||||
return qbool_get_int(qobject_to_qbool(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_get_try_str(): Try to get a pointer to the stored string
|
||||
* mapped by 'key'
|
||||
@ -318,12 +357,49 @@ void qdict_iter(const QDict *qdict,
|
||||
int i;
|
||||
QDictEntry *entry;
|
||||
|
||||
for (i = 0; i < QDICT_HASH_SIZE; i++) {
|
||||
for (i = 0; i < QDICT_BUCKET_MAX; i++) {
|
||||
QLIST_FOREACH(entry, &qdict->table[i], next)
|
||||
iter(entry->key, entry->value, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) {
|
||||
if (!QLIST_EMPTY(&qdict->table[i])) {
|
||||
return QLIST_FIRST(&qdict->table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_first(): Return first qdict entry for iteration.
|
||||
*/
|
||||
const QDictEntry *qdict_first(const QDict *qdict)
|
||||
{
|
||||
return qdict_next_entry(qdict, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_next(): Return next qdict entry in an iteration.
|
||||
*/
|
||||
const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry)
|
||||
{
|
||||
QDictEntry *ret;
|
||||
|
||||
ret = QLIST_NEXT(entry, next);
|
||||
if (!ret) {
|
||||
unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX;
|
||||
ret = qdict_next_entry(qdict, bucket + 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* qentry_destroy(): Free all the memory allocated by a QDictEntry
|
||||
*/
|
||||
@ -347,7 +423,7 @@ void qdict_del(QDict *qdict, const char *key)
|
||||
{
|
||||
QDictEntry *entry;
|
||||
|
||||
entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_HASH_SIZE);
|
||||
entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX);
|
||||
if (entry) {
|
||||
QLIST_REMOVE(entry, next);
|
||||
qentry_destroy(entry);
|
||||
@ -366,7 +442,7 @@ static void qdict_destroy_obj(QObject *obj)
|
||||
assert(obj != NULL);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
|
||||
for (i = 0; i < QDICT_HASH_SIZE; i++) {
|
||||
for (i = 0; i < QDICT_BUCKET_MAX; i++) {
|
||||
QDictEntry *entry = QLIST_FIRST(&qdict->table[i]);
|
||||
while (entry) {
|
||||
QDictEntry *tmp = QLIST_NEXT(entry, next);
|
||||
|
11
qdict.h
11
qdict.h
@ -18,7 +18,7 @@
|
||||
#include "qemu-queue.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define QDICT_HASH_SIZE 512
|
||||
#define QDICT_BUCKET_MAX 512
|
||||
|
||||
typedef struct QDictEntry {
|
||||
char *key;
|
||||
@ -29,11 +29,13 @@ typedef struct QDictEntry {
|
||||
typedef struct QDict {
|
||||
QObject_HEAD;
|
||||
size_t size;
|
||||
QLIST_HEAD(,QDictEntry) table[QDICT_HASH_SIZE];
|
||||
QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX];
|
||||
} QDict;
|
||||
|
||||
/* Object API */
|
||||
QDict *qdict_new(void);
|
||||
const char *qdict_entry_key(const QDictEntry *entry);
|
||||
QObject *qdict_entry_value(const QDictEntry *entry);
|
||||
size_t qdict_size(const QDict *qdict);
|
||||
void qdict_put_obj(QDict *qdict, const char *key, QObject *value);
|
||||
void qdict_del(QDict *qdict, const char *key);
|
||||
@ -43,6 +45,8 @@ QDict *qobject_to_qdict(const QObject *obj);
|
||||
void qdict_iter(const QDict *qdict,
|
||||
void (*iter)(const char *key, QObject *obj, void *opaque),
|
||||
void *opaque);
|
||||
const QDictEntry *qdict_first(const QDict *qdict);
|
||||
const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry);
|
||||
|
||||
/* Helper to qdict_put_obj(), accepts any object */
|
||||
#define qdict_put(qdict, key, obj) \
|
||||
@ -56,7 +60,8 @@ QList *qdict_get_qlist(const QDict *qdict, const char *key);
|
||||
QDict *qdict_get_qdict(const QDict *qdict, const char *key);
|
||||
const char *qdict_get_str(const QDict *qdict, const char *key);
|
||||
int64_t qdict_get_try_int(const QDict *qdict, const char *key,
|
||||
int64_t err_value);
|
||||
int64_t def_value);
|
||||
int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value);
|
||||
const char *qdict_get_try_str(const QDict *qdict, const char *key);
|
||||
|
||||
#endif /* QDICT_H */
|
||||
|
@ -1287,7 +1287,7 @@ ETEXI
|
||||
.help = "request VM to change its memory allocation (in MB)",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd_async = do_balloon,
|
||||
.async = 1,
|
||||
.flags = MONITOR_CMD_ASYNC,
|
||||
},
|
||||
|
||||
STEXI
|
||||
|
6
qerror.c
6
qerror.c
@ -82,7 +82,7 @@ static const QErrorStringTable qerror_table[] = {
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_DEVICE_NOT_ACTIVE,
|
||||
.desc = "Device '%(device)' has not been activated by the guest",
|
||||
.desc = "Device '%(device)' has not been activated",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_DEVICE_NOT_ENCRYPTED,
|
||||
@ -176,6 +176,10 @@ static const QErrorStringTable qerror_table[] = {
|
||||
.error_fmt = QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
|
||||
.desc = "QMP input object member '%(member)' expects '%(expected)'",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_QMP_EXTRA_MEMBER,
|
||||
.desc = "QMP input object member '%(member)' is unexpected",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_SET_PASSWD_FAILED,
|
||||
.desc = "Could not set password",
|
||||
|
3
qerror.h
3
qerror.h
@ -148,6 +148,9 @@ QError *qobject_to_qerror(const QObject *obj);
|
||||
#define QERR_QMP_BAD_INPUT_OBJECT_MEMBER \
|
||||
"{ 'class': 'QMPBadInputObjectMember', 'data': { 'member': %s, 'expected': %s } }"
|
||||
|
||||
#define QERR_QMP_EXTRA_MEMBER \
|
||||
"{ 'class': 'QMPExtraInputObjectMember', 'data': { 'member': %s } }"
|
||||
|
||||
#define QERR_SET_PASSWD_FAILED \
|
||||
"{ 'class': 'SetPasswdFailed', 'data': {} }"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user