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: Peter Xu <peterx@redhat.com> Message-Id: <20180309090006.10018-11-peterx@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> [eblake: touch up commit message] Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
a5ed352596
commit
02130314d8
77
monitor.c
77
monitor.c
@ -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++;
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user