summaryrefslogtreecommitdiffstats
path: root/server/spicevmc.c
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2012-11-30 13:47:55 +0100
committerMarc-André Lureau <marcandre.lureau@redhat.com>2012-12-05 11:46:28 +0100
commit069270f641b617538c17b64d4bebfd4ccb51c6dc (patch)
treef59ef0ed4e1d672396f8fefa59810a42218baaea /server/spicevmc.c
parent616eee84c11939ebe3a28b6134fe819720426f05 (diff)
downloadspice-069270f641b617538c17b64d4bebfd4ccb51c6dc.tar.gz
spice-069270f641b617538c17b64d4bebfd4ccb51c6dc.tar.xz
spice-069270f641b617538c17b64d4bebfd4ccb51c6dc.zip
server: add "port" channel support
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.
Diffstat (limited to 'server/spicevmc.c')
-rw-r--r--server/spicevmc.c152
1 files changed, 136 insertions, 16 deletions
diff --git a/server/spicevmc.c b/server/spicevmc.c
index 058a1820..aba2a5d5 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);
+}