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:
Peter Xu 2018-03-09 16:59:53 +08:00 committed by Eric Blake
parent a5ed352596
commit 02130314d8
3 changed files with 110 additions and 9 deletions

View file

@ -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++;