Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 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 | |
Peter Maydell | cbf2115 | 2016-01-29 17:49:57 +0000 | [diff] [blame] | 14 | #include "qemu/osdep.h" |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 15 | |
| 16 | #include "block/aio.h" |
Markus Armbruster | 6dd7547 | 2021-03-18 16:55:10 +0100 | [diff] [blame] | 17 | #include "qapi/compat-policy.h" |
Markus Armbruster | da34e65 | 2016-03-14 09:01:28 +0100 | [diff] [blame] | 18 | #include "qapi/error.h" |
Paolo Bonzini | 7b1b5d1 | 2012-12-17 18:19:43 +0100 | [diff] [blame] | 19 | #include "qapi/qmp/dispatch.h" |
Markus Armbruster | 452fcdb | 2018-02-01 12:18:39 +0100 | [diff] [blame] | 20 | #include "qapi/qmp/qdict.h" |
Eric Blake | c7eb39c | 2016-06-09 10:48:32 -0600 | [diff] [blame] | 21 | #include "qapi/qmp/qjson.h" |
Markus Armbruster | db29164 | 2021-03-18 16:55:18 +0100 | [diff] [blame] | 22 | #include "qapi/qobject-input-visitor.h" |
Markus Armbruster | 91fa93e | 2021-03-18 16:55:11 +0100 | [diff] [blame] | 23 | #include "qapi/qobject-output-visitor.h" |
Markus Armbruster | 54d3123 | 2019-08-12 07:23:59 +0200 | [diff] [blame] | 24 | #include "sysemu/runstate.h" |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 25 | #include "qapi/qmp/qbool.h" |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 26 | #include "qemu/coroutine.h" |
| 27 | #include "qemu/main-loop.h" |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 28 | |
Markus Armbruster | 6dd7547 | 2021-03-18 16:55:10 +0100 | [diff] [blame] | 29 | CompatPolicy compat_policy; |
| 30 | |
Markus Armbruster | db29164 | 2021-03-18 16:55:18 +0100 | [diff] [blame] | 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 | |
Markus Armbruster | 91fa93e | 2021-03-18 16:55:11 +0100 | [diff] [blame] | 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 | |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 47 | static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, |
Markus Armbruster | 69240fe | 2018-07-03 10:53:43 +0200 | [diff] [blame] | 48 | Error **errp) |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 49 | { |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 50 | const char *exec_key = NULL; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 51 | const QDictEntry *ent; |
| 52 | const char *arg_name; |
| 53 | const QObject *arg_obj; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 54 | |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 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 | |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 60 | if (!strcmp(arg_name, "execute") |
| 61 | || (!strcmp(arg_name, "exec-oob") && allow_oob)) { |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 62 | if (qobject_type(arg_obj) != QTYPE_QSTRING) { |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 63 | error_setg(errp, "QMP input member '%s' must be a string", |
| 64 | arg_name); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 65 | return NULL; |
| 66 | } |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 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; |
Markus Armbruster | 74d8c9d | 2017-03-03 13:32:21 +0100 | [diff] [blame] | 73 | } else if (!strcmp(arg_name, "arguments")) { |
| 74 | if (qobject_type(arg_obj) != QTYPE_QDICT) { |
Markus Armbruster | 10e3783 | 2017-04-27 10:41:23 +0200 | [diff] [blame] | 75 | error_setg(errp, |
| 76 | "QMP input member 'arguments' must be an object"); |
Markus Armbruster | 74d8c9d | 2017-03-03 13:32:21 +0100 | [diff] [blame] | 77 | return NULL; |
| 78 | } |
Marc-André Lureau | 4eaca8d | 2019-02-20 16:42:53 +0100 | [diff] [blame] | 79 | } else if (!strcmp(arg_name, "id")) { |
| 80 | continue; |
Markus Armbruster | 74d8c9d | 2017-03-03 13:32:21 +0100 | [diff] [blame] | 81 | } else { |
Markus Armbruster | 10e3783 | 2017-04-27 10:41:23 +0200 | [diff] [blame] | 82 | error_setg(errp, "QMP input member '%s' is unexpected", |
Markus Armbruster | 99fb0c5 | 2017-03-03 13:32:29 +0100 | [diff] [blame] | 83 | arg_name); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 84 | return NULL; |
| 85 | } |
| 86 | } |
| 87 | |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 88 | if (!exec_key) { |
Markus Armbruster | 10e3783 | 2017-04-27 10:41:23 +0200 | [diff] [blame] | 89 | error_setg(errp, "QMP input lacks member 'execute'"); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 90 | return NULL; |
| 91 | } |
| 92 | |
| 93 | return dict; |
| 94 | } |
| 95 | |
Markus Armbruster | cee3279 | 2018-07-03 10:53:48 +0200 | [diff] [blame] | 96 | QDict *qmp_error_response(Error *err) |
Luiz Capitulino | 93b91c5 | 2012-08-01 16:30:13 -0300 | [diff] [blame] | 97 | { |
Markus Armbruster | cee3279 | 2018-07-03 10:53:48 +0200 | [diff] [blame] | 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; |
Luiz Capitulino | 93b91c5 | 2012-08-01 16:30:13 -0300 | [diff] [blame] | 105 | } |
| 106 | |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 107 | /* |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 108 | * Does @qdict look like a command to be run out-of-band? |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 109 | */ |
Marc-André Lureau | 2aa788f | 2018-08-29 15:40:35 +0200 | [diff] [blame] | 110 | bool qmp_is_oob(const QDict *dict) |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 111 | { |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 112 | return qdict_haskey(dict, "exec-oob") |
| 113 | && !qdict_haskey(dict, "execute"); |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 114 | } |
| 115 | |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 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 | */ |
Marc-André Lureau | f0ccc00 | 2020-03-16 18:18:24 +0100 | [diff] [blame] | 140 | QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, |
Kevin Wolf | 41725fa | 2020-10-05 17:58:47 +0200 | [diff] [blame] | 141 | bool allow_oob, Monitor *cur_mon) |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 142 | { |
| 143 | Error *err = NULL; |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 144 | bool oob; |
| 145 | const char *command; |
| 146 | QDict *args; |
Marc-André Lureau | f0ccc00 | 2020-03-16 18:18:24 +0100 | [diff] [blame] | 147 | const QmpCommand *cmd; |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 148 | QDict *dict; |
| 149 | QObject *id; |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 150 | QObject *ret = NULL; |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 151 | QDict *rsp = NULL; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 152 | |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 153 | dict = qobject_to(QDict, request); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 154 | if (!dict) { |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 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)) { |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 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 | } |
Markus Armbruster | d203259 | 2021-03-18 16:55:17 +0100 | [diff] [blame] | 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; |
Markus Armbruster | dbb675c | 2021-03-18 16:55:19 +0100 | [diff] [blame] | 188 | case COMPAT_POLICY_INPUT_CRASH: |
Markus Armbruster | d203259 | 2021-03-18 16:55:17 +0100 | [diff] [blame] | 189 | default: |
| 190 | abort(); |
| 191 | } |
| 192 | } |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 193 | if (!cmd->enabled) { |
| 194 | error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, |
Marc-André Lureau | c98939d | 2021-02-19 12:28:14 +0400 | [diff] [blame] | 195 | "Command %s has been disabled%s%s", |
| 196 | command, |
| 197 | cmd->disable_reason ? ": " : "", |
| 198 | cmd->disable_reason ?: ""); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 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 | |
Paolo Bonzini | 164dafd | 2020-10-27 04:44:18 -0400 | [diff] [blame] | 207 | if (!qmp_command_available(cmd, &err)) { |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 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 | } |
Kevin Wolf | 41725fa | 2020-10-05 17:58:47 +0200 | [diff] [blame] | 217 | |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 218 | assert(!(oob && qemu_in_coroutine())); |
Kevin Wolf | 41725fa | 2020-10-05 17:58:47 +0200 | [diff] [blame] | 219 | assert(monitor_cur() == NULL); |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 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)); |
Kevin Wolf | 41725fa | 2020-10-05 17:58:47 +0200 | [diff] [blame] | 238 | |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 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 | } |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 251 | qobject_unref(args); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 252 | if (err) { |
Marc-André Lureau | b3fbb32 | 2020-03-25 19:47:22 +0100 | [diff] [blame] | 253 | /* or assert(!ret) after reviewing all handlers: */ |
| 254 | qobject_unref(ret); |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 255 | goto out; |
| 256 | } |
| 257 | |
| 258 | if (cmd->options & QCO_NO_SUCCESS_RESP) { |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 259 | g_assert(!ret); |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 260 | return NULL; |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 261 | } else if (!ret) { |
Markus Armbruster | 4a88373 | 2020-03-17 12:54:49 +0100 | [diff] [blame] | 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 | */ |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 267 | ret = QOBJECT(qdict_new()); |
| 268 | } |
| 269 | |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 270 | rsp = qdict_new(); |
| 271 | qdict_put_obj(rsp, "return", ret); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 272 | |
| 273 | out: |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 274 | if (err) { |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 275 | assert(!rsp); |
Markus Armbruster | cee3279 | 2018-07-03 10:53:48 +0200 | [diff] [blame] | 276 | rsp = qmp_error_response(err); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 277 | } |
| 278 | |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 279 | assert(rsp); |
| 280 | |
| 281 | if (id) { |
Marc-André Lureau | 4eaca8d | 2019-02-20 16:42:53 +0100 | [diff] [blame] | 282 | qdict_put_obj(rsp, "id", qobject_ref(id)); |
| 283 | } |
| 284 | |
Markus Armbruster | d43b169 | 2018-07-03 10:53:49 +0200 | [diff] [blame] | 285 | return rsp; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 286 | } |