[Spice-devel] [PATCH spice] server: add "port" channel support

Marc-André Lureau marcandre.lureau at gmail.com
Tue Dec 4 06:52:41 PST 2012


A Spice port channel carry arbitrary data between the Spice client and
the Spice server. It may be used to provide additional services on top
of a Spice connection. For example, a channel can be associated with
the qemu monitor for the client to interact with it, just like any
qemu chardev. Or it may be used with various protocols, such as the
Spice Controller.

A port kind is identified simply by its fqdn, such as org.qemu.monitor,
org.spice.spicy.test or org.ovirt.controller...

The channel is based on Spicevmc which simply tunnels data between
client and server, with a few additional messages.

See the description of the channel protocol in spice-common history.
---
 server/reds.c            |  11 +++-
 server/spice-server.syms |   5 ++
 server/spice.h           |   5 +-
 server/spicevmc.c        | 152 ++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 155 insertions(+), 18 deletions(-)

diff --git a/server/reds.c b/server/reds.c
index 1cb46f4..084620c 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3525,6 +3525,7 @@ SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance*
 #define SUBTYPE_VDAGENT "vdagent"
 #define SUBTYPE_SMARTCARD "smartcard"
 #define SUBTYPE_USBREDIR "usbredir"
+#define SUBTYPE_PORT "port"
 
 const char *spice_server_char_device_recognized_subtypes_list[] = {
     SUBTYPE_VDAGENT,
@@ -3598,6 +3599,10 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
     else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
         dev_state = spicevmc_device_connect(char_device, SPICE_CHANNEL_USBREDIR);
     }
+    else if (strcmp(char_device->subtype, SUBTYPE_PORT) == 0) {
+        dev_state = spicevmc_device_connect(char_device, SPICE_CHANNEL_PORT);
+    }
+
     if (dev_state) {
         spice_assert(char_device->st);
         /* setting the char_device state to "started" for backward compatibily with
@@ -3629,9 +3634,13 @@ static void spice_server_char_device_remove_interface(SpiceBaseInstance *sin)
         smartcard_device_disconnect(char_device);
     }
 #endif
-    else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
+    else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0 ||
+             strcmp(char_device->subtype, SUBTYPE_PORT) == 0) {
         spicevmc_device_disconnect(char_device);
+    } else {
+        spice_warning("failed to remove char device %s", char_device->subtype);
     }
+
     char_device->st = NULL;
 }
 
diff --git a/server/spice-server.syms b/server/spice-server.syms
index eadfed8..2091fe0 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -130,3 +130,8 @@ SPICE_SERVER_0.11.4 {
 global:
     spice_server_set_exit_on_disconnect;
 } SPICE_SERVER_0.11.2;
+
+SPICE_SERVER_0.12.2 {
+global:
+    spice_server_port_event;
+} SPICE_SERVER_0.11.4;
diff --git a/server/spice.h b/server/spice.h
index 22f17d6..4b86f4b 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -388,7 +388,7 @@ void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute);
 
 #define SPICE_INTERFACE_CHAR_DEVICE "char_device"
 #define SPICE_INTERFACE_CHAR_DEVICE_MAJOR 1
-#define SPICE_INTERFACE_CHAR_DEVICE_MINOR 1
+#define SPICE_INTERFACE_CHAR_DEVICE_MINOR 2
 typedef struct SpiceCharDeviceInterface SpiceCharDeviceInterface;
 typedef struct SpiceCharDeviceInstance SpiceCharDeviceInstance;
 typedef struct SpiceCharDeviceState SpiceCharDeviceState;
@@ -399,15 +399,18 @@ struct SpiceCharDeviceInterface {
     void (*state)(SpiceCharDeviceInstance *sin, int connected);
     int (*write)(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len);
     int (*read)(SpiceCharDeviceInstance *sin, uint8_t *buf, int len);
+    void (*event)(SpiceCharDeviceInstance *sin, uint8_t event);
 };
 
 struct SpiceCharDeviceInstance {
     SpiceBaseInstance base;
     const char* subtype;
     SpiceCharDeviceState *st;
+    const char* portname;
 };
 
 void spice_server_char_device_wakeup(SpiceCharDeviceInstance *sin);
+void spice_server_port_event(SpiceCharDeviceInstance *char_device, uint8_t event);
 const char** spice_server_char_device_recognized_subtypes(void);
 
 /* spice server setup */
diff --git a/server/spicevmc.c b/server/spicevmc.c
index 058a182..aba2a5d 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -28,6 +28,8 @@
 #include <netinet/in.h> // IPPROTO_TCP
 #include <netinet/tcp.h> // TCP_NODELAY
 
+#include "common/generated_server_marshallers.h"
+
 #include "char_device.h"
 #include "red_channel.h"
 #include "reds.h"
@@ -56,11 +58,25 @@ typedef struct SpiceVmcState {
     SpiceCharDeviceInstance *chardev_sin;
     SpiceVmcPipeItem *pipe_item;
     SpiceCharDeviceWriteBuffer *recv_from_client_buf;
+    uint8_t port_opened;
 } SpiceVmcState;
 
+typedef struct PortInitPipeItem {
+    PipeItem base;
+    char* name;
+    uint8_t opened;
+} PortInitPipeItem;
+
+typedef struct PortEventPipeItem {
+    PipeItem base;
+    uint8_t event;
+} PortEventPipeItem;
+
 enum {
     PIPE_ITEM_TYPE_SPICEVMC_DATA = PIPE_ITEM_TYPE_CHANNEL_BASE,
     PIPE_ITEM_TYPE_SPICEVMC_MIGRATE_DATA,
+    PIPE_ITEM_TYPE_PORT_INIT,
+    PIPE_ITEM_TYPE_PORT_EVENT,
 };
 
 static SpiceVmcPipeItem *spicevmc_pipe_item_ref(SpiceVmcPipeItem *item)
@@ -137,6 +153,27 @@ static void spicevmc_chardev_send_msg_to_client(SpiceCharDeviceMsgToClient *msg,
     red_channel_client_pipe_add_push(state->rcc, &vmc_msg->base);
 }
 
+static void spicevmc_port_send_init(RedChannelClient *rcc)
+{
+    SpiceVmcState *state = SPICE_CONTAINEROF(rcc->channel, SpiceVmcState, channel);
+    SpiceCharDeviceInstance *sin = state->chardev_sin;
+    PortInitPipeItem *item = spice_malloc(sizeof(PortInitPipeItem));
+
+    red_channel_pipe_item_init(rcc->channel, &item->base, PIPE_ITEM_TYPE_PORT_INIT);
+    item->name = strdup(sin->portname);
+    item->opened = state->port_opened;
+    red_channel_client_pipe_add_push(rcc, &item->base);
+}
+
+static void spicevmc_port_send_event(RedChannelClient *rcc, uint8_t event)
+{
+    PortEventPipeItem *item = spice_malloc(sizeof(PortEventPipeItem));
+
+    red_channel_pipe_item_init(rcc->channel, &item->base, PIPE_ITEM_TYPE_PORT_EVENT);
+    item->event = event;
+    red_channel_client_pipe_add_push(rcc, &item->base);
+}
+
 static void spicevmc_char_dev_send_tokens_to_client(RedClient *client,
                                                     uint32_t tokens,
                                                     void *opaque)
@@ -245,16 +282,32 @@ static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc,
                                                       uint8_t *msg)
 {
     SpiceVmcState *state;
+    SpiceCharDeviceInstance *sin;
+    SpiceCharDeviceInterface *sif;
 
     state = spicevmc_red_channel_client_get_state(rcc);
-    if (type != SPICE_MSGC_SPICEVMC_DATA) {
+    sin = state->chardev_sin;
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+
+    switch (type) {
+    case SPICE_MSGC_SPICEVMC_DATA:
+        spice_assert(state->recv_from_client_buf->buf == msg);
+        state->recv_from_client_buf->buf_used = size;
+        spice_char_device_write_buffer_add(state->chardev_st, state->recv_from_client_buf);
+        state->recv_from_client_buf = NULL;
+        break;
+    case SPICE_MSGC_PORT_EVENT:
+        if (size != sizeof(uint8_t)) {
+            spice_warning("bad port event message size");
+            return FALSE;
+        }
+        if (sif->base.minor_version >= 2 && sif->event != NULL)
+            sif->event(sin, *msg);
+        break;
+    default:
         return red_channel_client_handle_message(rcc, size, type, msg);
     }
 
-    spice_assert(state->recv_from_client_buf->buf == msg);
-    state->recv_from_client_buf->buf_used = size;
-    spice_char_device_write_buffer_add(state->chardev_st, state->recv_from_client_buf);
-    state->recv_from_client_buf = NULL;
     return TRUE;
 }
 
@@ -266,16 +319,23 @@ static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
 
     state = SPICE_CONTAINEROF(rcc->channel, SpiceVmcState, channel);
 
-    assert(!state->recv_from_client_buf);
+    switch (type) {
+    case SPICE_MSGC_SPICEVMC_DATA:
+        assert(!state->recv_from_client_buf);
 
-    state->recv_from_client_buf = spice_char_device_write_buffer_get(state->chardev_st,
-                                                                     rcc->client,
-                                                                     size);
-    if (!state->recv_from_client_buf) {
-        spice_error("failed to allocate write buffer");
-        return NULL;
+        state->recv_from_client_buf = spice_char_device_write_buffer_get(state->chardev_st,
+                                                                         rcc->client,
+                                                                         size);
+        if (!state->recv_from_client_buf) {
+            spice_error("failed to allocate write buffer");
+            return NULL;
+        }
+        return state->recv_from_client_buf->buf;
+
+    default:
+        return spice_malloc(size);
     }
-    return state->recv_from_client_buf->buf;
+
 }
 
 static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
@@ -287,9 +347,15 @@ static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
 
     state = SPICE_CONTAINEROF(rcc->channel, SpiceVmcState, channel);
 
-    if (state->recv_from_client_buf) { /* buffer wasn't pushed to device */
-        spice_char_device_write_buffer_release(state->chardev_st, state->recv_from_client_buf);
-        state->recv_from_client_buf = NULL;
+    switch (type) {
+    case SPICE_MSGC_SPICEVMC_DATA:
+        if (state->recv_from_client_buf) { /* buffer wasn't pushed to device */
+            spice_char_device_write_buffer_release(state->chardev_st, state->recv_from_client_buf);
+            state->recv_from_client_buf = NULL;
+        }
+        break;
+    default:
+        free(msg);
     }
 }
 
@@ -323,6 +389,32 @@ static void spicevmc_red_channel_send_migrate_data(RedChannelClient *rcc,
     spice_char_device_state_migrate_data_marshall(state->chardev_st, m);
 }
 
+static void spicevmc_red_channel_send_port_init(RedChannelClient *rcc,
+                                                SpiceMarshaller *m,
+                                                PipeItem *item)
+{
+    PortInitPipeItem *i = SPICE_CONTAINEROF(item, PortInitPipeItem, base);
+    SpiceMsgPortInit init;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_PORT_INIT, item);
+    init.name = (uint8_t *)i->name;
+    init.name_size = strlen(i->name) + 1;
+    init.opened = i->opened;
+    spice_marshall_msg_port_init(m, &init);
+}
+
+static void spicevmc_red_channel_send_port_event(RedChannelClient *rcc,
+                                                 SpiceMarshaller *m,
+                                                 PipeItem *item)
+{
+    PortEventPipeItem *i = SPICE_CONTAINEROF(item, PortEventPipeItem, base);
+    SpiceMsgPortEvent event;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_PORT_EVENT, item);
+    event.event = i->event;
+    spice_marshall_msg_port_event(m, &event);
+}
+
 static void spicevmc_red_channel_send_item(RedChannelClient *rcc,
                                            PipeItem *item)
 {
@@ -335,6 +427,12 @@ static void spicevmc_red_channel_send_item(RedChannelClient *rcc,
     case PIPE_ITEM_TYPE_SPICEVMC_MIGRATE_DATA:
         spicevmc_red_channel_send_migrate_data(rcc, m, item);
         break;
+    case PIPE_ITEM_TYPE_PORT_INIT:
+        spicevmc_red_channel_send_port_init(rcc, m, item);
+        break;
+    case PIPE_ITEM_TYPE_PORT_EVENT:
+        spicevmc_red_channel_send_port_event(rcc, m, item);
+        break;
     default:
         spice_error("bad pipe item %d", item->type);
         free(item);
@@ -384,6 +482,10 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client,
     state->rcc = rcc;
     red_channel_client_ack_zero_messages_window(rcc);
 
+    if (strcmp(sin->subtype, "port") == 0) {
+        spicevmc_port_send_init(rcc);
+    }
+
     if (!spice_char_device_client_add(state->chardev_st, client, FALSE, 0, ~0, ~0,
                                       red_channel_client_waits_for_migrate_data(rcc))) {
         spice_warning("failed to add client to spicevmc");
@@ -461,3 +563,21 @@ void spicevmc_device_disconnect(SpiceCharDeviceInstance *sin)
     free(state->pipe_item);
     red_channel_destroy(&state->channel);
 }
+
+SPICE_GNUC_VISIBLE void spice_server_port_event(SpiceCharDeviceInstance *sin, uint8_t event)
+{
+    SpiceVmcState *state;
+
+    state = (SpiceVmcState *)spice_char_device_state_opaque_get(sin->st);
+    if (event == SPICE_PORT_EVENT_OPENED) {
+        state->port_opened = TRUE;
+    } else if (event == SPICE_PORT_EVENT_CLOSED) {
+        state->port_opened = FALSE;
+    }
+
+    if (state->rcc == NULL) {
+        return;
+    }
+
+    spicevmc_port_send_event(state->rcc, event);
+}
-- 
1.7.11.7



More information about the Spice-devel mailing list