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

Alon Levy alevy at redhat.com
Tue Dec 4 07:22:08 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.

Looks good to me.

> 
> 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
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
> 


More information about the Spice-devel mailing list