summaryrefslogtreecommitdiff
path: root/monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'monitor.c')
-rw-r--r--monitor.c182
1 files changed, 65 insertions, 117 deletions
diff --git a/monitor.c b/monitor.c
index b68944d93c..ec7623efdd 100644
--- a/monitor.c
+++ b/monitor.c
@@ -165,7 +165,7 @@ typedef struct {
* When command qmp_capabilities succeeds, we go into command
* mode.
*/
- bool in_command_mode; /* are we in command mode? */
+ QmpCommandList *commands;
} MonitorQMP;
/*
@@ -221,6 +221,8 @@ static int mon_refcount;
static mon_cmd_t mon_cmds[];
static mon_cmd_t info_cmds[];
+QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
+
Monitor *cur_mon;
static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME;
@@ -414,7 +416,8 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
trace_monitor_protocol_event_emit(event, qdict);
QLIST_FOREACH(mon, &mon_list, entry) {
- if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) {
+ if (monitor_is_qmp(mon)
+ && mon->qmp.commands != &qmp_cap_negotiation_commands) {
monitor_json_emitter(mon, QOBJECT(qdict));
}
}
@@ -563,11 +566,6 @@ static void monitor_qapi_event_init(void)
qmp_event_set_func_emit(monitor_qapi_event_queue);
}
-void qmp_qmp_capabilities(Error **errp)
-{
- cur_mon->qmp.in_command_mode = true;
-}
-
static void handle_hmp_command(Monitor *mon, const char *cmdline);
static void monitor_data_init(Monitor *mon)
@@ -919,7 +917,7 @@ CommandInfoList *qmp_query_commands(Error **errp)
{
CommandInfoList *list = NULL;
- qmp_for_each_command(query_commands_cb, &list);
+ qmp_for_each_command(cur_mon->qmp.commands, query_commands_cb, &list);
return list;
}
@@ -973,44 +971,67 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
static void qmp_unregister_commands_hack(void)
{
#ifndef CONFIG_SPICE
- qmp_unregister_command("query-spice");
+ qmp_unregister_command(&qmp_commands, "query-spice");
#endif
#ifndef TARGET_I386
- qmp_unregister_command("rtc-reset-reinjection");
+ qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
#endif
#ifndef TARGET_S390X
- qmp_unregister_command("dump-skeys");
+ qmp_unregister_command(&qmp_commands, "dump-skeys");
#endif
#ifndef TARGET_ARM
- qmp_unregister_command("query-gic-capabilities");
+ qmp_unregister_command(&qmp_commands, "query-gic-capabilities");
#endif
#if !defined(TARGET_S390X) && !defined(TARGET_I386)
- qmp_unregister_command("query-cpu-model-expansion");
+ qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
#endif
#if !defined(TARGET_S390X)
- qmp_unregister_command("query-cpu-model-baseline");
- qmp_unregister_command("query-cpu-model-comparison");
+ qmp_unregister_command(&qmp_commands, "query-cpu-model-baseline");
+ qmp_unregister_command(&qmp_commands, "query-cpu-model-comparison");
#endif
#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
&& !defined(TARGET_S390X)
- qmp_unregister_command("query-cpu-definitions");
+ qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
#endif
}
-static void qmp_init_marshal(void)
+void monitor_init_qmp_commands(void)
{
- qmp_register_command("query-qmp-schema", qmp_query_qmp_schema,
+ /*
+ * Two command lists:
+ * - qmp_commands contains all QMP commands
+ * - qmp_cap_negotiation_commands contains just
+ * "qmp_capabilities", to enforce capability negotiation
+ */
+
+ qmp_init_marshal(&qmp_commands);
+
+ qmp_register_command(&qmp_commands, "query-qmp-schema",
+ qmp_query_qmp_schema,
QCO_NO_OPTIONS);
- qmp_register_command("device_add", qmp_device_add,
+ qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
QCO_NO_OPTIONS);
- qmp_register_command("netdev_add", qmp_netdev_add,
+ qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
QCO_NO_OPTIONS);
- /* call it after the rest of qapi_init() */
- register_module_init(qmp_unregister_commands_hack, MODULE_INIT_QAPI);
+ qmp_unregister_commands_hack();
+
+ QTAILQ_INIT(&qmp_cap_negotiation_commands);
+ qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
+ qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
}
-qapi_init(qmp_init_marshal);
+void qmp_qmp_capabilities(Error **errp)
+{
+ if (cur_mon->qmp.commands == &qmp_commands) {
+ error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "Capabilities negotiation is already complete, command "
+ "ignored");
+ return;
+ }
+
+ cur_mon->qmp.commands = &qmp_commands;
+}
/* set the current CPU defined by the user */
int monitor_set_cpu(int cpu_index)
@@ -3679,87 +3700,10 @@ static int monitor_can_read(void *opaque)
return (mon->suspend_cnt == 0) ? 1 : 0;
}
-static bool invalid_qmp_mode(const Monitor *mon, const char *cmd,
- Error **errp)
-{
- bool is_cap = g_str_equal(cmd, "qmp_capabilities");
-
- if (is_cap && mon->qmp.in_command_mode) {
- error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
- "Capabilities negotiation is already complete, command "
- "'%s' ignored", cmd);
- return true;
- }
- if (!is_cap && !mon->qmp.in_command_mode) {
- error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
- "Expecting capabilities negotiation with "
- "'qmp_capabilities' before command '%s'", cmd);
- return true;
- }
- return false;
-}
-
-/*
- * 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, Error **errp)
-{
- const QDictEntry *ent;
- int has_exec_key = 0;
- QDict *input_dict;
-
- input_dict = qobject_to_qdict(input_obj);
- if (!input_dict) {
- error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT, "object");
- return NULL;
- }
-
-
- 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) {
- error_setg(errp, 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) {
- error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
- "arguments", "object");
- return NULL;
- }
- } else if (!strcmp(arg_name, "id")) {
- /* Any string is acceptable as "id", so nothing to check */
- } else {
- error_setg(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
- return NULL;
- }
- }
-
- if (!has_exec_key) {
- error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute");
- return NULL;
- }
-
- return input_dict;
-}
-
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
{
QObject *req, *rsp = NULL, *id = NULL;
QDict *qdict = NULL;
- const char *cmd_name;
Monitor *mon = cur_mon;
Error *err = NULL;
@@ -3772,24 +3716,28 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
goto err_out;
}
- qdict = qmp_check_input_obj(req, &err);
- if (!qdict) {
- goto err_out;
- }
-
- id = qdict_get(qdict, "id");
- qobject_incref(id);
- qdict_del(qdict, "id");
-
- cmd_name = qdict_get_str(qdict, "execute");
- trace_handle_qmp_command(mon, cmd_name);
-
- if (invalid_qmp_mode(mon, cmd_name, &err)) {
- goto err_out;
+ qdict = qobject_to_qdict(req);
+ if (qdict) {
+ id = qdict_get(qdict, "id");
+ qobject_incref(id);
+ qdict_del(qdict, "id");
+ } /* else will fail qmp_dispatch() */
+
+ rsp = qmp_dispatch(cur_mon->qmp.commands, req);
+
+ if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
+ qdict = qdict_get_qdict(qobject_to_qdict(rsp), "error");
+ if (qdict
+ && !g_strcmp0(qdict_get_try_str(qdict, "class"),
+ QapiErrorClass_lookup[ERROR_CLASS_COMMAND_NOT_FOUND])) {
+ /* Provide a more useful error message */
+ qdict_del(qdict, "desc");
+ qdict_put(qdict, "desc",
+ qstring_from_str("Expecting capabilities negotiation"
+ " with 'qmp_capabilities'"));
+ }
}
- rsp = qmp_dispatch(req);
-
err_out:
if (err) {
qdict = qdict_new();
@@ -3886,7 +3834,7 @@ static void monitor_qmp_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_OPENED:
- mon->qmp.in_command_mode = false;
+ mon->qmp.commands = &qmp_cap_negotiation_commands;
data = get_qmp_greeting();
monitor_json_emitter(mon, data);
qobject_decref(data);