|  | /* | 
|  | * 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 "block/aio.h" | 
|  | #include "qapi/compat-policy.h" | 
|  | #include "qapi/error.h" | 
|  | #include "qapi/qmp-registry.h" | 
|  | #include "qobject/qdict.h" | 
|  | #include "qobject/qjson.h" | 
|  | #include "qapi/qobject-input-visitor.h" | 
|  | #include "qapi/qobject-output-visitor.h" | 
|  | #include "qobject/qbool.h" | 
|  | #include "qemu/coroutine.h" | 
|  | #include "qemu/main-loop.h" | 
|  |  | 
|  | Visitor *qobject_input_visitor_new_qmp(QObject *obj) | 
|  | { | 
|  | Visitor *v = qobject_input_visitor_new(obj); | 
|  |  | 
|  | visit_set_policy(v, &compat_policy); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | Visitor *qobject_output_visitor_new_qmp(QObject **result) | 
|  | { | 
|  | Visitor *v = qobject_output_visitor_new(result); | 
|  |  | 
|  | visit_set_policy(v, &compat_policy); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, | 
|  | Error **errp) | 
|  | { | 
|  | const char *exec_key = NULL; | 
|  | const QDictEntry *ent; | 
|  | const char *arg_name; | 
|  | const QObject *arg_obj; | 
|  |  | 
|  | 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 if (!strcmp(arg_name, "id")) { | 
|  | continue; | 
|  | } 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; | 
|  | } | 
|  |  | 
|  | QDict *qmp_error_response(Error *err) | 
|  | { | 
|  | QDict *rsp; | 
|  |  | 
|  | rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", | 
|  | QapiErrorClass_str(error_get_class(err)), | 
|  | error_get_pretty(err)); | 
|  | error_free(err); | 
|  | return rsp; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Does @qdict look like a command to be run out-of-band? | 
|  | */ | 
|  | bool qmp_is_oob(const QDict *dict) | 
|  | { | 
|  | return qdict_haskey(dict, "exec-oob") | 
|  | && !qdict_haskey(dict, "execute"); | 
|  | } | 
|  |  | 
|  | typedef struct QmpDispatchBH { | 
|  | const QmpCommand *cmd; | 
|  | Monitor *cur_mon; | 
|  | QDict *args; | 
|  | QObject **ret; | 
|  | Error **errp; | 
|  | Coroutine *co; | 
|  | } QmpDispatchBH; | 
|  |  | 
|  | static void do_qmp_dispatch_bh(void *opaque) | 
|  | { | 
|  | QmpDispatchBH *data = opaque; | 
|  |  | 
|  | assert(monitor_cur() == NULL); | 
|  | monitor_set_cur(qemu_coroutine_self(), data->cur_mon); | 
|  | data->cmd->fn(data->args, data->ret, data->errp); | 
|  | monitor_set_cur(qemu_coroutine_self(), NULL); | 
|  | aio_co_wake(data->co); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Runs outside of coroutine context for OOB commands, but in coroutine | 
|  | * context for everything else. | 
|  | */ | 
|  | QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, | 
|  | bool allow_oob, Monitor *cur_mon) | 
|  | { | 
|  | Error *err = NULL; | 
|  | bool oob; | 
|  | const char *command; | 
|  | QDict *args; | 
|  | const QmpCommand *cmd; | 
|  | QDict *dict; | 
|  | QObject *id; | 
|  | QObject *ret = NULL; | 
|  | QDict *rsp = NULL; | 
|  |  | 
|  | dict = qobject_to(QDict, request); | 
|  | if (!dict) { | 
|  | id = NULL; | 
|  | error_setg(&err, "QMP input must be a JSON object"); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | id = qdict_get(dict, "id"); | 
|  |  | 
|  | if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) { | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | 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(&err, ERROR_CLASS_COMMAND_NOT_FOUND, | 
|  | "The command %s has not been found", command); | 
|  | goto out; | 
|  | } | 
|  | if (!compat_policy_input_ok(cmd->features, &compat_policy, | 
|  | ERROR_CLASS_COMMAND_NOT_FOUND, | 
|  | "command", command, &err)) { | 
|  | goto out; | 
|  | } | 
|  | if (!cmd->enabled) { | 
|  | error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, | 
|  | "Command %s has been disabled%s%s", | 
|  | command, | 
|  | cmd->disable_reason ? ": " : "", | 
|  | cmd->disable_reason ?: ""); | 
|  | goto out; | 
|  | } | 
|  | if (oob && !(cmd->options & QCO_ALLOW_OOB)) { | 
|  | error_setg(&err, "The command %s does not support OOB", | 
|  | command); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!qmp_command_available(cmd, &err)) { | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!qdict_haskey(dict, "arguments")) { | 
|  | args = qdict_new(); | 
|  | } else { | 
|  | args = qdict_get_qdict(dict, "arguments"); | 
|  | qobject_ref(args); | 
|  | } | 
|  |  | 
|  | assert(!(oob && qemu_in_coroutine())); | 
|  | assert(monitor_cur() == NULL); | 
|  | if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { | 
|  | if (qemu_in_coroutine()) { | 
|  | /* | 
|  | * Move the coroutine from iohandler_ctx to qemu_aio_context for | 
|  | * executing the command handler so that it can make progress if it | 
|  | * involves an AIO_WAIT_WHILE(). | 
|  | */ | 
|  | aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self()); | 
|  | qemu_coroutine_yield(); | 
|  | } | 
|  |  | 
|  | monitor_set_cur(qemu_coroutine_self(), cur_mon); | 
|  | cmd->fn(args, &ret, &err); | 
|  | monitor_set_cur(qemu_coroutine_self(), NULL); | 
|  |  | 
|  | if (qemu_in_coroutine()) { | 
|  | /* | 
|  | * Yield and reschedule so the main loop stays responsive. | 
|  | * | 
|  | * Move back to iohandler_ctx so that nested event loops for | 
|  | * qemu_aio_context don't start new monitor commands. | 
|  | */ | 
|  | aio_co_schedule(iohandler_get_aio_context(), | 
|  | qemu_coroutine_self()); | 
|  | qemu_coroutine_yield(); | 
|  | } | 
|  | } else { | 
|  | /* | 
|  | * Actual context doesn't match the one the command needs. | 
|  | * | 
|  | * Case 1: we are in coroutine context, but command does not | 
|  | * have QCO_COROUTINE.  We need to drop out of coroutine | 
|  | * context for executing it. | 
|  | * | 
|  | * Case 2: we are outside coroutine context, but command has | 
|  | * QCO_COROUTINE.  Can't actually happen, because we get here | 
|  | * outside coroutine context only when executing a command | 
|  | * out of band, and OOB commands never have QCO_COROUTINE. | 
|  | */ | 
|  | assert(!oob && qemu_in_coroutine() && !(cmd->options & QCO_COROUTINE)); | 
|  |  | 
|  | QmpDispatchBH data = { | 
|  | .cur_mon    = cur_mon, | 
|  | .cmd        = cmd, | 
|  | .args       = args, | 
|  | .ret        = &ret, | 
|  | .errp       = &err, | 
|  | .co         = qemu_coroutine_self(), | 
|  | }; | 
|  | aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh, | 
|  | &data); | 
|  | qemu_coroutine_yield(); | 
|  | } | 
|  | qobject_unref(args); | 
|  | if (err) { | 
|  | /* or assert(!ret) after reviewing all handlers: */ | 
|  | qobject_unref(ret); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (cmd->options & QCO_NO_SUCCESS_RESP) { | 
|  | g_assert(!ret); | 
|  | return NULL; | 
|  | } else if (!ret) { | 
|  | /* | 
|  | * When the command's schema has no 'returns', cmd->fn() | 
|  | * leaves @ret null.  The QMP spec calls for an empty object | 
|  | * then; supply it. | 
|  | */ | 
|  | ret = QOBJECT(qdict_new()); | 
|  | } | 
|  |  | 
|  | rsp = qdict_new(); | 
|  | qdict_put_obj(rsp, "return", ret); | 
|  |  | 
|  | out: | 
|  | if (err) { | 
|  | assert(!rsp); | 
|  | rsp = qmp_error_response(err); | 
|  | } | 
|  |  | 
|  | assert(rsp); | 
|  |  | 
|  | if (id) { | 
|  | qdict_put_obj(rsp, "id", qobject_ref(id)); | 
|  | } | 
|  |  | 
|  | return rsp; | 
|  | } |