xref: /openbmc/qemu/qapi/qmp-dispatch.c (revision 36c1febe3f34ae38db375865b7841165d76cdae4)
1ab02ab2aSMichael Roth /*
2ab02ab2aSMichael Roth  * Core Definitions for QAPI/QMP Dispatch
3ab02ab2aSMichael Roth  *
4ab02ab2aSMichael Roth  * Copyright IBM, Corp. 2011
5ab02ab2aSMichael Roth  *
6ab02ab2aSMichael Roth  * Authors:
7ab02ab2aSMichael Roth  *  Anthony Liguori   <aliguori@us.ibm.com>
8ab02ab2aSMichael Roth  *
9ab02ab2aSMichael Roth  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10ab02ab2aSMichael Roth  * See the COPYING.LIB file in the top-level directory.
11ab02ab2aSMichael Roth  *
12ab02ab2aSMichael Roth  */
13ab02ab2aSMichael Roth 
14cbf21151SPeter Maydell #include "qemu/osdep.h"
159ce44e2cSKevin Wolf 
169ce44e2cSKevin Wolf #include "block/aio.h"
176dd75472SMarkus Armbruster #include "qapi/compat-policy.h"
18da34e65cSMarkus Armbruster #include "qapi/error.h"
197b1b5d19SPaolo Bonzini #include "qapi/qmp/dispatch.h"
20452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
21c7eb39cbSEric Blake #include "qapi/qmp/qjson.h"
22db291641SMarkus Armbruster #include "qapi/qobject-input-visitor.h"
2391fa93e5SMarkus Armbruster #include "qapi/qobject-output-visitor.h"
24cf869d53SPeter Xu #include "qapi/qmp/qbool.h"
259ce44e2cSKevin Wolf #include "qemu/coroutine.h"
269ce44e2cSKevin Wolf #include "qemu/main-loop.h"
27ab02ab2aSMichael Roth 
qobject_input_visitor_new_qmp(QObject * obj)28db291641SMarkus Armbruster Visitor *qobject_input_visitor_new_qmp(QObject *obj)
29db291641SMarkus Armbruster {
30db291641SMarkus Armbruster     Visitor *v = qobject_input_visitor_new(obj);
31db291641SMarkus Armbruster 
32ed29bb28SMarkus Armbruster     visit_set_policy(v, &compat_policy);
33db291641SMarkus Armbruster     return v;
34db291641SMarkus Armbruster }
35db291641SMarkus Armbruster 
qobject_output_visitor_new_qmp(QObject ** result)3691fa93e5SMarkus Armbruster Visitor *qobject_output_visitor_new_qmp(QObject **result)
3791fa93e5SMarkus Armbruster {
3891fa93e5SMarkus Armbruster     Visitor *v = qobject_output_visitor_new(result);
3991fa93e5SMarkus Armbruster 
40ed29bb28SMarkus Armbruster     visit_set_policy(v, &compat_policy);
4191fa93e5SMarkus Armbruster     return v;
4291fa93e5SMarkus Armbruster }
4391fa93e5SMarkus Armbruster 
qmp_dispatch_check_obj(QDict * dict,bool allow_oob,Error ** errp)44a62c6174SMarkus Armbruster static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob,
45674ed722SMarkus Armbruster                                      Error **errp)
46ab02ab2aSMichael Roth {
4700ecec15SMarkus Armbruster     const char *exec_key = NULL;
48ab02ab2aSMichael Roth     const QDictEntry *ent;
49ab02ab2aSMichael Roth     const char *arg_name;
50ab02ab2aSMichael Roth     const QObject *arg_obj;
51ab02ab2aSMichael Roth 
52ab02ab2aSMichael Roth     for (ent = qdict_first(dict); ent;
53ab02ab2aSMichael Roth          ent = qdict_next(dict, ent)) {
54ab02ab2aSMichael Roth         arg_name = qdict_entry_key(ent);
55ab02ab2aSMichael Roth         arg_obj = qdict_entry_value(ent);
56ab02ab2aSMichael Roth 
5700ecec15SMarkus Armbruster         if (!strcmp(arg_name, "execute")
5800ecec15SMarkus Armbruster             || (!strcmp(arg_name, "exec-oob") && allow_oob)) {
59ab02ab2aSMichael Roth             if (qobject_type(arg_obj) != QTYPE_QSTRING) {
6000ecec15SMarkus Armbruster                 error_setg(errp, "QMP input member '%s' must be a string",
6100ecec15SMarkus Armbruster                            arg_name);
62ab02ab2aSMichael Roth                 return NULL;
63ab02ab2aSMichael Roth             }
6400ecec15SMarkus Armbruster             if (exec_key) {
6500ecec15SMarkus Armbruster                 error_setg(errp, "QMP input member '%s' clashes with '%s'",
6600ecec15SMarkus Armbruster                            arg_name, exec_key);
6700ecec15SMarkus Armbruster                 return NULL;
6800ecec15SMarkus Armbruster             }
6900ecec15SMarkus Armbruster             exec_key = arg_name;
7074d8c9d9SMarkus Armbruster         } else if (!strcmp(arg_name, "arguments")) {
7174d8c9d9SMarkus Armbruster             if (qobject_type(arg_obj) != QTYPE_QDICT) {
7210e37839SMarkus Armbruster                 error_setg(errp,
7310e37839SMarkus Armbruster                            "QMP input member 'arguments' must be an object");
7474d8c9d9SMarkus Armbruster                 return NULL;
7574d8c9d9SMarkus Armbruster             }
764eaca8deSMarc-André Lureau         } else if (!strcmp(arg_name, "id")) {
774eaca8deSMarc-André Lureau             continue;
7874d8c9d9SMarkus Armbruster         } else {
7910e37839SMarkus Armbruster             error_setg(errp, "QMP input member '%s' is unexpected",
8099fb0c53SMarkus Armbruster                        arg_name);
81ab02ab2aSMichael Roth             return NULL;
82ab02ab2aSMichael Roth         }
83ab02ab2aSMichael Roth     }
84ab02ab2aSMichael Roth 
8500ecec15SMarkus Armbruster     if (!exec_key) {
8610e37839SMarkus Armbruster         error_setg(errp, "QMP input lacks member 'execute'");
87ab02ab2aSMichael Roth         return NULL;
88ab02ab2aSMichael Roth     }
89ab02ab2aSMichael Roth 
90ab02ab2aSMichael Roth     return dict;
91ab02ab2aSMichael Roth }
92ab02ab2aSMichael Roth 
qmp_error_response(Error * err)93cee32796SMarkus Armbruster QDict *qmp_error_response(Error *err)
9493b91c59SLuiz Capitulino {
95cee32796SMarkus Armbruster     QDict *rsp;
96cee32796SMarkus Armbruster 
97cee32796SMarkus Armbruster     rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }",
98977c736fSMarkus Armbruster                                   QapiErrorClass_str(error_get_class(err)),
99e940f543SMarkus Armbruster                                   error_get_pretty(err));
100cee32796SMarkus Armbruster     error_free(err);
101cee32796SMarkus Armbruster     return rsp;
10293b91c59SLuiz Capitulino }
10393b91c59SLuiz Capitulino 
104cf869d53SPeter Xu /*
10500ecec15SMarkus Armbruster  * Does @qdict look like a command to be run out-of-band?
106cf869d53SPeter Xu  */
qmp_is_oob(const QDict * dict)1072aa788f5SMarc-André Lureau bool qmp_is_oob(const QDict *dict)
108cf869d53SPeter Xu {
10900ecec15SMarkus Armbruster     return qdict_haskey(dict, "exec-oob")
11000ecec15SMarkus Armbruster         && !qdict_haskey(dict, "execute");
111cf869d53SPeter Xu }
112cf869d53SPeter Xu 
1139ce44e2cSKevin Wolf typedef struct QmpDispatchBH {
1149ce44e2cSKevin Wolf     const QmpCommand *cmd;
1159ce44e2cSKevin Wolf     Monitor *cur_mon;
1169ce44e2cSKevin Wolf     QDict *args;
1179ce44e2cSKevin Wolf     QObject **ret;
1189ce44e2cSKevin Wolf     Error **errp;
1199ce44e2cSKevin Wolf     Coroutine *co;
1209ce44e2cSKevin Wolf } QmpDispatchBH;
1219ce44e2cSKevin Wolf 
do_qmp_dispatch_bh(void * opaque)1229ce44e2cSKevin Wolf static void do_qmp_dispatch_bh(void *opaque)
1239ce44e2cSKevin Wolf {
1249ce44e2cSKevin Wolf     QmpDispatchBH *data = opaque;
1259ce44e2cSKevin Wolf 
1269ce44e2cSKevin Wolf     assert(monitor_cur() == NULL);
1279ce44e2cSKevin Wolf     monitor_set_cur(qemu_coroutine_self(), data->cur_mon);
1289ce44e2cSKevin Wolf     data->cmd->fn(data->args, data->ret, data->errp);
1299ce44e2cSKevin Wolf     monitor_set_cur(qemu_coroutine_self(), NULL);
1309ce44e2cSKevin Wolf     aio_co_wake(data->co);
1319ce44e2cSKevin Wolf }
1329ce44e2cSKevin Wolf 
1339ce44e2cSKevin Wolf /*
1349ce44e2cSKevin Wolf  * Runs outside of coroutine context for OOB commands, but in coroutine
1359ce44e2cSKevin Wolf  * context for everything else.
1369ce44e2cSKevin Wolf  */
qmp_dispatch(const QmpCommandList * cmds,QObject * request,bool allow_oob,Monitor * cur_mon)137a50c99bcSPaolo Bonzini QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request,
13841725fa7SKevin Wolf                                        bool allow_oob, Monitor *cur_mon)
139ab02ab2aSMichael Roth {
140ab02ab2aSMichael Roth     Error *err = NULL;
141cf4a0643SMarkus Armbruster     bool oob;
142cf4a0643SMarkus Armbruster     const char *command;
143cf4a0643SMarkus Armbruster     QDict *args;
144f0ccc00bSMarc-André Lureau     const QmpCommand *cmd;
145a62c6174SMarkus Armbruster     QDict *dict;
146a62c6174SMarkus Armbruster     QObject *id;
147cf4a0643SMarkus Armbruster     QObject *ret = NULL;
148d3226035SMarkus Armbruster     QDict *rsp = NULL;
149ab02ab2aSMichael Roth 
150a62c6174SMarkus Armbruster     dict = qobject_to(QDict, request);
151cf4a0643SMarkus Armbruster     if (!dict) {
152a62c6174SMarkus Armbruster         id = NULL;
153a62c6174SMarkus Armbruster         error_setg(&err, "QMP input must be a JSON object");
154a62c6174SMarkus Armbruster         goto out;
155a62c6174SMarkus Armbruster     }
156a62c6174SMarkus Armbruster 
157a62c6174SMarkus Armbruster     id = qdict_get(dict, "id");
158a62c6174SMarkus Armbruster 
159a62c6174SMarkus Armbruster     if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) {
160cf4a0643SMarkus Armbruster         goto out;
161cf4a0643SMarkus Armbruster     }
162cf4a0643SMarkus Armbruster 
163cf4a0643SMarkus Armbruster     command = qdict_get_try_str(dict, "execute");
164cf4a0643SMarkus Armbruster     oob = false;
165cf4a0643SMarkus Armbruster     if (!command) {
166cf4a0643SMarkus Armbruster         assert(allow_oob);
167cf4a0643SMarkus Armbruster         command = qdict_get_str(dict, "exec-oob");
168cf4a0643SMarkus Armbruster         oob = true;
169cf4a0643SMarkus Armbruster     }
170cf4a0643SMarkus Armbruster     cmd = qmp_find_command(cmds, command);
171cf4a0643SMarkus Armbruster     if (cmd == NULL) {
172cf4a0643SMarkus Armbruster         error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
173cf4a0643SMarkus Armbruster                   "The command %s has not been found", command);
174cf4a0643SMarkus Armbruster         goto out;
175cf4a0643SMarkus Armbruster     }
1767ce5fc63SMarkus Armbruster     if (!compat_policy_input_ok(cmd->special_features, &compat_policy,
1777ce5fc63SMarkus Armbruster                                 ERROR_CLASS_COMMAND_NOT_FOUND,
1787ce5fc63SMarkus Armbruster                                 "command", command, &err)) {
179d2032598SMarkus Armbruster         goto out;
180d2032598SMarkus Armbruster     }
181cf4a0643SMarkus Armbruster     if (!cmd->enabled) {
182cf4a0643SMarkus Armbruster         error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
183c98939daSMarc-André Lureau                   "Command %s has been disabled%s%s",
184c98939daSMarc-André Lureau                   command,
185c98939daSMarc-André Lureau                   cmd->disable_reason ? ": " : "",
186c98939daSMarc-André Lureau                   cmd->disable_reason ?: "");
187cf4a0643SMarkus Armbruster         goto out;
188cf4a0643SMarkus Armbruster     }
189cf4a0643SMarkus Armbruster     if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
190cf4a0643SMarkus Armbruster         error_setg(&err, "The command %s does not support OOB",
191cf4a0643SMarkus Armbruster                    command);
192cf4a0643SMarkus Armbruster         goto out;
193cf4a0643SMarkus Armbruster     }
194cf4a0643SMarkus Armbruster 
195164dafd1SPaolo Bonzini     if (!qmp_command_available(cmd, &err)) {
196cf4a0643SMarkus Armbruster         goto out;
197cf4a0643SMarkus Armbruster     }
198cf4a0643SMarkus Armbruster 
199cf4a0643SMarkus Armbruster     if (!qdict_haskey(dict, "arguments")) {
200cf4a0643SMarkus Armbruster         args = qdict_new();
201cf4a0643SMarkus Armbruster     } else {
202cf4a0643SMarkus Armbruster         args = qdict_get_qdict(dict, "arguments");
203cf4a0643SMarkus Armbruster         qobject_ref(args);
204cf4a0643SMarkus Armbruster     }
20541725fa7SKevin Wolf 
2069ce44e2cSKevin Wolf     assert(!(oob && qemu_in_coroutine()));
20741725fa7SKevin Wolf     assert(monitor_cur() == NULL);
2089ce44e2cSKevin Wolf     if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) {
209effd60c8SStefan Hajnoczi         if (qemu_in_coroutine()) {
210effd60c8SStefan Hajnoczi             /*
211effd60c8SStefan Hajnoczi              * Move the coroutine from iohandler_ctx to qemu_aio_context for
212effd60c8SStefan Hajnoczi              * executing the command handler so that it can make progress if it
213effd60c8SStefan Hajnoczi              * involves an AIO_WAIT_WHILE().
214effd60c8SStefan Hajnoczi              */
215*719c6819SStefan Hajnoczi             aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self());
216*719c6819SStefan Hajnoczi             qemu_coroutine_yield();
217effd60c8SStefan Hajnoczi         }
218effd60c8SStefan Hajnoczi 
219e69ee454SKevin Wolf         monitor_set_cur(qemu_coroutine_self(), cur_mon);
220cf4a0643SMarkus Armbruster         cmd->fn(args, &ret, &err);
221e69ee454SKevin Wolf         monitor_set_cur(qemu_coroutine_self(), NULL);
222effd60c8SStefan Hajnoczi 
223effd60c8SStefan Hajnoczi         if (qemu_in_coroutine()) {
224effd60c8SStefan Hajnoczi             /*
225effd60c8SStefan Hajnoczi              * Yield and reschedule so the main loop stays responsive.
226effd60c8SStefan Hajnoczi              *
227effd60c8SStefan Hajnoczi              * Move back to iohandler_ctx so that nested event loops for
228effd60c8SStefan Hajnoczi              * qemu_aio_context don't start new monitor commands.
229effd60c8SStefan Hajnoczi              */
230*719c6819SStefan Hajnoczi             aio_co_schedule(iohandler_get_aio_context(),
231*719c6819SStefan Hajnoczi                             qemu_coroutine_self());
232*719c6819SStefan Hajnoczi             qemu_coroutine_yield();
233effd60c8SStefan Hajnoczi         }
2349ce44e2cSKevin Wolf     } else {
2359ce44e2cSKevin Wolf        /*
2369ce44e2cSKevin Wolf         * Actual context doesn't match the one the command needs.
2379ce44e2cSKevin Wolf         *
2389ce44e2cSKevin Wolf         * Case 1: we are in coroutine context, but command does not
2399ce44e2cSKevin Wolf         * have QCO_COROUTINE.  We need to drop out of coroutine
2409ce44e2cSKevin Wolf         * context for executing it.
2419ce44e2cSKevin Wolf         *
2429ce44e2cSKevin Wolf         * Case 2: we are outside coroutine context, but command has
2439ce44e2cSKevin Wolf         * QCO_COROUTINE.  Can't actually happen, because we get here
2449ce44e2cSKevin Wolf         * outside coroutine context only when executing a command
2459ce44e2cSKevin Wolf         * out of band, and OOB commands never have QCO_COROUTINE.
2469ce44e2cSKevin Wolf         */
2479ce44e2cSKevin Wolf         assert(!oob && qemu_in_coroutine() && !(cmd->options & QCO_COROUTINE));
2489ce44e2cSKevin Wolf 
2499ce44e2cSKevin Wolf         QmpDispatchBH data = {
2509ce44e2cSKevin Wolf             .cur_mon    = cur_mon,
2519ce44e2cSKevin Wolf             .cmd        = cmd,
2529ce44e2cSKevin Wolf             .args       = args,
2539ce44e2cSKevin Wolf             .ret        = &ret,
2549ce44e2cSKevin Wolf             .errp       = &err,
2559ce44e2cSKevin Wolf             .co         = qemu_coroutine_self(),
2569ce44e2cSKevin Wolf         };
257effd60c8SStefan Hajnoczi         aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh,
2589ce44e2cSKevin Wolf                                 &data);
2599ce44e2cSKevin Wolf         qemu_coroutine_yield();
2609ce44e2cSKevin Wolf     }
261d3226035SMarkus Armbruster     qobject_unref(args);
262cf4a0643SMarkus Armbruster     if (err) {
263b3fbb328SMarc-André Lureau         /* or assert(!ret) after reviewing all handlers: */
264b3fbb328SMarc-André Lureau         qobject_unref(ret);
265d3226035SMarkus Armbruster         goto out;
266d3226035SMarkus Armbruster     }
267d3226035SMarkus Armbruster 
268d3226035SMarkus Armbruster     if (cmd->options & QCO_NO_SUCCESS_RESP) {
269cf4a0643SMarkus Armbruster         g_assert(!ret);
270d3226035SMarkus Armbruster         return NULL;
271cf4a0643SMarkus Armbruster     } else if (!ret) {
2724a883738SMarkus Armbruster         /*
2734a883738SMarkus Armbruster          * When the command's schema has no 'returns', cmd->fn()
2744a883738SMarkus Armbruster          * leaves @ret null.  The QMP spec calls for an empty object
2754a883738SMarkus Armbruster          * then; supply it.
2764a883738SMarkus Armbruster          */
277cf4a0643SMarkus Armbruster         ret = QOBJECT(qdict_new());
278cf4a0643SMarkus Armbruster     }
279cf4a0643SMarkus Armbruster 
280d3226035SMarkus Armbruster     rsp = qdict_new();
281d3226035SMarkus Armbruster     qdict_put_obj(rsp, "return", ret);
282cf4a0643SMarkus Armbruster 
283cf4a0643SMarkus Armbruster out:
284ab02ab2aSMichael Roth     if (err) {
285d3226035SMarkus Armbruster         assert(!rsp);
286cee32796SMarkus Armbruster         rsp = qmp_error_response(err);
287ab02ab2aSMichael Roth     }
288ab02ab2aSMichael Roth 
289d3226035SMarkus Armbruster     assert(rsp);
290d3226035SMarkus Armbruster 
291d3226035SMarkus Armbruster     if (id) {
2924eaca8deSMarc-André Lureau         qdict_put_obj(rsp, "id", qobject_ref(id));
2934eaca8deSMarc-André Lureau     }
2944eaca8deSMarc-André Lureau 
295d43b1694SMarkus Armbruster     return rsp;
296ab02ab2aSMichael Roth }
297