QMP input is handled by monitor_handle_qmp_command(). This function's job is to check if the input is correct and if so call the appropriate handler. In other words, it does for QMP what monitor_parse_command() does for the user protocol.
This means that monitor_handle_qmp_command() also has to parse the (ugly) "args_type" format to able to get the arguments names and types expected by the handler. The format to input commands to QMP is as follows: { "execute": json-string, "id": json-value, "arguments": json-object } Please, note that this commit also adds "id" support. TODO: Use QJSON to read from the user TODO: Errors need to be reviewed Signed-off-by: Luiz Capitulino <lcapitul...@redhat.com> --- monitor.c | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 244 insertions(+), 7 deletions(-) diff --git a/monitor.c b/monitor.c index e81f9e6..5c5ae97 100644 --- a/monitor.c +++ b/monitor.c @@ -96,6 +96,7 @@ struct mon_fd_t { typedef struct MonitorControl { uint8_t buf[1024]; int size; + QObject *id; } MonitorControl; struct Monitor { @@ -296,6 +297,11 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) mon->error = NULL; } + if (mon->mc->id) { + qdict_put_obj(qmp, "id", mon->mc->id); + mon->mc->id = NULL; + } + monitor_json_emitter(mon, QOBJECT(qmp)); QDECREF(qmp); } @@ -374,16 +380,27 @@ static void do_info(Monitor *mon, const QDict *qdict, QObject **ret_data) const mon_cmd_t *cmd; const char *item = qdict_get_try_str(qdict, "item"); - if (!item) + if (!item) { + if (monitor_ctrl_mode(mon)) { + /* 'item' is mandatory in ctrl mode */ + qemu_error_new(QERR_COMMAND_NOT_ISSUED); + return; + } goto help; + } for (cmd = info_cmds; cmd->name != NULL; cmd++) { if (compare_cmd(item, cmd->name)) break; } - if (cmd->name == NULL) + if (cmd->name == NULL) { + if (monitor_ctrl_mode(mon)) { + qemu_error_new(QERR_COMMAND_NOT_FOUND, item); + return; + } goto help; + } if (monitor_handler_ported(cmd)) { cmd->mhandler.info_new(mon, ret_data); @@ -397,7 +414,12 @@ static void do_info(Monitor *mon, const QDict *qdict, QObject **ret_data) cmd->user_print(mon, *ret_data); } } else { - cmd->mhandler.info(mon); + if (monitor_ctrl_mode(mon)) { + /* handler not converted yet */ + qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd->name); + } else { + cmd->mhandler.info(mon); + } } return; @@ -3355,10 +3377,15 @@ static void monitor_handle_command(Monitor *mon, const char *cmdline) if (monitor_handler_ported(cmd)) { monitor_call_handler(mon, cmd, qdict); } else { - cmd->mhandler.cmd(mon, qdict); + if (monitor_ctrl_mode(mon)) { + /* handler not converted yet */ + qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd->name); + } else { + cmd->mhandler.cmd(mon, qdict); - if (monitor_has_error(mon)) { - monitor_print_error(mon); + if (monitor_has_error(mon)) { + monitor_print_error(mon); + } } } @@ -3590,6 +3617,216 @@ static int monitor_can_read(void *opaque) return (mon->suspend_cnt == 0) ? 128 : 0; } +typedef struct CmdArgs { + QString *name; + int type; + int flag; + int optional; +} CmdArgs; + +static int check_opt(const CmdArgs *cmd_args, const char *name) +{ + if (!cmd_args->optional) { + qemu_error_new(QERR_INVALID_PARAMETER, name, "parameter required"); + return -1; + } + + return 0; +} + +static int check_arg(const CmdArgs *cmd_args, const QDict *args) +{ + QObject *value; + const char *name; + + name = qstring_get_str(cmd_args->name); + + if (!args) { + return check_opt(cmd_args, name); + } + + value = qdict_get(args, name); + if (!value) { + return check_opt(cmd_args, name); + } + + switch (cmd_args->type) { + case 'F': + case 'B': + case 's': + if (qobject_type(value) != QTYPE_QSTRING) { + qemu_error_new(QERR_INVALID_PARAMETER, name,"must be a 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) { + qemu_error_new(QERR_INVALID_PARAMETER, name, + "missing 'count', 'format' or 'size'"); + return -1; + } + if (qobject_type(obj) != QTYPE_QINT) { + qemu_error_new(QERR_INVALID_PARAMETER, name, + "must be integer"); + return -1; + } + } + break; + } + case 'i': + case 'l': + case '-': + if (qobject_type(value) != QTYPE_QINT) { + qemu_error_new(QERR_INVALID_PARAMETER, name, + "must be an integer"); + return -1; + } + break; + 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; +} + +/* + * 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 use an array for that and will drop all + * this parsing... + */ +static int monitor_check_qmp_args(const mon_cmd_t *cmd, const QDict *args) +{ + int err; + const char *p; + CmdArgs cmd_args; + + if (cmd->args_type == '\0') { + return (qdict_size(args) == 0 ? 0 : -1); + } + + err = 0; + cmd_args_init(&cmd_args); + + for (p = cmd->args_type;; p++) { + if (*p == ':') { + cmd_args.type = *++p; + p++; + if (cmd_args.type == '-') { + cmd_args.flag = *p++; + } else if (*p == '?') { + cmd_args.optional = 1; + p++; + } + + assert(*p == ',' || *p == '\0'); + err = check_arg(&cmd_args, args); + + QDECREF(cmd_args.name); + cmd_args_init(&cmd_args); + + if (err < 0) { + break; + } + } else { + qstring_append_chr(cmd_args.name, *p); + } + + if (*p == '\0') { + break; + } + } + + QDECREF(cmd_args.name); + return err; +} + +static void monitor_handle_qmp_command(Monitor *mon) +{ + int err; + QObject *obj; + QDict *input, *args; + const char *cmd_name; + const mon_cmd_t *cmd; + + qemu_errors_to_mon(mon); + + obj = qobject_from_json((char *) mon->mc->buf); + if (!obj) { + // FIXME: should be triggered in qobject_from_json() + qemu_error_new(QERR_BAD_SYNTAX, "parsing error"); + goto error; + } else if (qobject_type(obj) != QTYPE_QDICT) { + qemu_error_new(QERR_BAD_SYNTAX, "should be a dictionary"); + qobject_decref(obj); + goto error; + } + + input = qobject_to_qdict(obj); + + mon->mc->id = qdict_get(input, "id"); + qobject_incref(mon->mc->id); + + if (!qdict_haskey(input, "execute")) { + qemu_error_new(QERR_BAD_SYNTAX, "missing 'execute' keyword"); + QDECREF(input); + goto error; + } + + cmd_name = qdict_get_str(input, "execute"); + for (cmd = mon_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(cmd_name, cmd->name)) { + break; + } + } + + if (cmd->name == NULL) { + qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd_name); + QDECREF(input); + goto error; + } + + obj = qdict_get(input, "arguments"); + if (!obj) { + QDECREF(input); + args = qdict_new(); + } else { + args = qobject_to_qdict(obj); + QINCREF(args); + QDECREF(input); + } + + err = monitor_check_qmp_args(cmd, args); + if (err < 0) { + QDECREF(args); + goto error; + } + + monitor_call_handler(mon, cmd, args); + + QDECREF(args); + + qemu_errors_to_previous(); + return; + +error: + monitor_protocol_emitter(mon, NULL); + qemu_errors_to_previous(); +} + /** * monitor_control_read(): Read and handle QMP input */ @@ -3605,7 +3842,7 @@ static void monitor_control_read(void *opaque, const uint8_t *buf, int size) cur_mon->mc->buf[cur_mon->mc->size] = '\0'; cur_mon->mc->size = 0; - /* TODO: parse QMP input */ + monitor_handle_qmp_command(cur_mon); break; } else { cur_mon->mc->buf[cur_mon->mc->size++] = buf[i]; -- 1.6.5.3.148.g785c5