xref: /openbmc/qemu/qapi/qmp-dispatch.c (revision cbb45ff0)
1 /*
2  * Core Definitions for QAPI/QMP Dispatch
3  *
4  * Copyright IBM, Corp. 2011
5  *
6  * Authors:
7  *  Anthony Liguori   <aliguori@us.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 
16 #include "block/aio.h"
17 #include "qapi/compat-policy.h"
18 #include "qapi/error.h"
19 #include "qapi/qmp/dispatch.h"
20 #include "qapi/qmp/qdict.h"
21 #include "qapi/qmp/qjson.h"
22 #include "qapi/qobject-input-visitor.h"
23 #include "qapi/qobject-output-visitor.h"
24 #include "sysemu/runstate.h"
25 #include "qapi/qmp/qbool.h"
26 #include "qemu/coroutine.h"
27 #include "qemu/main-loop.h"
28 
29 Visitor *qobject_input_visitor_new_qmp(QObject *obj)
30 {
31     Visitor *v = qobject_input_visitor_new(obj);
32 
33     visit_set_policy(v, &compat_policy);
34     return v;
35 }
36 
37 Visitor *qobject_output_visitor_new_qmp(QObject **result)
38 {
39     Visitor *v = qobject_output_visitor_new(result);
40 
41     visit_set_policy(v, &compat_policy);
42     return v;
43 }
44 
45 static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob,
46                                      Error **errp)
47 {
48     const char *exec_key = NULL;
49     const QDictEntry *ent;
50     const char *arg_name;
51     const QObject *arg_obj;
52 
53     for (ent = qdict_first(dict); ent;
54          ent = qdict_next(dict, ent)) {
55         arg_name = qdict_entry_key(ent);
56         arg_obj = qdict_entry_value(ent);
57 
58         if (!strcmp(arg_name, "execute")
59             || (!strcmp(arg_name, "exec-oob") && allow_oob)) {
60             if (qobject_type(arg_obj) != QTYPE_QSTRING) {
61                 error_setg(errp, "QMP input member '%s' must be a string",
62                            arg_name);
63                 return NULL;
64             }
65             if (exec_key) {
66                 error_setg(errp, "QMP input member '%s' clashes with '%s'",
67                            arg_name, exec_key);
68                 return NULL;
69             }
70             exec_key = arg_name;
71         } else if (!strcmp(arg_name, "arguments")) {
72             if (qobject_type(arg_obj) != QTYPE_QDICT) {
73                 error_setg(errp,
74                            "QMP input member 'arguments' must be an object");
75                 return NULL;
76             }
77         } else if (!strcmp(arg_name, "id")) {
78             continue;
79         } else {
80             error_setg(errp, "QMP input member '%s' is unexpected",
81                        arg_name);
82             return NULL;
83         }
84     }
85 
86     if (!exec_key) {
87         error_setg(errp, "QMP input lacks member 'execute'");
88         return NULL;
89     }
90 
91     return dict;
92 }
93 
94 QDict *qmp_error_response(Error *err)
95 {
96     QDict *rsp;
97 
98     rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }",
99                                   QapiErrorClass_str(error_get_class(err)),
100                                   error_get_pretty(err));
101     error_free(err);
102     return rsp;
103 }
104 
105 /*
106  * Does @qdict look like a command to be run out-of-band?
107  */
108 bool qmp_is_oob(const QDict *dict)
109 {
110     return qdict_haskey(dict, "exec-oob")
111         && !qdict_haskey(dict, "execute");
112 }
113 
114 typedef struct QmpDispatchBH {
115     const QmpCommand *cmd;
116     Monitor *cur_mon;
117     QDict *args;
118     QObject **ret;
119     Error **errp;
120     Coroutine *co;
121 } QmpDispatchBH;
122 
123 static void do_qmp_dispatch_bh(void *opaque)
124 {
125     QmpDispatchBH *data = opaque;
126 
127     assert(monitor_cur() == NULL);
128     monitor_set_cur(qemu_coroutine_self(), data->cur_mon);
129     data->cmd->fn(data->args, data->ret, data->errp);
130     monitor_set_cur(qemu_coroutine_self(), NULL);
131     aio_co_wake(data->co);
132 }
133 
134 /*
135  * Runs outside of coroutine context for OOB commands, but in coroutine
136  * context for everything else.
137  */
138 QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
139                     bool allow_oob, Monitor *cur_mon)
140 {
141     Error *err = NULL;
142     bool oob;
143     const char *command;
144     QDict *args;
145     const QmpCommand *cmd;
146     QDict *dict;
147     QObject *id;
148     QObject *ret = NULL;
149     QDict *rsp = NULL;
150 
151     dict = qobject_to(QDict, request);
152     if (!dict) {
153         id = NULL;
154         error_setg(&err, "QMP input must be a JSON object");
155         goto out;
156     }
157 
158     id = qdict_get(dict, "id");
159 
160     if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) {
161         goto out;
162     }
163 
164     command = qdict_get_try_str(dict, "execute");
165     oob = false;
166     if (!command) {
167         assert(allow_oob);
168         command = qdict_get_str(dict, "exec-oob");
169         oob = true;
170     }
171     cmd = qmp_find_command(cmds, command);
172     if (cmd == NULL) {
173         error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
174                   "The command %s has not been found", command);
175         goto out;
176     }
177     if (!compat_policy_input_ok(cmd->special_features, &compat_policy,
178                                 ERROR_CLASS_COMMAND_NOT_FOUND,
179                                 "command", command, &err)) {
180         goto out;
181     }
182     if (!cmd->enabled) {
183         error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
184                   "Command %s has been disabled%s%s",
185                   command,
186                   cmd->disable_reason ? ": " : "",
187                   cmd->disable_reason ?: "");
188         goto out;
189     }
190     if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
191         error_setg(&err, "The command %s does not support OOB",
192                    command);
193         goto out;
194     }
195 
196     if (!qmp_command_available(cmd, &err)) {
197         goto out;
198     }
199 
200     if (!qdict_haskey(dict, "arguments")) {
201         args = qdict_new();
202     } else {
203         args = qdict_get_qdict(dict, "arguments");
204         qobject_ref(args);
205     }
206 
207     assert(!(oob && qemu_in_coroutine()));
208     assert(monitor_cur() == NULL);
209     if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) {
210         monitor_set_cur(qemu_coroutine_self(), cur_mon);
211         cmd->fn(args, &ret, &err);
212         monitor_set_cur(qemu_coroutine_self(), NULL);
213     } else {
214        /*
215         * Actual context doesn't match the one the command needs.
216         *
217         * Case 1: we are in coroutine context, but command does not
218         * have QCO_COROUTINE.  We need to drop out of coroutine
219         * context for executing it.
220         *
221         * Case 2: we are outside coroutine context, but command has
222         * QCO_COROUTINE.  Can't actually happen, because we get here
223         * outside coroutine context only when executing a command
224         * out of band, and OOB commands never have QCO_COROUTINE.
225         */
226         assert(!oob && qemu_in_coroutine() && !(cmd->options & QCO_COROUTINE));
227 
228         QmpDispatchBH data = {
229             .cur_mon    = cur_mon,
230             .cmd        = cmd,
231             .args       = args,
232             .ret        = &ret,
233             .errp       = &err,
234             .co         = qemu_coroutine_self(),
235         };
236         aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh,
237                                 &data);
238         qemu_coroutine_yield();
239     }
240     qobject_unref(args);
241     if (err) {
242         /* or assert(!ret) after reviewing all handlers: */
243         qobject_unref(ret);
244         goto out;
245     }
246 
247     if (cmd->options & QCO_NO_SUCCESS_RESP) {
248         g_assert(!ret);
249         return NULL;
250     } else if (!ret) {
251         /*
252          * When the command's schema has no 'returns', cmd->fn()
253          * leaves @ret null.  The QMP spec calls for an empty object
254          * then; supply it.
255          */
256         ret = QOBJECT(qdict_new());
257     }
258 
259     rsp = qdict_new();
260     qdict_put_obj(rsp, "return", ret);
261 
262 out:
263     if (err) {
264         assert(!rsp);
265         rsp = qmp_error_response(err);
266     }
267 
268     assert(rsp);
269 
270     if (id) {
271         qdict_put_obj(rsp, "id", qobject_ref(id));
272     }
273 
274     return rsp;
275 }
276