| /* | 
 |  * 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; | 
 | } |