@@ -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];
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 <lcapitulino@redhat.com> --- monitor.c | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 244 insertions(+), 7 deletions(-)