diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2010-07-06 08:31:17 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2010-07-06 08:31:17 -0500 |
commit | 02d0ba1420803562109185f47be6f7430bfdefae (patch) | |
tree | 7d7185e9edd5bcf30ea38760d2a7f12fd21bb1c0 | |
parent | 9f5a1fae7ee1a7c66462e5b8e9d21552d4dc5027 (diff) | |
parent | a6c4d36425871fafc55ce3937bebd05e86f5ea81 (diff) |
Merge remote branch 'qmp/for-anthony' into staging
-rwxr-xr-x | QMP/qmp-shell | 1 | ||||
-rw-r--r-- | QMP/qmp.py | 6 | ||||
-rwxr-xr-x | QMP/vm-info | 1 | ||||
-rw-r--r-- | blockdev.c | 2 | ||||
-rw-r--r-- | check-qdict.c | 33 | ||||
-rw-r--r-- | migration.c | 16 | ||||
-rw-r--r-- | monitor.c | 435 | ||||
-rw-r--r-- | monitor.h | 4 | ||||
-rw-r--r-- | net.c | 4 | ||||
-rw-r--r-- | qdict.c | 106 | ||||
-rw-r--r-- | qdict.h | 11 | ||||
-rw-r--r-- | qemu-monitor.hx | 2 | ||||
-rw-r--r-- | qerror.c | 6 | ||||
-rw-r--r-- | qerror.h | 3 |
14 files changed, 418 insertions, 212 deletions
diff --git a/QMP/qmp-shell b/QMP/qmp-shell index f89b9af87e..a5b72d15d1 100755 --- a/QMP/qmp-shell +++ b/QMP/qmp-shell @@ -42,6 +42,7 @@ def main(): qemu = qmp.QEMUMonitorProtocol(argv[1]) qemu.connect() + qemu.send("qmp_capabilities") print 'Connected!' diff --git a/QMP/qmp.py b/QMP/qmp.py index d9da603bec..4062f84f36 100644 --- a/QMP/qmp.py +++ b/QMP/qmp.py @@ -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() diff --git a/QMP/vm-info b/QMP/vm-info index 8ebaeb38c0..be5b03843c 100755 --- a/QMP/vm-info +++ b/QMP/vm-info @@ -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)) diff --git a/blockdev.c b/blockdev.c index 3b8c6067c7..4dcfad89c5 100644 --- a/blockdev.c +++ b/blockdev.c @@ -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); diff --git a/check-qdict.c b/check-qdict.c index 2c3089fa66..6afce5a5ca 100644 --- a/check-qdict.c +++ b/check-qdict.c @@ -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); diff --git a/migration.c b/migration.c index b49964c5e3..650eb78d26 100644 --- a/migration.c +++ b/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); @@ -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, - const QDict *params) +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) { @@ -3899,12 +3913,15 @@ 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; + for (cmd = mon_cmds; cmd->name != NULL; cmd++) { + 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,8 +3971,11 @@ 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); - return -1; - } - - if (cmd_args->type == '-') { - /* handlers expect a value, they need to be changed */ - qdict_put(args, name, qint_from_int(0)); - } - - return 0; + int is_cap = compare_cmd(cmd_name, "qmp_capabilities"); + return (qmp_cmd_mode(mon) ? is_cap : !is_cap); } -static int check_arg(const CmdArgs *cmd_args, QDict *args) +/* + * 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) { - QObject *value; - const char *name; + const QDictEntry *ent; - name = qstring_get_str(cmd_args->name); - - if (!args) { - return check_opt(cmd_args, name, args); - } + 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; + } - value = qdict_get(args, name); - if (!value) { - return check_opt(cmd_args, name, args); - } + arg_type = qobject_to_qstring(obj); + assert(arg_type != NULL); - 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; - } + break; case 'i': case 'l': case 'M': - if (qobject_type(value) != QTYPE_QINT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "int"); - return -1; + 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"); - return -1; + 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"); - 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)))); + if (qobject_type(client_arg) != QTYPE_QBOOL) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "bool"); + return -1; } 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; +} + +/* + * - Check if the client has passed all mandatory args + * - Set special flags for argument validation + */ +static int check_mandatory_args(const QDict *cmd_args, + const QDict *client_args, int *flags) +{ + const QDictEntry *ent; + + 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; + } } return 0; } -static void cmd_args_init(CmdArgs *cmd_args) +static QDict *qdict_from_args_type(const char *args_type) { - cmd_args->name = qstring_new(); - cmd_args->type = cmd_args->flag = cmd_args->optional = 0; + 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; + } + + 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; + case ':': + cur_qs = type; + break; + default: + qstring_append_chr(cur_qs, args_type[i]); + break; + } + } + +out: + return qdict; } -static int check_opts(QemuOptsList *opts_list, QDict *args) +/* + * 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) { - assert(!opts_list->desc->name); - return 0; + 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; } /* - * This is not trivial, we have to parse Monitor command's argument - * type syntax to be able to check the arguments provided by clients. + * Input object checking rules * - * In the near future we will be using an array for that and will be - * able to drop all this parsing... + * 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 int monitor_check_qmp_args(const mon_cmd_t *cmd, QDict *args) +static QDict *qmp_check_input_obj(QObject *input_obj) { - int err; - const char *p; - CmdArgs cmd_args; - QemuOptsList *opts_list; + const QDictEntry *ent; + int has_exec_key = 0; + QDict *input_dict; - if (cmd->args_type == NULL) { - return (qdict_size(args) == 0 ? 0 : -1); + if (qobject_type(input_obj) != QTYPE_QDICT) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object"); + return NULL; } - err = 0; - cmd_args_init(&cmd_args); - opts_list = NULL; + input_dict = qobject_to_qdict(input_obj); - 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++; - } + 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); - 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); + if (!strcmp(arg_name, "execute")) { + if (qobject_type(arg_obj) != QTYPE_QSTRING) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", + "string"); + return NULL; } - - if (err < 0) { - break; + 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 { - qstring_append_chr(cmd_args.name, *p); - } - - if (*p == '\0') { - break; + qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name); + return NULL; } } - QDECREF(cmd_args.name); - return err; -} + if (!has_exec_key) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute"); + return NULL; + } -static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name) -{ - int is_cap = compare_cmd(cmd_name, "qmp_capabilities"); - return (qmp_cmd_mode(mon) ? is_cap : !is_cap); + 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, @@ -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; } @@ -83,14 +83,35 @@ static QDictEntry *alloc_entry(const char *key, QObject *value) } /** + * 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,21 +293,39 @@ 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); @@ -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 */ diff --git a/qemu-monitor.hx b/qemu-monitor.hx index 9f62b94862..2af3de6c22 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -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 @@ -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, @@ -177,6 +177,10 @@ static const QErrorStringTable qerror_table[] = { .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", }, @@ -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': {} }" |