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" |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 24 | #include "qapi/qmp/qbool.h" |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 25 | #include "qemu/coroutine.h" |
| 26 | #include "qemu/main-loop.h" |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 27 | |
Markus Armbruster | db29164 | 2021-03-18 16:55:18 +0100 | [diff] [blame] | 28 | Visitor *qobject_input_visitor_new_qmp(QObject *obj) |
| 29 | { |
| 30 | Visitor *v = qobject_input_visitor_new(obj); |
| 31 | |
Markus Armbruster | ed29bb2 | 2021-10-25 06:24:03 +0200 | [diff] [blame] | 32 | visit_set_policy(v, &compat_policy); |
Markus Armbruster | db29164 | 2021-03-18 16:55:18 +0100 | [diff] [blame] | 33 | return v; |
| 34 | } |
| 35 | |
Markus Armbruster | 91fa93e | 2021-03-18 16:55:11 +0100 | [diff] [blame] | 36 | Visitor *qobject_output_visitor_new_qmp(QObject **result) |
| 37 | { |
| 38 | Visitor *v = qobject_output_visitor_new(result); |
| 39 | |
Markus Armbruster | ed29bb2 | 2021-10-25 06:24:03 +0200 | [diff] [blame] | 40 | visit_set_policy(v, &compat_policy); |
Markus Armbruster | 91fa93e | 2021-03-18 16:55:11 +0100 | [diff] [blame] | 41 | return v; |
| 42 | } |
| 43 | |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 44 | static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, |
Markus Armbruster | 69240fe | 2018-07-03 10:53:43 +0200 | [diff] [blame] | 45 | Error **errp) |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 46 | { |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 47 | const char *exec_key = NULL; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 48 | const QDictEntry *ent; |
| 49 | const char *arg_name; |
| 50 | const QObject *arg_obj; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 51 | |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 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 | |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 57 | if (!strcmp(arg_name, "execute") |
| 58 | || (!strcmp(arg_name, "exec-oob") && allow_oob)) { |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 59 | if (qobject_type(arg_obj) != QTYPE_QSTRING) { |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 60 | error_setg(errp, "QMP input member '%s' must be a string", |
| 61 | arg_name); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 62 | return NULL; |
| 63 | } |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 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; |
Markus Armbruster | 74d8c9d | 2017-03-03 13:32:21 +0100 | [diff] [blame] | 70 | } else if (!strcmp(arg_name, "arguments")) { |
| 71 | if (qobject_type(arg_obj) != QTYPE_QDICT) { |
Markus Armbruster | 10e3783 | 2017-04-27 10:41:23 +0200 | [diff] [blame] | 72 | error_setg(errp, |
| 73 | "QMP input member 'arguments' must be an object"); |
Markus Armbruster | 74d8c9d | 2017-03-03 13:32:21 +0100 | [diff] [blame] | 74 | return NULL; |
| 75 | } |
Marc-André Lureau | 4eaca8d | 2019-02-20 16:42:53 +0100 | [diff] [blame] | 76 | } else if (!strcmp(arg_name, "id")) { |
| 77 | continue; |
Markus Armbruster | 74d8c9d | 2017-03-03 13:32:21 +0100 | [diff] [blame] | 78 | } else { |
Markus Armbruster | 10e3783 | 2017-04-27 10:41:23 +0200 | [diff] [blame] | 79 | error_setg(errp, "QMP input member '%s' is unexpected", |
Markus Armbruster | 99fb0c5 | 2017-03-03 13:32:29 +0100 | [diff] [blame] | 80 | arg_name); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 81 | return NULL; |
| 82 | } |
| 83 | } |
| 84 | |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 85 | if (!exec_key) { |
Markus Armbruster | 10e3783 | 2017-04-27 10:41:23 +0200 | [diff] [blame] | 86 | error_setg(errp, "QMP input lacks member 'execute'"); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 87 | return NULL; |
| 88 | } |
| 89 | |
| 90 | return dict; |
| 91 | } |
| 92 | |
Markus Armbruster | cee3279 | 2018-07-03 10:53:48 +0200 | [diff] [blame] | 93 | QDict *qmp_error_response(Error *err) |
Luiz Capitulino | 93b91c5 | 2012-08-01 16:30:13 -0300 | [diff] [blame] | 94 | { |
Markus Armbruster | cee3279 | 2018-07-03 10:53:48 +0200 | [diff] [blame] | 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; |
Luiz Capitulino | 93b91c5 | 2012-08-01 16:30:13 -0300 | [diff] [blame] | 102 | } |
| 103 | |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 104 | /* |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 105 | * Does @qdict look like a command to be run out-of-band? |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 106 | */ |
Marc-André Lureau | 2aa788f | 2018-08-29 15:40:35 +0200 | [diff] [blame] | 107 | bool qmp_is_oob(const QDict *dict) |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 108 | { |
Markus Armbruster | 00ecec1 | 2018-07-03 10:53:38 +0200 | [diff] [blame] | 109 | return qdict_haskey(dict, "exec-oob") |
| 110 | && !qdict_haskey(dict, "execute"); |
Peter Xu | cf869d5 | 2018-03-10 20:38:05 -0600 | [diff] [blame] | 111 | } |
| 112 | |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 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 | */ |
Paolo Bonzini | a50c99b | 2022-12-16 12:48:26 +0100 | [diff] [blame] | 137 | QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, |
| 138 | bool allow_oob, Monitor *cur_mon) |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 139 | { |
| 140 | Error *err = NULL; |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 141 | bool oob; |
| 142 | const char *command; |
| 143 | QDict *args; |
Marc-André Lureau | f0ccc00 | 2020-03-16 18:18:24 +0100 | [diff] [blame] | 144 | const QmpCommand *cmd; |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 145 | QDict *dict; |
| 146 | QObject *id; |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 147 | QObject *ret = NULL; |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 148 | QDict *rsp = NULL; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 149 | |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 150 | dict = qobject_to(QDict, request); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 151 | if (!dict) { |
Markus Armbruster | a62c617 | 2020-03-17 12:54:48 +0100 | [diff] [blame] | 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)) { |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 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 | } |
Markus Armbruster | 7ce5fc6 | 2021-10-28 12:25:19 +0200 | [diff] [blame] | 176 | if (!compat_policy_input_ok(cmd->special_features, &compat_policy, |
| 177 | ERROR_CLASS_COMMAND_NOT_FOUND, |
| 178 | "command", command, &err)) { |
| 179 | goto out; |
Markus Armbruster | d203259 | 2021-03-18 16:55:17 +0100 | [diff] [blame] | 180 | } |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 181 | if (!cmd->enabled) { |
| 182 | error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, |
Marc-André Lureau | c98939d | 2021-02-19 12:28:14 +0400 | [diff] [blame] | 183 | "Command %s has been disabled%s%s", |
| 184 | command, |
| 185 | cmd->disable_reason ? ": " : "", |
| 186 | cmd->disable_reason ?: ""); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 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 | |
Paolo Bonzini | 164dafd | 2020-10-27 04:44:18 -0400 | [diff] [blame] | 195 | if (!qmp_command_available(cmd, &err)) { |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 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 | } |
Kevin Wolf | 41725fa | 2020-10-05 17:58:47 +0200 | [diff] [blame] | 205 | |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 206 | assert(!(oob && qemu_in_coroutine())); |
Kevin Wolf | 41725fa | 2020-10-05 17:58:47 +0200 | [diff] [blame] | 207 | assert(monitor_cur() == NULL); |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 208 | if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { |
Stefan Hajnoczi | effd60c | 2024-01-18 09:48:23 -0500 | [diff] [blame] | 209 | if (qemu_in_coroutine()) { |
| 210 | /* |
| 211 | * Move the coroutine from iohandler_ctx to qemu_aio_context for |
| 212 | * executing the command handler so that it can make progress if it |
| 213 | * involves an AIO_WAIT_WHILE(). |
| 214 | */ |
Stefan Hajnoczi | 719c681 | 2024-05-06 15:06:21 -0400 | [diff] [blame] | 215 | aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self()); |
| 216 | qemu_coroutine_yield(); |
Stefan Hajnoczi | effd60c | 2024-01-18 09:48:23 -0500 | [diff] [blame] | 217 | } |
| 218 | |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 219 | monitor_set_cur(qemu_coroutine_self(), cur_mon); |
| 220 | cmd->fn(args, &ret, &err); |
| 221 | monitor_set_cur(qemu_coroutine_self(), NULL); |
Stefan Hajnoczi | effd60c | 2024-01-18 09:48:23 -0500 | [diff] [blame] | 222 | |
| 223 | if (qemu_in_coroutine()) { |
| 224 | /* |
| 225 | * Yield and reschedule so the main loop stays responsive. |
| 226 | * |
| 227 | * Move back to iohandler_ctx so that nested event loops for |
| 228 | * qemu_aio_context don't start new monitor commands. |
| 229 | */ |
Stefan Hajnoczi | 719c681 | 2024-05-06 15:06:21 -0400 | [diff] [blame] | 230 | aio_co_schedule(iohandler_get_aio_context(), |
| 231 | qemu_coroutine_self()); |
| 232 | qemu_coroutine_yield(); |
Stefan Hajnoczi | effd60c | 2024-01-18 09:48:23 -0500 | [diff] [blame] | 233 | } |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 234 | } else { |
| 235 | /* |
| 236 | * Actual context doesn't match the one the command needs. |
| 237 | * |
| 238 | * Case 1: we are in coroutine context, but command does not |
| 239 | * have QCO_COROUTINE. We need to drop out of coroutine |
| 240 | * context for executing it. |
| 241 | * |
| 242 | * Case 2: we are outside coroutine context, but command has |
| 243 | * QCO_COROUTINE. Can't actually happen, because we get here |
| 244 | * outside coroutine context only when executing a command |
| 245 | * out of band, and OOB commands never have QCO_COROUTINE. |
| 246 | */ |
| 247 | assert(!oob && qemu_in_coroutine() && !(cmd->options & QCO_COROUTINE)); |
Kevin Wolf | 41725fa | 2020-10-05 17:58:47 +0200 | [diff] [blame] | 248 | |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 249 | QmpDispatchBH data = { |
| 250 | .cur_mon = cur_mon, |
| 251 | .cmd = cmd, |
| 252 | .args = args, |
| 253 | .ret = &ret, |
| 254 | .errp = &err, |
| 255 | .co = qemu_coroutine_self(), |
| 256 | }; |
Stefan Hajnoczi | effd60c | 2024-01-18 09:48:23 -0500 | [diff] [blame] | 257 | aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh, |
Kevin Wolf | 9ce44e2 | 2020-10-05 17:58:50 +0200 | [diff] [blame] | 258 | &data); |
| 259 | qemu_coroutine_yield(); |
| 260 | } |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 261 | qobject_unref(args); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 262 | if (err) { |
Marc-André Lureau | b3fbb32 | 2020-03-25 19:47:22 +0100 | [diff] [blame] | 263 | /* or assert(!ret) after reviewing all handlers: */ |
| 264 | qobject_unref(ret); |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 265 | goto out; |
| 266 | } |
| 267 | |
| 268 | if (cmd->options & QCO_NO_SUCCESS_RESP) { |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 269 | g_assert(!ret); |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 270 | return NULL; |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 271 | } else if (!ret) { |
Markus Armbruster | 4a88373 | 2020-03-17 12:54:49 +0100 | [diff] [blame] | 272 | /* |
| 273 | * When the command's schema has no 'returns', cmd->fn() |
| 274 | * leaves @ret null. The QMP spec calls for an empty object |
| 275 | * then; supply it. |
| 276 | */ |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 277 | ret = QOBJECT(qdict_new()); |
| 278 | } |
| 279 | |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 280 | rsp = qdict_new(); |
| 281 | qdict_put_obj(rsp, "return", ret); |
Markus Armbruster | cf4a064 | 2020-03-17 12:54:46 +0100 | [diff] [blame] | 282 | |
| 283 | out: |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 284 | if (err) { |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 285 | assert(!rsp); |
Markus Armbruster | cee3279 | 2018-07-03 10:53:48 +0200 | [diff] [blame] | 286 | rsp = qmp_error_response(err); |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 287 | } |
| 288 | |
Markus Armbruster | d322603 | 2020-03-17 12:54:47 +0100 | [diff] [blame] | 289 | assert(rsp); |
| 290 | |
| 291 | if (id) { |
Marc-André Lureau | 4eaca8d | 2019-02-20 16:42:53 +0100 | [diff] [blame] | 292 | qdict_put_obj(rsp, "id", qobject_ref(id)); |
| 293 | } |
| 294 | |
Markus Armbruster | d43b169 | 2018-07-03 10:53:49 +0200 | [diff] [blame] | 295 | return rsp; |
Michael Roth | ab02ab2 | 2011-07-19 14:50:37 -0500 | [diff] [blame] | 296 | } |