On Fri, 2017-08-25 at 10:53 +0100, Frediano Ziglio wrote: > This allows to add channels after the client is connected. "allows to add" -> "allows the server to add" > > Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> > --- > server/main-channel-client.c | 50 > ++++++++++++++++++++++++++++++++++++++++++++ > server/main-channel-client.h | 3 +++ > server/main-channel.c | 6 ++++++ > server/main-channel.h | 3 +++ > server/reds.c | 2 ++ > 5 files changed, 64 insertions(+) > > diff --git a/server/main-channel-client.c b/server/main-channel- > client.c > index 82b578c8..25c7c13e 100644 > --- a/server/main-channel-client.c > +++ b/server/main-channel-client.c > @@ -119,6 +119,11 @@ typedef struct RedMultiMediaTimePipeItem { > int time; > } RedMultiMediaTimePipeItem; > > +typedef struct RedRegisterChannelPipeItem { > + RedPipeItem base; > + RedChannel *channel; > +} RedRegisterChannelPipeItem; > + > #define ZERO_BUF_SIZE 4096 > > static const uint8_t zero_page[ZERO_BUF_SIZE] = {0}; > @@ -437,6 +442,26 @@ RedPipeItem > *main_multi_media_time_item_new(RedChannelClient *rcc, > return &item->base; > } > > +static void register_channel_pipe_item_free(RedPipeItem *base) > +{ > + RedRegisterChannelPipeItem *pipe_item = > SPICE_UPCAST(RedRegisterChannelPipeItem, base); > + > + g_object_unref(pipe_item->channel); > + free(pipe_item); > +} > + > +RedPipeItem *register_channel_item_new(RedChannelClient *rcc, void > *data, int num) > +{ > + RedChannel *channel = data; > + RedRegisterChannelPipeItem *item; > + > + item = spice_new0(RedRegisterChannelPipeItem, 1); > + red_pipe_item_init_full(&item->base, > RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL, > + register_channel_pipe_item_free); > + item->channel = g_object_ref(channel); > + return &item->base; > +} > + > void main_channel_client_handle_migrate_connected(MainChannelClient > *mcc, > int success, > int seamless) > @@ -918,6 +943,27 @@ static void > main_channel_marshall_agent_connected(SpiceMarshaller *m, > spice_marshall_msg_main_agent_connected_tokens(m, &connected); > } > > +static void > main_channel_marshall_registered_channel(RedChannelClient *rcc, > + SpiceMarshaller > *m, > + RedRegisterChan > nelPipeItem *item) > +{ > + SpiceMsgChannels* channels_info; > + RedChannel *channel = item->channel; > + > + red_channel_client_init_send_data(rcc, > SPICE_MSG_MAIN_CHANNELS_LIST); > + channels_info = (SpiceMsgChannels > *)spice_malloc(sizeof(SpiceMsgChannels) > + + 1 * sizeof(SpiceChannelId)); > + > + uint32_t type, id; > + g_object_get(channel, "channel-type", &type, "id", &id, NULL); > + channels_info->channels[0].type = type; > + channels_info->channels[0].id = id; > + channels_info->num_of_channels = 1; > + > + spice_marshall_msg_main_channels_list(m, channels_info); > + free(channels_info); > +} It's a little bit surprising to me that this works. I had always assumed (based on the name) that this protocol message was supposed to send a list *all* of the channels. I looked at the spice-gtk code and it appears that it simply creates a new client-side channel for each channel in the list, without even verifying whether a channel with that ID already exists or not. So when we get a new CHANNELS_LIST message with a single channel, it creates a new client channel of that type. Presumably this is because in the past this message was only sent once at startup so since we hadn't created any channels yet, we could assume that all channels in the list were by definition new. The protocol documentation (docs/Spice_protocol.odt) is a bit vague on this message. It says that it can only be sent after receiving a REDC_MAIN_ATTACH_CHANNELS message, but doesn't say anything about whether the message can be sent multiple times in response to a single ATTACH_CHANNELS message from the client. Given the requirement above, this patch potentially introduces a protocol violation. If the new channel happens to be created after a client connects, but before the client sends an ATTACH_CHANNELS message, we would violate that rule. We'd also probably introduce some client misbehavior in this corner case, such as: -> CHANNELS_LIST (in response to new display channel) [ client creates a channel object for the single new display channel ] <- ATTACH_CHANNELS -> CHANNELS_LIST (all channels including new display channel) [ client creates channel object for all channels, including a duplicate channel object for the new display channel ] > + > void main_channel_client_send_item(RedChannelClient *rcc, > RedPipeItem *base) > { > MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); > @@ -994,6 +1040,10 @@ void > main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem > *base) > case RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS: > main_channel_marshall_agent_connected(m, rcc, base); > break; > + case RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL: > + main_channel_marshall_registered_channel(rcc, m, > + SPICE_UPCAST(RedRegisterChannelPipeItem, base)); > + break; > default: > break; > }; > diff --git a/server/main-channel-client.h b/server/main-channel- > client.h > index a2e38c2f..6911cb07 100644 > --- a/server/main-channel-client.h > +++ b/server/main-channel-client.h > @@ -122,6 +122,7 @@ enum { > RED_PIPE_ITEM_TYPE_MAIN_NAME, > RED_PIPE_ITEM_TYPE_MAIN_UUID, > RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS, > + RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL, > }; > > typedef struct MainMouseModeItemInfo { > @@ -138,6 +139,8 @@ typedef struct MainMultiMediaTimeItemInfo { > RedPipeItem *main_multi_media_time_item_new(RedChannelClient *rcc, > void *data, int num); > > +RedPipeItem *register_channel_item_new(RedChannelClient *rcc, void > *data, int num); > + > G_END_DECLS > > #endif /* MAIN_CHANNEL_CLIENT_H_ */ > diff --git a/server/main-channel.c b/server/main-channel.c > index 4834f79b..f9be9e6c 100644 > --- a/server/main-channel.c > +++ b/server/main-channel.c > @@ -170,6 +170,12 @@ static void > main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice > main_channel->mig_target.sport = mig_target->sport; > } > > +void > +main_channel_registered_new_channel(MainChannel *main_chan, > RedChannel *channel) > +{ > + red_channel_pipes_new_add(RED_CHANNEL(main_chan), > register_channel_item_new, channel); > +} I'll just note here that this is called for *all* new channels, so looks like the server will send out a new single-channel CHANNELS_LIST message for every channel that is created. But generally channels are created before a client is ever connected, and red_channel_pipes_new_add() will simply drop the pipe item if there are no clients connected. > + > void main_channel_migrate_switch(MainChannel *main_chan, > RedsMigSpice *mig_target) > { > main_channel_fill_mig_target(main_chan, mig_target); > diff --git a/server/main-channel.h b/server/main-channel.h > index 833957dd..d2bdcc35 100644 > --- a/server/main-channel.h > +++ b/server/main-channel.h > @@ -66,6 +66,9 @@ void main_channel_push_mouse_mode(MainChannel > *main_chan, SpiceMouseMode current > void main_channel_push_agent_connected(MainChannel *main_chan); > void main_channel_push_agent_disconnected(MainChannel *main_chan); > void main_channel_push_multi_media_time(MainChannel *main_chan, int > time); > +/* tell MainChannel we have a new channel ready */ > +void main_channel_registered_new_channel(MainChannel *main_chan, > + RedChannel *channel); > > int main_channel_is_connected(MainChannel *main_chan); > > diff --git a/server/reds.c b/server/reds.c > index 005ead47..b712c182 100644 > --- a/server/reds.c > +++ b/server/reds.c > @@ -387,6 +387,8 @@ void reds_register_channel(RedsState *reds, > RedChannel *channel) > { > spice_assert(reds); > reds->channels = g_list_prepend(reds->channels, channel); > + // create new channel in the client if possible > + main_channel_registered_new_channel(reds->main_channel, > channel); > } This introduces a implicit restriction on creation order of channels. The main channel must be created first or we will crash. I personally think it would be cleaner for the SpiceServer object to emit a "new- channel" signal when a new channel is registered. Then MainChannel could connect to this signal and send out updates when it is triggered. But at the moment SpiceServer is not a GObject so it's kind of a moot point. > > void reds_unregister_channel(RedsState *reds, RedChannel *channel) _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel