Commit 02130314 authored by Peter Xu's avatar Peter Xu Committed by Eric Blake
Browse files

qmp: introduce QMPCapability



There were no QMP capabilities defined.  Define the first capability,
"oob", to allow out-of-band messages.

After this patch, we will allow QMP clients to enable QMP capabilities
when sending the first "qmp_capabilities" command.  Originally we are
starting QMP session with no arguments like:

  { "execute": "qmp_capabilities" }

Now we can enable some QMP capabilities using (take OOB as example,
which is the only capability that we support):

  { "execute": "qmp_capabilities",
    "arguments": { "enable": [ "oob" ] } }

When the "arguments" key is not provided, no capability is enabled.

For capability "oob", the monitor needs to be run on a dedicated IO
thread, otherwise the command will fail.  For example, trying to enable
OOB on a MUXed typed QMP monitor will fail.

One thing to mention is that QMP capabilities are per-monitor, and also
when the connection is closed due to some reason, the capabilities will
be reset.

Also, touch up qmp-test.c to test the new bits.

Signed-off-by: default avatarPeter Xu <peterx@redhat.com>
Message-Id: <20180309090006.10018-11-peterx@redhat.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
[eblake: touch up commit message]
Signed-off-by: default avatarEric Blake <eblake@redhat.com>
parent a5ed3525
Loading
Loading
Loading
Loading
+72 −5
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/qlist.h"
#include "qom/object_interfaces.h"
#include "trace-root.h"
#include "trace/control.h"
@@ -170,6 +171,7 @@ typedef struct {
     * mode.
     */
    QmpCommandList *commands;
    bool qmp_caps[QMP_CAPABILITY__MAX];
} MonitorQMP;

/*
@@ -1042,8 +1044,42 @@ static void monitor_init_qmp_commands(void)
                         qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
}

void qmp_qmp_capabilities(Error **errp)
static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list,
                           Error **errp)
{
    for (; list; list = list->next) {
        assert(list->value < QMP_CAPABILITY__MAX);
        switch (list->value) {
        case QMP_CAPABILITY_OOB:
            if (!mon->use_io_thr) {
                /*
                 * Out-Of-Band only works with monitors that are
                 * running on dedicated IOThread.
                 */
                error_setg(errp, "This monitor does not support "
                           "Out-Of-Band (OOB)");
                return;
            }
            break;
        default:
            break;
        }
    }
}

/* This function should only be called after capabilities are checked. */
static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list)
{
    for (; list; list = list->next) {
        mon->qmp.qmp_caps[list->value] = true;
    }
}

void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
                          Error **errp)
{
    Error *local_err = NULL;

    if (cur_mon->qmp.commands == &qmp_commands) {
        error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
                  "Capabilities negotiation is already complete, command "
@@ -1051,6 +1087,21 @@ void qmp_qmp_capabilities(Error **errp)
        return;
    }

    /* Enable QMP capabilities provided by the client if applicable. */
    if (has_enable) {
        qmp_caps_check(cur_mon, enable, &local_err);
        if (local_err) {
            /*
             * Failed check on any of the capabilities will fail the
             * entire command (and thus not apply any of the other
             * capabilities that were also requested).
             */
            error_propagate(errp, local_err);
            return;
        }
        qmp_caps_apply(cur_mon, enable);
    }

    cur_mon->qmp.commands = &qmp_commands;
}

@@ -3896,14 +3947,29 @@ void monitor_resume(Monitor *mon)
        readline_show_prompt(mon->rs);
}

static QObject *get_qmp_greeting(void)
static QObject *get_qmp_greeting(Monitor *mon)
{
    QList *cap_list = qlist_new();
    QObject *ver = NULL;
    QMPCapability cap;

    qmp_marshal_query_version(NULL, &ver, NULL);

    return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': []}}",
                              ver);
    for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
        if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) {
            /* Monitors that are not using IOThread won't support OOB */
            continue;
        }
        qlist_append(cap_list, qstring_from_str(QMPCapability_str(cap)));
    }

    return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}",
                              ver, cap_list);
}

static void monitor_qmp_caps_reset(Monitor *mon)
{
    memset(mon->qmp.qmp_caps, 0, sizeof(mon->qmp.qmp_caps));
}

static void monitor_qmp_event(void *opaque, int event)
@@ -3914,7 +3980,8 @@ static void monitor_qmp_event(void *opaque, int event)
    switch (event) {
    case CHR_EVENT_OPENED:
        mon->qmp.commands = &qmp_cap_negotiation_commands;
        data = get_qmp_greeting();
        monitor_qmp_caps_reset(mon);
        data = get_qmp_greeting(mon);
        monitor_json_emitter(mon, data);
        qobject_decref(data);
        mon_refcount++;
+29 −3
Original line number Diff line number Diff line
@@ -10,21 +10,47 @@
#
# Enable QMP capabilities.
#
# Arguments: None.
# Arguments:
#
# @enable:   An optional list of QMPCapability values to enable.  The
#            client must not enable any capability that is not
#            mentioned in the QMP greeting message.  If the field is not
#            provided, it means no QMP capabilities will be enabled.
#            (since 2.12)
#
# Example:
#
# -> { "execute": "qmp_capabilities" }
# -> { "execute": "qmp_capabilities",
#      "arguments": { "enable": [ "oob" ] } }
# <- { "return": {} }
#
# Notes: This command is valid exactly when first connecting: it must be
# issued before any other command will be accepted, and will fail once the
# monitor is accepting other commands. (see qemu docs/interop/qmp-spec.txt)
#
# The QMP client needs to explicitly enable QMP capabilities, otherwise
# all the QMP capabilities will be turned off by default.
#
# Since: 0.13
#
##
{ 'command': 'qmp_capabilities' }
{ 'command': 'qmp_capabilities',
  'data': { '*enable': [ 'QMPCapability' ] } }

##
# @QMPCapability:
#
# Enumeration of capabilities to be advertised during initial client
# connection, used for agreeing on particular QMP extension behaviors.
#
# @oob:   QMP ability to support Out-Of-Band requests.
#         (Please refer to qmp-spec.txt for more information on OOB)
#
# Since: 2.12
#
##
{ 'enum': 'QMPCapability',
  'data': [ 'oob' ] }

##
# @VersionTriple:
+9 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "qapi/qobject-input-visitor.h"
#include "qapi/util.h"
#include "qapi/visitor.h"
#include "qapi/qmp/qstring.h"

const char common_args[] = "-nodefaults -machine none";

@@ -79,6 +80,8 @@ static void test_qmp_protocol(void)
    QDict *resp, *q, *ret;
    QList *capabilities;
    QTestState *qts;
    const QListEntry *entry;
    QString *qstr;

    qts = qtest_init_without_qmp_handshake(common_args);

@@ -88,7 +91,12 @@ static void test_qmp_protocol(void)
    g_assert(q);
    test_version(qdict_get(q, "version"));
    capabilities = qdict_get_qlist(q, "capabilities");
    g_assert(capabilities && qlist_empty(capabilities));
    g_assert(capabilities);
    entry = qlist_first(capabilities);
    g_assert(entry);
    qstr = qobject_to(QString, entry->value);
    g_assert(qstr);
    g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
    QDECREF(resp);

    /* Test valid command before handshake */