aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--monitor.c77
-rw-r--r--qapi/misc.json32
-rw-r--r--tests/qmp-test.c10
3 files changed, 110 insertions, 9 deletions
diff --git a/monitor.c b/monitor.c
index 44b2fa2..d31ec70 100644
--- a/monitor.c
+++ b/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++;
diff --git a/qapi/misc.json b/qapi/misc.json
index 6150b9a..564216a 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -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:
diff --git a/tests/qmp-test.c b/tests/qmp-test.c
index 7470c6b..d1fa1cb 100644
--- a/tests/qmp-test.c
+++ b/tests/qmp-test.c
@@ -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 */