mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 23:33:54 -06:00

handle_qmp_command() reports certain errors right away. This is wrong when OOB is enabled, because the errors can "jump the queue" then, as the previous commit demonstrates. To fix, we need to delay errors until dispatch. Do that for semantic errors, mostly by reverting ill-advised parts of commitcf869d5317
"qmp: support out-of-band (oob) execution". Bonus: doesn't run qmp_dispatch_check_obj() twice, once in handle_qmp_command(), and again in do_qmp_dispatch(). That's also due to commitcf869d5317
. The next commit will fix queue jumping for syntax errors. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180703085358.13941-18-armbru@redhat.com>
181 lines
4.8 KiB
C
181 lines
4.8 KiB
C
/*
|
|
* Core Definitions for QAPI/QMP Dispatch
|
|
*
|
|
* Copyright IBM, Corp. 2011
|
|
*
|
|
* Authors:
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
|
* See the COPYING.LIB file in the top-level directory.
|
|
*
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qmp/dispatch.h"
|
|
#include "qapi/qmp/json-parser.h"
|
|
#include "qapi/qmp/qdict.h"
|
|
#include "qapi/qmp/qjson.h"
|
|
#include "qapi/qmp/qbool.h"
|
|
#include "sysemu/sysemu.h"
|
|
|
|
static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
|
|
Error **errp)
|
|
{
|
|
const char *exec_key = NULL;
|
|
const QDictEntry *ent;
|
|
const char *arg_name;
|
|
const QObject *arg_obj;
|
|
QDict *dict;
|
|
|
|
dict = qobject_to(QDict, request);
|
|
if (!dict) {
|
|
error_setg(errp, "QMP input must be a JSON object");
|
|
return NULL;
|
|
}
|
|
|
|
for (ent = qdict_first(dict); ent;
|
|
ent = qdict_next(dict, ent)) {
|
|
arg_name = qdict_entry_key(ent);
|
|
arg_obj = qdict_entry_value(ent);
|
|
|
|
if (!strcmp(arg_name, "execute")
|
|
|| (!strcmp(arg_name, "exec-oob") && allow_oob)) {
|
|
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
|
|
error_setg(errp, "QMP input member '%s' must be a string",
|
|
arg_name);
|
|
return NULL;
|
|
}
|
|
if (exec_key) {
|
|
error_setg(errp, "QMP input member '%s' clashes with '%s'",
|
|
arg_name, exec_key);
|
|
return NULL;
|
|
}
|
|
exec_key = arg_name;
|
|
} else if (!strcmp(arg_name, "arguments")) {
|
|
if (qobject_type(arg_obj) != QTYPE_QDICT) {
|
|
error_setg(errp,
|
|
"QMP input member 'arguments' must be an object");
|
|
return NULL;
|
|
}
|
|
} else {
|
|
error_setg(errp, "QMP input member '%s' is unexpected",
|
|
arg_name);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!exec_key) {
|
|
error_setg(errp, "QMP input lacks member 'execute'");
|
|
return NULL;
|
|
}
|
|
|
|
return dict;
|
|
}
|
|
|
|
static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
|
bool allow_oob, Error **errp)
|
|
{
|
|
Error *local_err = NULL;
|
|
bool oob;
|
|
const char *command;
|
|
QDict *args, *dict;
|
|
QmpCommand *cmd;
|
|
QObject *ret = NULL;
|
|
|
|
dict = qmp_dispatch_check_obj(request, allow_oob, errp);
|
|
if (!dict) {
|
|
return NULL;
|
|
}
|
|
|
|
command = qdict_get_try_str(dict, "execute");
|
|
oob = false;
|
|
if (!command) {
|
|
assert(allow_oob);
|
|
command = qdict_get_str(dict, "exec-oob");
|
|
oob = true;
|
|
}
|
|
cmd = qmp_find_command(cmds, command);
|
|
if (cmd == NULL) {
|
|
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
|
|
"The command %s has not been found", command);
|
|
return NULL;
|
|
}
|
|
if (!cmd->enabled) {
|
|
error_setg(errp, "The command %s has been disabled for this instance",
|
|
command);
|
|
return NULL;
|
|
}
|
|
if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
|
|
error_setg(errp, "The command %s does not support OOB",
|
|
command);
|
|
return false;
|
|
}
|
|
|
|
if (runstate_check(RUN_STATE_PRECONFIG) &&
|
|
!(cmd->options & QCO_ALLOW_PRECONFIG)) {
|
|
error_setg(errp, "The command '%s' isn't permitted in '%s' state",
|
|
cmd->name, RunState_str(RUN_STATE_PRECONFIG));
|
|
return NULL;
|
|
}
|
|
|
|
if (!qdict_haskey(dict, "arguments")) {
|
|
args = qdict_new();
|
|
} else {
|
|
args = qdict_get_qdict(dict, "arguments");
|
|
qobject_ref(args);
|
|
}
|
|
|
|
cmd->fn(args, &ret, &local_err);
|
|
if (local_err) {
|
|
error_propagate(errp, local_err);
|
|
} else if (cmd->options & QCO_NO_SUCCESS_RESP) {
|
|
g_assert(!ret);
|
|
} else if (!ret) {
|
|
ret = QOBJECT(qdict_new());
|
|
}
|
|
|
|
qobject_unref(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
QObject *qmp_build_error_object(Error *err)
|
|
{
|
|
return qobject_from_jsonf("{ 'class': %s, 'desc': %s }",
|
|
QapiErrorClass_str(error_get_class(err)),
|
|
error_get_pretty(err));
|
|
}
|
|
|
|
/*
|
|
* Does @qdict look like a command to be run out-of-band?
|
|
*/
|
|
bool qmp_is_oob(QDict *dict)
|
|
{
|
|
return qdict_haskey(dict, "exec-oob")
|
|
&& !qdict_haskey(dict, "execute");
|
|
}
|
|
|
|
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
|
bool allow_oob)
|
|
{
|
|
Error *err = NULL;
|
|
QObject *ret;
|
|
QDict *rsp;
|
|
|
|
ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
|
|
|
|
rsp = qdict_new();
|
|
if (err) {
|
|
qdict_put_obj(rsp, "error", qmp_build_error_object(err));
|
|
error_free(err);
|
|
} else if (ret) {
|
|
qdict_put_obj(rsp, "return", ret);
|
|
} else {
|
|
qobject_unref(rsp);
|
|
return NULL;
|
|
}
|
|
|
|
return QOBJECT(rsp);
|
|
}
|