qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Eric Blake <eblake@redhat.com>
To: qemu-devel@nongnu.org
Cc: Peter Xu <peterx@redhat.com>,
	Markus Armbruster <armbru@redhat.com>,
	"Dr. David Alan Gilbert" <dgilbert@redhat.com>
Subject: [Qemu-devel] [PULL 21/36] qmp: introduce QMPCapability
Date: Mon, 12 Mar 2018 13:36:12 -0500	[thread overview]
Message-ID: <20180312183628.394722-22-eblake@redhat.com> (raw)
In-Reply-To: <20180312183628.394722-1-eblake@redhat.com>

From: Peter Xu <peterx@redhat.com>

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>
---
 qapi/misc.json   | 32 ++++++++++++++++++++---
 monitor.c        | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 tests/qmp-test.c | 10 +++++++-
 3 files changed, 110 insertions(+), 9 deletions(-)

diff --git a/qapi/misc.json b/qapi/misc.json
index bcd5d10778a..b2f69eb4ece 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/monitor.c b/monitor.c
index ce9f7fc8e8a..1a95fb372c6 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;

 /*
@@ -1039,8 +1041,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 "
@@ -1048,6 +1084,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;
 }

@@ -3893,14 +3944,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)
@@ -3911,7 +3977,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/tests/qmp-test.c b/tests/qmp-test.c
index 22445d9ec25..a8384bdda3b 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 */
-- 
2.14.3

  parent reply	other threads:[~2018-03-12 18:36 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-12 18:35 [Qemu-devel] [PULL 00/36] QAPI patches for 2018-03-12, 2.12 softfreeze Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 01/36] qapi2texi: minor python code simplification Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 02/36] qlit: use QType instead of int Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 03/36] qlit: add qobject_from_qlit() Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 04/36] qapi: generate a literal qobject for introspection Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 05/36] compiler: Add QEMU_BUILD_BUG_MSG() macro Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 06/36] qapi: Add qobject_to() Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 07/36] qapi: Replace qobject_to_X(o) by qobject_to(X, o) Eric Blake
2018-03-12 18:35 ` [Qemu-devel] [PULL 08/36] qapi: Remove qobject_to_X() functions Eric Blake
2018-04-12 17:51   ` [Qemu-devel] [PULL, " Yuval Shaia
2018-04-12 20:52     ` Eric Blake
2018-04-13 11:33       ` Yuval Shaia
2018-03-12 18:36 ` [Qemu-devel] [PULL 09/36] qapi: Make more of qobject_to() Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 10/36] block: Handle null backing link Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 11/36] block: Deprecate "backing": "" Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 12/36] docs: update QMP documents for OOB commands Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 13/36] qobject: introduce qstring_get_try_str() Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 14/36] qobject: introduce qobject_get_try_str() Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 15/36] qobject: let object_property_get_str() use new API Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 16/36] monitor: move skip_flush into monitor_data_init Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 17/36] monitor: move the cur_mon hack deeper for QMP Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 18/36] monitor: unify global init Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 19/36] monitor: let mon_list be tail queue Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 20/36] monitor: allow using IO thread for parsing Eric Blake
2018-03-12 18:36 ` Eric Blake [this message]
2018-03-12 18:36 ` [Qemu-devel] [PULL 22/36] monitor: introduce monitor_qmp_respond() Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 23/36] monitor: let suspend_cnt be thread safe Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 24/36] monitor: let suspend/resume work even with QMPs Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 25/36] monitor: separate QMP parser and dispatcher Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 26/36] qmp: add new event "command-dropped" Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 27/36] monitor: send event when command queue full Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 28/36] qapi: introduce new cmd option "allow-oob" Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 29/36] qmp: support out-of-band (oob) execution Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 30/36] qmp: isolate responses into io thread Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 31/36] monitor: enable IO thread for (qmp & !mux) typed Eric Blake
2018-03-21 12:37   ` Max Reitz
2018-03-21 12:41     ` Max Reitz
2018-03-21 17:11     ` Eric Blake
2018-03-21 17:15       ` Dr. David Alan Gilbert
2018-03-22  3:09       ` Peter Xu
2018-03-12 18:36 ` [Qemu-devel] [PULL 32/36] qmp: add command "x-oob-test" Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 33/36] tests: qmp-test: verify command batching Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 34/36] tests: qmp-test: add oob test Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 35/36] block/accounting: introduce latency histogram Eric Blake
2018-03-12 18:36 ` [Qemu-devel] [PULL 36/36] qapi: add block latency histogram interface Eric Blake
2018-03-13 14:02 ` [Qemu-devel] [PULL 00/36] QAPI patches for 2018-03-12, 2.12 softfreeze Peter Maydell
2018-03-13 14:17   ` Eric Blake
2018-03-13 15:21     ` Peter Xu
2018-03-13 21:55     ` Eric Blake
2018-03-14 12:31       ` Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180312183628.394722-22-eblake@redhat.com \
    --to=eblake@redhat.com \
    --cc=armbru@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=peterx@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).