qemu/qapi/qmp-dispatch.c
<<
>>
Prefs
   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
  29CompatPolicy compat_policy;
  30
  31Visitor *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
  39Visitor *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
  47static 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
  96QDict *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 */
 110bool qmp_is_oob(const QDict *dict)
 111{
 112    return qdict_haskey(dict, "exec-oob")
 113        && !qdict_haskey(dict, "execute");
 114}
 115
 116typedef 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
 125static 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 */
 140QDict *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
 273out:
 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