Patchwork [2/2] spice: add qmp 'query-spice' and hmp 'info spice' commands.

login
register
mail settings
Submitter Gerd Hoffmann
Date Nov. 30, 2010, 1:11 p.m.
Message ID <1291122714-2634-3-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/73731/
State New
Headers show

Comments

Gerd Hoffmann - Nov. 30, 2010, 1:11 p.m.
The patch adds a 'query-spice' monitor command which returns
informations about the spice server configuration and also a list of
channel connections.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 monitor.c       |   20 ++++++++
 qmp-commands.hx |   70 ++++++++++++++++++++++++++++
 ui/qemu-spice.h |    3 +
 ui/spice-core.c |  136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 229 insertions(+), 0 deletions(-)

Patch

diff --git a/monitor.c b/monitor.c
index 7eb8fca..bfeec0f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2543,6 +2543,16 @@  static const mon_cmd_t info_cmds[] = {
         .user_print = do_info_vnc_print,
         .mhandler.info_new = do_info_vnc,
     },
+#if defined(CONFIG_SPICE)
+    {
+        .name       = "spice",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the spice server status",
+        .user_print = do_info_spice_print,
+        .mhandler.info_new = do_info_spice,
+    },
+#endif
     {
         .name       = "name",
         .args_type  = "",
@@ -2730,6 +2740,16 @@  static const mon_cmd_t qmp_query_cmds[] = {
         .user_print = do_info_vnc_print,
         .mhandler.info_new = do_info_vnc,
     },
+#if defined(CONFIG_SPICE)
+    {
+        .name       = "spice",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the spice server status",
+        .user_print = do_info_spice_print,
+        .mhandler.info_new = do_info_spice,
+    },
+#endif
     {
         .name       = "name",
         .args_type  = "",
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 793cf1c..b2f4fe5 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1394,6 +1394,76 @@  Example:
 EQMP
 
 SQMP
+query-spice
+-----------
+
+Show SPICE server information.
+
+Return a json-object with server information. Connected clients are returned
+as a json-array of json-objects.
+
+The main json-object contains the following:
+
+- "enabled": true or false (json-bool)
+- "host": server's IP address (json-string)
+- "port": server's port number (json-int, optional)
+- "tls-port": server's port number (json-int, optional)
+- "auth": authentication method (json-string)
+         - Possible values: "none", "spice"
+- "channels": a json-array of all active channels clients
+
+Channels are described by a json-object, each one contain the following:
+
+- "host": client's IP address (json-string)
+- "family": address family (json-string)
+         - Possible values: "ipv4", "ipv6", "unix", "unknown"
+- "port": client's port number (json-string)
+- "connection-id": spice connection id.  All channels with the same id
+                   belong to the same spice session (json-int)
+- "channel-type": channel type.  "1" is the main control channel, filter for
+                  this one if you want track spice sessions only (json-int)
+- "channel-id": channel id.  Usually "0", might be different needed when
+                multiple channels of the same type exist, such as multiple
+                display channels in a multihead setup (json-int)
+- "tls": whevener the channel is encrypted (json-bool)
+
+Example:
+
+-> { "execute": "query-spice" }
+<- {
+      "return": {
+         "enabled": true,
+         "auth": "spice",
+         "port": 5920,
+         "tls-port": 5921,
+         "host": "0.0.0.0",
+         "channels": [
+            {
+               "port": "54924",
+               "family": "ipv4",
+               "channel-type": 1,
+               "connection-id": 1804289383,
+               "host": "127.0.0.1",
+               "channel-id": 0,
+               "tls": true
+            },
+            {
+               "port": "36710",
+               "family": "ipv4",
+               "channel-type": 4,
+               "connection-id": 1804289383,
+               "host": "127.0.0.1",
+               "channel-id": 0,
+               "tls": false
+            },
+            [ ... more channels follow ... ]
+         ]
+      }
+   }
+
+EQMP
+
+SQMP
 query-name
 ----------
 
diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
index 0e3ad9b..8b23ac9 100644
--- a/ui/qemu-spice.h
+++ b/ui/qemu-spice.h
@@ -33,6 +33,9 @@  void qemu_spice_audio_init(void);
 void qemu_spice_display_init(DisplayState *ds);
 int qemu_spice_add_interface(SpiceBaseInstance *sin);
 
+void do_info_spice_print(Monitor *mon, const QObject *data);
+void do_info_spice(Monitor *mon, QObject **ret_data);
+
 #else  /* CONFIG_SPICE */
 
 #define using_spice 0
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 93461c6..d29d203 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -131,6 +131,36 @@  static void watch_remove(SpiceWatch *watch)
 
 #if SPICE_INTERFACE_CORE_MINOR >= 3
 
+typedef struct ChannelList ChannelList;
+struct ChannelList {
+    SpiceChannelEventInfo *info;
+    QTAILQ_ENTRY(ChannelList) link;
+};
+static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
+
+static void channel_list_add(SpiceChannelEventInfo *info)
+{
+    ChannelList *item;
+
+    item = qemu_mallocz(sizeof(*item));
+    item->info = info;
+    QTAILQ_INSERT_TAIL(&channel_list, item, link);
+}
+
+static void channel_list_del(SpiceChannelEventInfo *info)
+{
+    ChannelList *item;
+
+    QTAILQ_FOREACH(item, &channel_list, link) {
+        if (item->info != info) {
+            continue;
+        }
+        QTAILQ_REMOVE(&channel_list, item, link);
+        qemu_free(item);
+        return;
+    }
+}
+
 static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
 {
     char host[NI_MAXHOST], port[NI_MAXSERV];
@@ -155,6 +185,22 @@  static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
     qdict_put(dict, "tls", qbool_from_int(tls));
 }
 
+static QList *channel_list_get(void)
+{
+    ChannelList *item;
+    QList *list;
+    QDict *dict;
+
+    list = qlist_new();
+    QTAILQ_FOREACH(item, &channel_list, link) {
+        dict = qdict_new();
+        add_addr_info(dict, &item->info->paddr, item->info->plen);
+        add_channel_info(dict, item->info);
+        qlist_append(list, dict);
+    }
+    return list;
+}
+
 static void channel_event(int event, SpiceChannelEventInfo *info)
 {
     static const int qevent[] = {
@@ -174,6 +220,10 @@  static void channel_event(int event, SpiceChannelEventInfo *info)
     if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
         qdict_put(server, "auth", qstring_from_str(auth));
         add_channel_info(client, info);
+        channel_list_add(info);
+    }
+    if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
+        channel_list_del(info);
     }
 
     data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
@@ -278,6 +328,92 @@  static const char *wan_compression_names[] = {
 
 /* functions for the rest of qemu */
 
+static void info_spice_iter(QObject *obj, void *opaque)
+{
+    QDict *client;
+    Monitor *mon = opaque;
+
+    client = qobject_to_qdict(obj);
+    monitor_printf(mon, "Channel:\n");
+    monitor_printf(mon, "     address: %s:%s%s\n",
+                   qdict_get_str(client, "host"),
+                   qdict_get_str(client, "port"),
+                   qdict_get_bool(client, "tls") ? " [tls]" : "");
+    monitor_printf(mon, "     session: %" PRId64 "\n",
+                   qdict_get_int(client, "connection-id"));
+    monitor_printf(mon, "     channel: %d:%d\n",
+                   (int)qdict_get_int(client, "channel-type"),
+                   (int)qdict_get_int(client, "channel-id"));
+}
+
+void do_info_spice_print(Monitor *mon, const QObject *data)
+{
+    QDict *server;
+    QList *channels;
+    const char *host;
+    int port;
+
+    server = qobject_to_qdict(data);
+    if (qdict_get_bool(server, "enabled") == 0) {
+        monitor_printf(mon, "Server: disabled\n");
+        return;
+    }
+
+    monitor_printf(mon, "Server:\n");
+    host = qdict_get_str(server, "host");
+    port = qdict_get_try_int(server, "port", -1);
+    if (port != -1) {
+        monitor_printf(mon, "     address: %s:%d\n", host, port);
+    }
+    port = qdict_get_try_int(server, "tls-port", -1);
+    if (port != -1) {
+        monitor_printf(mon, "     address: %s:%d [tls]\n", host, port);
+    }
+    monitor_printf(mon, "        auth: %s\n", qdict_get_str(server, "auth"));
+
+    channels = qdict_get_qlist(server, "channels");
+    if (qlist_empty(channels)) {
+        monitor_printf(mon, "Channels: none\n");
+    } else {
+        qlist_iter(channels, info_spice_iter, mon);
+    }
+}
+
+void do_info_spice(Monitor *mon, QObject **ret_data)
+{
+    QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+    QDict *server;
+    QList *clist;
+    const char *addr;
+    int port, tls_port;
+
+    if (!spice_server) {
+        *ret_data = qobject_from_jsonf("{ 'enabled': false }");
+        return;
+    }
+
+    addr = qemu_opt_get(opts, "addr");
+    port = qemu_opt_get_number(opts, "port", 0);
+    tls_port = qemu_opt_get_number(opts, "tls-port", 0);
+    clist = channel_list_get();
+
+    server = qdict_new();
+    qdict_put(server, "enabled", qbool_from_int(true));
+    qdict_put(server, "auth", qstring_from_str(auth));
+    qdict_put(server, "host", qstring_from_str(addr ? addr : "0.0.0.0"));
+    if (port) {
+        qdict_put(server, "port", qint_from_int(port));
+    }
+    if (tls_port) {
+        qdict_put(server, "tls-port", qint_from_int(tls_port));
+    }
+    if (clist) {
+        qdict_put(server, "channels", clist);
+    }
+
+    *ret_data = QOBJECT(server);
+}
+
 static int add_channel(const char *name, const char *value, void *opaque)
 {
     int security = 0;