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