X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=monitor.c;h=be282ecb80fbcc44555bce60c0a2f072f7b3a581;hb=a0ee3797bf4917b1b7a4554a4dffbb45f387f087;hp=5aff7aebb01282fcf6dd2aec212ae8c80258a3b8;hpb=6959e4523e517b8af965af1030d3ac5bec9cd0fb;p=mirror_qemu.git diff --git a/monitor.c b/monitor.c index 5aff7aebb0..be282ecb80 100644 --- a/monitor.c +++ b/monitor.c @@ -77,6 +77,7 @@ #include "qapi-event.h" #include "qmp-introspect.h" #include "sysemu/qtest.h" +#include "sysemu/cpus.h" #include "qemu/cutils.h" #include "qapi/qmp/dispatch.h" @@ -165,7 +166,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 +222,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 +417,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 +567,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 +918,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; } @@ -955,7 +954,7 @@ EventInfoList *qmp_query_events(Error **errp) static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, Error **errp) { - *ret_data = qobject_from_json(qmp_schema_json); + *ret_data = qobject_from_json(qmp_schema_json, &error_abort); } /* @@ -973,42 +972,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(&qmp_commands, "query-cpu-model-expansion"); #endif #if !defined(TARGET_S390X) - qmp_unregister_command("query-cpu-model-expansion"); - 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) @@ -2799,7 +2823,8 @@ static QDict *monitor_parse_arguments(Monitor *mon, break; case 'o': { - int64_t val; + int ret; + uint64_t val; char *end; while (qemu_isspace(*p)) { @@ -2811,8 +2836,8 @@ static QDict *monitor_parse_arguments(Monitor *mon, break; } } - val = qemu_strtosz(p, &end); - if (val < 0) { + ret = qemu_strtosz_MiB(p, &end, &val); + if (ret < 0 || val > INT64_MAX) { monitor_printf(mon, "invalid size\n"); goto fail; } @@ -3676,87 +3701,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; @@ -3769,24 +3717,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(); @@ -3883,7 +3835,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);