[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 07/15] QMP: Input support
From: |
Luiz Capitulino |
Subject: |
[Qemu-devel] [PATCH 07/15] QMP: Input support |
Date: |
Thu, 19 Nov 2009 13:13:35 -0200 |
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 <address@hidden>
---
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
- [Qemu-devel] [RFC v0 00/15] QEMU Monitor Protocol, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 01/15] monitor: Introduce MONITOR_USE_CONTROL flag, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 02/15] monitor: Command-line flag to enable control mode, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 03/15] monitor: Move handler calling code to its own function, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 04/15] QError: Add errors used by QMP, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 05/15] QMP: chardev handling, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 06/15] QMP: Output support, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 07/15] QMP: Input support,
Luiz Capitulino <=
- [Qemu-devel] [PATCH 08/15] QMP: Asynchronous events infrastructure, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 09/15] QMP: Introduce basic asynchronous events, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 10/15] QMP: Disable monitor print functions, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 11/15] QMP: Introduce README file, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 12/15] QMP: Introduce specification, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 13/15] QMP: Introduce qmp-events.txt, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 14/15] QMP: Introduce qmp-shell, Luiz Capitulino, 2009/11/19
- [Qemu-devel] [PATCH 15/15] QMP: Introduce vm-info, Luiz Capitulino, 2009/11/19
- [Qemu-devel] Re: [RFC v0 00/15] QEMU Monitor Protocol, Avi Kivity, 2009/11/19