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