diff --git a/SOURCES/0001-inputs-channel-Check-message-size-handling-migration.patch b/SOURCES/0001-inputs-channel-Check-message-size-handling-migration.patch new file mode 100644 index 0000000..3438403 --- /dev/null +++ b/SOURCES/0001-inputs-channel-Check-message-size-handling-migration.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Fri, 6 Oct 2017 09:38:31 +0100 +Subject: [spice-server] inputs-channel: Check message size handling migration + data + +Prevent possible buffer reading overflow. +Note that message pointer must be valid and data are checked +value by value so even on overflow you just get an error. + +Signed-off-by: Frediano Ziglio +Acked-by: Christophe Fergeau +--- + server/inputs-channel.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/server/inputs-channel.c b/server/inputs-channel.c +index 8e17cc724..11a338a26 100644 +--- a/server/inputs-channel.c ++++ b/server/inputs-channel.c +@@ -505,6 +505,11 @@ static bool inputs_channel_handle_migrate_data(RedChannelClient *rcc, + SpiceMigrateDataHeader *header; + SpiceMigrateDataInputs *mig_data; + ++ if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataInputs)) { ++ spice_warning("bad message size %u", size); ++ return FALSE; ++ } ++ + header = (SpiceMigrateDataHeader *)message; + mig_data = (SpiceMigrateDataInputs *)(header + 1); + diff --git a/SOURCES/0002-red-channel-Remove-red_channel_init_outgoing_message.patch b/SOURCES/0002-red-channel-Remove-red_channel_init_outgoing_message.patch new file mode 100644 index 0000000..1b06a0a --- /dev/null +++ b/SOURCES/0002-red-channel-Remove-red_channel_init_outgoing_message.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Fri, 6 Oct 2017 08:57:42 +0100 +Subject: [spice-server] red-channel: Remove + red_channel_init_outgoing_messages_window + +This function does not make much sense anymore. +Is called by RedVmcChannel which doesn't use RedChannelClient ACKs +so the variable changed are not used. +Moreover, at red_vmc_channel_constructed() time, there will be no +clients yet, so red_channel_init_outgoing_messages() will be a no-op. + +Signed-off-by: Frediano Ziglio +Acked-by: Christophe Fergeau +--- + server/red-channel.c | 8 -------- + server/red-channel.h | 3 --- + server/spicevmc.c | 2 -- + 3 files changed, 13 deletions(-) + +diff --git a/server/red-channel.c b/server/red-channel.c +index b5094829e..8c507f202 100644 +--- a/server/red-channel.c ++++ b/server/red-channel.c +@@ -421,14 +421,6 @@ void red_channel_push(RedChannel *channel) + g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push, NULL); + } + +-// TODO: this function doesn't make sense because the window should be client (WAN/LAN) +-// specific +-void red_channel_init_outgoing_messages_window(RedChannel *channel) +-{ +- g_list_foreach(channel->priv->clients, +- (GFunc)red_channel_client_init_outgoing_messages_window, NULL); +-} +- + void red_channel_pipes_add(RedChannel *channel, RedPipeItem *item) + { + RedChannelClient *rcc; +diff --git a/server/red-channel.h b/server/red-channel.h +index e0fe94fec..281ed0c9e 100644 +--- a/server/red-channel.h ++++ b/server/red-channel.h +@@ -157,9 +157,6 @@ void red_channel_destroy(RedChannel *channel); + /* return true if all the channel clients support the cap */ + bool red_channel_test_remote_cap(RedChannel *channel, uint32_t cap); + +-/* should be called when a new channel is ready to send messages */ +-void red_channel_init_outgoing_messages_window(RedChannel *channel); +- + // helper to push a new item to all channels + typedef RedPipeItem *(*new_pipe_item_t)(RedChannelClient *rcc, void *data, int num); + int red_channel_pipes_new_add(RedChannel *channel, new_pipe_item_t creator, void *data); +diff --git a/server/spicevmc.c b/server/spicevmc.c +index 6b9b96fc8..a1685483d 100644 +--- a/server/spicevmc.c ++++ b/server/spicevmc.c +@@ -246,8 +246,6 @@ red_vmc_channel_constructed(GObject *object) + red_channel_set_cap(RED_CHANNEL(self), SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4); + #endif + +- red_channel_init_outgoing_messages_window(RED_CHANNEL(self)); +- + reds_register_channel(reds, RED_CHANNEL(self)); + } + diff --git a/SOURCES/0003-reds-Remove-leak-allocating-migration-state.patch b/SOURCES/0003-reds-Remove-leak-allocating-migration-state.patch new file mode 100644 index 0000000..dc6b99f --- /dev/null +++ b/SOURCES/0003-reds-Remove-leak-allocating-migration-state.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Tue, 19 Sep 2017 08:28:02 +0100 +Subject: [spice-server] reds: Remove leak allocating migration state + +Signed-off-by: Frediano Ziglio +Acked-by: Christophe Fergeau +--- + server/reds.c | 2 +- + server/reds.h | 4 ---- + 2 files changed, 1 insertion(+), 5 deletions(-) + +diff --git a/server/reds.c b/server/reds.c +index 12c33570a..1b1ab94ea 100644 +--- a/server/reds.c ++++ b/server/reds.c +@@ -3378,7 +3378,7 @@ SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *reds, + return -1; + } + reds->migration_interface = SPICE_CONTAINEROF(sin, SpiceMigrateInstance, base); +- reds->migration_interface->st = spice_new0(SpiceMigrateState, 1); ++ reds->migration_interface->st = (SpiceMigrateState *)(intptr_t)1; // dummy pointer + } + + return 0; +diff --git a/server/reds.h b/server/reds.h +index 4f5fc28c3..cea002c51 100644 +--- a/server/reds.h ++++ b/server/reds.h +@@ -35,10 +35,6 @@ static inline QXLInterface * qxl_get_interface(QXLInstance *qxl) + return SPICE_CONTAINEROF(qxl->base.sif, QXLInterface, base); + } + +-struct SpiceMigrateState { +- int dummy; +-}; +- + /* main thread only */ + void reds_handle_channel_event(RedsState *reds, int event, SpiceChannelEventInfo *info); + diff --git a/SOURCES/0004-tests-Check-leaks-registering-migration-interface.patch b/SOURCES/0004-tests-Check-leaks-registering-migration-interface.patch new file mode 100644 index 0000000..3ba2f44 --- /dev/null +++ b/SOURCES/0004-tests-Check-leaks-registering-migration-interface.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Tue, 19 Sep 2017 08:27:38 +0100 +Subject: [spice-server] tests: Check leaks registering migration interface + +Signed-off-by: Frediano Ziglio +Acked-by: Christophe Fergeau +--- + server/tests/test-leaks.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/server/tests/test-leaks.c b/server/tests/test-leaks.c +index 04dcac4f4..7032000aa 100644 +--- a/server/tests/test-leaks.c ++++ b/server/tests/test-leaks.c +@@ -143,12 +143,48 @@ static void vmc_leaks(void) + basic_event_loop_destroy(); + } + ++static void migrate_cb(SpiceMigrateInstance *sin) ++{ ++} ++ ++static const SpiceMigrateInterface migrate_interface = { ++ .base = { ++ .type = SPICE_INTERFACE_MIGRATION, ++ .description = "migration", ++ .major_version = SPICE_INTERFACE_MIGRATION_MAJOR, ++ .minor_version = SPICE_INTERFACE_MIGRATION_MINOR, ++ }, ++ .migrate_connect_complete = migrate_cb, ++ .migrate_end_complete = migrate_cb, ++}; ++ ++static void migration_leaks(void) ++{ ++ SpiceCoreInterface *core; ++ SpiceServer *server = spice_server_new(); ++ SpiceMigrateInstance migrate; ++ ++ g_assert_nonnull(server); ++ ++ core = basic_event_loop_init(); ++ g_assert_nonnull(core); ++ ++ g_assert_cmpint(spice_server_init(server, core), ==, 0); ++ ++ migrate.base.sif = &migrate_interface.base; ++ spice_server_add_interface(server, &migrate.base); ++ ++ spice_server_destroy(server); ++ basic_event_loop_destroy(); ++} ++ + int main(int argc, char *argv[]) + { + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/server/server leaks", server_leaks); + g_test_add_func("/server/vmc leaks", vmc_leaks); ++ g_test_add_func("/server/migration leaks", migration_leaks); + + return g_test_run(); + } diff --git a/SOURCES/0005-Notify-client-of-the-creation-of-new-channels-dynami.patch b/SOURCES/0005-Notify-client-of-the-creation-of-new-channels-dynami.patch new file mode 100644 index 0000000..8937a67 --- /dev/null +++ b/SOURCES/0005-Notify-client-of-the-creation-of-new-channels-dynami.patch @@ -0,0 +1,179 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Thu, 24 Aug 2017 23:37:25 +0100 +Subject: [spice-server] Notify client of the creation of new channels + dynamically + +This allows the server to add channels after the client is connected. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/main-channel-client.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ + server/main-channel-client.h | 3 +++ + server/main-channel.c | 6 ++++++ + server/main-channel.h | 3 +++ + server/reds.c | 2 ++ + 5 files changed, 65 insertions(+) + +diff --git a/server/main-channel-client.c b/server/main-channel-client.c +index b7b60eddb..61a2830fa 100644 +--- a/server/main-channel-client.c ++++ b/server/main-channel-client.c +@@ -62,6 +62,7 @@ struct MainChannelClientPrivate { + int mig_wait_prev_try_seamless; + int init_sent; + int seamless_mig_dst; ++ bool initial_channels_list_sent; + uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; + }; + +@@ -119,6 +120,12 @@ typedef struct RedMultiMediaTimePipeItem { + uint32_t time; + } RedMultiMediaTimePipeItem; + ++typedef struct RedRegisteredChannelPipeItem { ++ RedPipeItem base; ++ uint32_t channel_type; ++ uint32_t channel_id; ++} RedRegisteredChannelPipeItem; ++ + #define ZERO_BUF_SIZE 4096 + + static const uint8_t zero_page[ZERO_BUF_SIZE] = {0}; +@@ -446,6 +453,20 @@ RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time) + return &item->base; + } + ++RedPipeItem *registered_channel_item_new(RedChannel *channel) ++{ ++ RedRegisteredChannelPipeItem *item; ++ ++ item = g_new0(RedRegisteredChannelPipeItem, 1); ++ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL); ++ ++ uint32_t type, id; ++ g_object_get(channel, "channel-type", &type, "id", &id, NULL); ++ item->channel_type = type; ++ item->channel_id = id; ++ return &item->base; ++} ++ + void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, + int success, + int seamless) +@@ -927,6 +948,25 @@ 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, ++ RedRegisteredChannelPipeItem *item) ++{ ++ struct { ++ SpiceMsgChannels info; ++ SpiceChannelId ids[1]; ++ } channels_info_buffer; ++ SpiceMsgChannels* channels_info = &channels_info_buffer.info; ++ ++ red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST); ++ ++ channels_info->channels[0].type = item->channel_type; ++ channels_info->channels[0].id = item->channel_id; ++ channels_info->num_of_channels = 1; ++ ++ spice_marshall_msg_main_channels_list(m, channels_info); ++} ++ + void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base) + { + MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); +@@ -947,6 +987,7 @@ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base) + switch (base->type) { + case RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST: + main_channel_marshall_channels(rcc, m, base); ++ mcc->priv->initial_channels_list_sent = true; + break; + case RED_PIPE_ITEM_TYPE_MAIN_PING: + main_channel_marshall_ping(rcc, m, +@@ -1003,6 +1044,16 @@ 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: ++ /* The spice protocol requires that the server receive a ATTACH_CHANNELS ++ * message from the client before sending any CHANNEL_LIST message. If ++ * we've already sent our initial CHANNELS_LIST message, then it should be ++ * safe to send new ones for newly-registered channels. */ ++ if (mcc->priv->initial_channels_list_sent) { ++ main_channel_marshall_registered_channel(rcc, m, ++ SPICE_UPCAST(RedRegisteredChannelPipeItem, base)); ++ } ++ break; + default: + break; + }; +diff --git a/server/main-channel-client.h b/server/main-channel-client.h +index 26b7e20b8..2cf2e3424 100644 +--- a/server/main-channel-client.h ++++ b/server/main-channel-client.h +@@ -122,12 +122,15 @@ 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, + }; + + RedPipeItem *main_mouse_mode_item_new(SpiceMouseMode current_mode, int is_client_mouse_allowed); + + RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time); + ++RedPipeItem *registered_channel_item_new(RedChannel *channel); ++ + G_END_DECLS + + #endif /* MAIN_CHANNEL_CLIENT_H_ */ +diff --git a/server/main-channel.c b/server/main-channel.c +index eca857f6b..4dc130e40 100644 +--- a/server/main-channel.c ++++ b/server/main-channel.c +@@ -149,6 +149,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_add(RED_CHANNEL(main_chan), registered_channel_item_new(channel)); ++} ++ + 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 eb3bcec3a..0cb5be728 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, uint32_t 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 1b1ab94ea..99b1fd76b 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); + } + + void reds_unregister_channel(RedsState *reds, RedChannel *channel) diff --git a/SOURCES/0006-stream-device-Add-device-to-handle-streaming.patch b/SOURCES/0006-stream-device-Add-device-to-handle-streaming.patch new file mode 100644 index 0000000..22cdee7 --- /dev/null +++ b/SOURCES/0006-stream-device-Add-device-to-handle-streaming.patch @@ -0,0 +1,194 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 11:24:58 +0000 +Subject: [spice-server] stream-device: Add device to handle streaming + +Add a stub device in guest. +The aim of this device is to make it possible for the guest to send a +stream through a DisplayChannel (in the sense of protocol channel). +This stub allows the guest to send some data and you can see some debug +lines of data arrived on host logs. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/Makefile.am | 1 + + server/char-device.h | 1 + + server/reds.c | 2 + + server/stream-device.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 134 insertions(+) + create mode 100644 server/stream-device.c + +diff --git a/server/Makefile.am b/server/Makefile.am +index 5d5590af9..f08ddf883 100644 +--- a/server/Makefile.am ++++ b/server/Makefile.am +@@ -166,6 +166,7 @@ libserver_la_SOURCES = \ + stat.h \ + stream.c \ + stream.h \ ++ stream-device.c \ + sw-canvas.c \ + tree.c \ + tree.h \ +diff --git a/server/char-device.h b/server/char-device.h +index dccd576da..54a1ef939 100644 +--- a/server/char-device.h ++++ b/server/char-device.h +@@ -236,6 +236,7 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds, + uint8_t channel_type); + void spicevmc_device_disconnect(RedsState *reds, + SpiceCharDeviceInstance *char_device); ++RedCharDevice *stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin); + + SpiceCharDeviceInterface *spice_char_device_get_interface(SpiceCharDeviceInstance *instance); + +diff --git a/server/reds.c b/server/reds.c +index 99b1fd76b..b24f61ab2 100644 +--- a/server/reds.c ++++ b/server/reds.c +@@ -3223,6 +3223,8 @@ static int spice_server_char_device_add_interface(SpiceServer *reds, + else if (strcmp(char_device->subtype, SUBTYPE_PORT) == 0) { + if (strcmp(char_device->portname, "org.spice-space.webdav.0") == 0) { + dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_WEBDAV); ++ } else if (strcmp(char_device->portname, "com.redhat.stream.0") == 0) { ++ dev_state = stream_device_connect(reds, char_device); + } else { + dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_PORT); + } +diff --git a/server/stream-device.c b/server/stream-device.c +new file mode 100644 +index 000000000..f3a147b80 +--- /dev/null ++++ b/server/stream-device.c +@@ -0,0 +1,130 @@ ++/* spice-server character device to handle a video stream ++ ++ Copyright (C) 2017 Red Hat, Inc. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, see . ++*/ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "char-device.h" ++ ++#define TYPE_STREAM_DEVICE stream_device_get_type() ++ ++#define STREAM_DEVICE(obj) \ ++ (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_DEVICE, StreamDevice)) ++#define STREAM_DEVICE_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_DEVICE, StreamDeviceClass)) ++#define STREAM_DEVICE_GET_CLASS(obj) \ ++ (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_DEVICE, StreamDeviceClass)) ++ ++typedef struct StreamDevice StreamDevice; ++typedef struct StreamDeviceClass StreamDeviceClass; ++ ++struct StreamDevice { ++ RedCharDevice parent; ++}; ++ ++struct StreamDeviceClass { ++ RedCharDeviceClass parent_class; ++}; ++ ++static GType stream_device_get_type(void) G_GNUC_CONST; ++static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds); ++ ++G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE) ++ ++static RedPipeItem * ++stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin) ++{ ++ SpiceCharDeviceInterface *sif; ++ int n; ++ ++ sif = spice_char_device_get_interface(sin); ++ ++ do { ++ uint8_t buf[256]; ++ n = sif->read(sin, buf, sizeof(buf)); ++ spice_debug("read %d bytes from device", n); ++ } while (n > 0); ++ ++ return NULL; ++} ++ ++static void ++stream_device_send_msg_to_client(RedCharDevice *self, RedPipeItem *msg, RedClient *client) ++{ ++} ++ ++static void ++stream_device_send_tokens_to_client(RedCharDevice *self, RedClient *client, uint32_t tokens) ++{ ++ spice_printerr("Not implemented!"); ++} ++ ++static void ++stream_device_remove_client(RedCharDevice *self, RedClient *client) ++{ ++} ++ ++RedCharDevice * ++stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) ++{ ++ SpiceCharDeviceInterface *sif; ++ ++ StreamDevice *dev = stream_device_new(sin, reds); ++ ++ sif = spice_char_device_get_interface(sin); ++ if (sif->state) { ++ sif->state(sin, 1); ++ } ++ ++ return RED_CHAR_DEVICE(dev); ++} ++ ++static void ++stream_device_dispose(GObject *object) ++{ ++} ++ ++static void ++stream_device_class_init(StreamDeviceClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS(klass); ++ RedCharDeviceClass *char_dev_class = RED_CHAR_DEVICE_CLASS(klass); ++ ++ object_class->dispose = stream_device_dispose; ++ ++ char_dev_class->read_one_msg_from_device = stream_device_read_msg_from_dev; ++ char_dev_class->send_msg_to_client = stream_device_send_msg_to_client; ++ char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client; ++ char_dev_class->remove_client = stream_device_remove_client; ++} ++ ++static void ++stream_device_init(StreamDevice *self) ++{ ++} ++ ++static StreamDevice * ++stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds) ++{ ++ return g_object_new(TYPE_STREAM_DEVICE, ++ "sin", sin, ++ "spice-server", reds, ++ "client-tokens-interval", 0ULL, ++ "self-tokens", ~0ULL, ++ NULL); ++} diff --git a/SOURCES/0007-stream-device-Start-parsing-new-protocol-from-guest.patch b/SOURCES/0007-stream-device-Start-parsing-new-protocol-from-guest.patch new file mode 100644 index 0000000..872697a --- /dev/null +++ b/SOURCES/0007-stream-device-Start-parsing-new-protocol-from-guest.patch @@ -0,0 +1,178 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Fri, 13 Jan 2017 18:20:43 +0000 +Subject: [spice-server] stream-device: Start parsing new protocol from guest + +Parse the data sent from the guest to the streaming device. +At the moment, the data is simply discarded after it is parsed. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-device.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 124 insertions(+), 4 deletions(-) + +diff --git a/server/stream-device.c b/server/stream-device.c +index f3a147b80..58edb3d11 100644 +--- a/server/stream-device.c ++++ b/server/stream-device.c +@@ -19,6 +19,8 @@ + #include + #endif + ++#include ++ + #include "char-device.h" + + #define TYPE_STREAM_DEVICE stream_device_get_type() +@@ -35,6 +37,9 @@ typedef struct StreamDeviceClass StreamDeviceClass; + + struct StreamDevice { + RedCharDevice parent; ++ StreamDevHeader hdr; ++ uint8_t hdr_pos; ++ bool has_error; + }; + + struct StreamDeviceClass { +@@ -46,21 +51,136 @@ static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState * + + G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE) + ++typedef bool StreamMsgHandler(StreamDevice *dev, SpiceCharDeviceInstance *sin) ++ SPICE_GNUC_WARN_UNUSED_RESULT; ++ ++static StreamMsgHandler handle_msg_format, handle_msg_data; ++ ++static bool handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin, ++ const char *error_msg) SPICE_GNUC_WARN_UNUSED_RESULT; ++ + static RedPipeItem * + stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin) + { ++ StreamDevice *dev = STREAM_DEVICE(self); + SpiceCharDeviceInterface *sif; + int n; ++ bool handled = false; ++ ++ if (dev->has_error) { ++ return NULL; ++ } + + sif = spice_char_device_get_interface(sin); + +- do { +- uint8_t buf[256]; ++ /* read header */ ++ while (dev->hdr_pos < sizeof(dev->hdr)) { ++ n = sif->read(sin, (uint8_t *) &dev->hdr, sizeof(dev->hdr) - dev->hdr_pos); ++ if (n <= 0) { ++ return NULL; ++ } ++ dev->hdr_pos += n; ++ if (dev->hdr_pos >= sizeof(dev->hdr)) { ++ dev->hdr.type = GUINT16_FROM_LE(dev->hdr.type); ++ dev->hdr.size = GUINT32_FROM_LE(dev->hdr.size); ++ } ++ } ++ ++ switch ((StreamMsgType) dev->hdr.type) { ++ case STREAM_TYPE_FORMAT: ++ if (dev->hdr.size != sizeof(StreamMsgFormat)) { ++ handled = handle_msg_invalid(dev, sin, "Wrong size for StreamMsgFormat"); ++ } else { ++ handled = handle_msg_format(dev, sin); ++ } ++ break; ++ case STREAM_TYPE_DATA: ++ handled = handle_msg_data(dev, sin); ++ break; ++ case STREAM_TYPE_CAPABILITIES: ++ /* FIXME */ ++ default: ++ handled = handle_msg_invalid(dev, sin, "Invalid message type"); ++ break; ++ } ++ ++ /* current message has been handled, so reset state and get ready to parse ++ * the next message */ ++ if (handled) { ++ dev->hdr_pos = 0; ++ } ++ ++ return NULL; ++} ++ ++static bool ++handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin, const char *error_msg) ++{ ++ static const char default_error_msg[] = "Protocol error"; ++ ++ if (!error_msg) { ++ error_msg = default_error_msg; ++ } ++ ++ int msg_size = sizeof(StreamMsgNotifyError) + strlen(error_msg) + 1; ++ int total_size = sizeof(StreamDevHeader) + msg_size; ++ ++ RedCharDevice *char_dev = RED_CHAR_DEVICE(dev); ++ RedCharDeviceWriteBuffer *buf = ++ red_char_device_write_buffer_get_server_no_token(char_dev, total_size); ++ buf->buf_used = total_size; ++ ++ StreamDevHeader *const hdr = (StreamDevHeader *)buf->buf; ++ hdr->protocol_version = STREAM_DEVICE_PROTOCOL; ++ hdr->padding = 0; ++ hdr->type = GUINT16_TO_LE(STREAM_TYPE_NOTIFY_ERROR); ++ hdr->size = GUINT32_TO_LE(msg_size); ++ ++ StreamMsgNotifyError *const error = (StreamMsgNotifyError *)(hdr+1); ++ error->error_code = GUINT32_TO_LE(0); ++ strcpy((char *) error->msg, error_msg); ++ ++ red_char_device_write_buffer_add(char_dev, buf); ++ ++ dev->has_error = true; ++ return false; ++} ++ ++static bool ++handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin) ++{ ++ StreamMsgFormat fmt; ++ SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin); ++ int n = sif->read(sin, (uint8_t *) &fmt, sizeof(fmt)); ++ if (n == 0) { ++ return false; ++ } ++ if (n != sizeof(fmt)) { ++ return handle_msg_invalid(dev, sin, NULL); ++ } ++ fmt.width = GUINT32_FROM_LE(fmt.width); ++ fmt.height = GUINT32_FROM_LE(fmt.height); ++ ++ return true; ++} ++ ++static bool ++handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin) ++{ ++ SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin); ++ int n; ++ while (1) { ++ uint8_t buf[16 * 1024]; + n = sif->read(sin, buf, sizeof(buf)); ++ /* TODO */ + spice_debug("read %d bytes from device", n); +- } while (n > 0); ++ if (n <= 0) { ++ break; ++ } ++ dev->hdr.size -= n; ++ } + +- return NULL; ++ return dev->hdr.size == 0; + } + + static void diff --git a/SOURCES/0008-stream-channel-Write-a-base-channel-to-implement-the.patch b/SOURCES/0008-stream-channel-Write-a-base-channel-to-implement-the.patch new file mode 100644 index 0000000..8d3b2c1 --- /dev/null +++ b/SOURCES/0008-stream-channel-Write-a-base-channel-to-implement-the.patch @@ -0,0 +1,271 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 15:29:18 +0000 +Subject: [spice-server] stream-channel: Write a base channel to implement the + streaming + +Currently only compile, not used and not much sense + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/Makefile.am | 2 + + server/stream-channel.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++ + server/stream-channel.h | 53 +++++++++++++++ + 3 files changed, 230 insertions(+) + create mode 100644 server/stream-channel.c + create mode 100644 server/stream-channel.h + +diff --git a/server/Makefile.am b/server/Makefile.am +index f08ddf883..e2e3ce861 100644 +--- a/server/Makefile.am ++++ b/server/Makefile.am +@@ -166,6 +166,8 @@ libserver_la_SOURCES = \ + stat.h \ + stream.c \ + stream.h \ ++ stream-channel.c \ ++ stream-channel.h \ + stream-device.c \ + sw-canvas.c \ + tree.c \ +diff --git a/server/stream-channel.c b/server/stream-channel.c +new file mode 100644 +index 000000000..fd735f562 +--- /dev/null ++++ b/server/stream-channel.c +@@ -0,0 +1,175 @@ ++/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* ++ Copyright (C) 2017 Red Hat, Inc. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, see . ++*/ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "red-channel-client.h" ++#include "stream-channel.h" ++#include "reds.h" ++#include "common-graphics-channel.h" ++ ++#define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type() ++ ++#define STREAM_CHANNEL_CLIENT(obj) \ ++ (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClient)) ++#define STREAM_CHANNEL_CLIENT_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass)) ++#define IS_STREAM_CHANNEL_CLIENT(obj) \ ++ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL_CLIENT)) ++#define IS_STREAM_CHANNEL_CLIENT_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL_CLIENT)) ++#define STREAM_CHANNEL_CLIENT_GET_CLASS(obj) \ ++ (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass)) ++ ++typedef struct StreamChannelClient StreamChannelClient; ++typedef struct StreamChannelClientClass StreamChannelClientClass; ++ ++/* we need to inherit from CommonGraphicsChannelClient ++ * to get buffer handling */ ++struct StreamChannelClient { ++ CommonGraphicsChannelClient parent; ++}; ++ ++struct StreamChannelClientClass { ++ CommonGraphicsChannelClientClass parent_class; ++}; ++ ++GType stream_channel_client_get_type(void) G_GNUC_CONST; ++ ++G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT) ++ ++struct StreamChannel { ++ RedChannel parent; ++}; ++ ++struct StreamChannelClass { ++ RedChannelClass parent_class; ++}; ++ ++G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) ++ ++static void stream_channel_client_on_disconnect(RedChannelClient *rcc); ++ ++static void ++stream_channel_client_class_init(StreamChannelClientClass *klass) ++{ ++ RedChannelClientClass *channel_class = RED_CHANNEL_CLIENT_CLASS(klass); ++ ++ channel_class->on_disconnect = stream_channel_client_on_disconnect; ++} ++ ++static void ++stream_channel_client_init(StreamChannelClient *client) ++{ ++} ++ ++static void ++stream_channel_client_on_disconnect(RedChannelClient *rcc) ++{ ++} ++ ++static StreamChannelClient* ++stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream *stream, ++ int mig_target, RedChannelCapabilities *caps) ++{ ++ StreamChannelClient *rcc; ++ ++ rcc = g_initable_new(TYPE_STREAM_CHANNEL_CLIENT, ++ NULL, NULL, ++ "channel", channel, ++ "client", client, ++ "stream", stream, ++ "monitor-latency", FALSE, ++ "caps", caps, ++ NULL); ++ ++ return rcc; ++} ++ ++static void ++stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) ++{ ++ switch (pipe_item->type) { ++ default: ++ spice_error("invalid pipe item type"); ++ } ++ ++ red_channel_client_begin_send_message(rcc); ++} ++ ++StreamChannel* ++stream_channel_new(RedsState *server, uint32_t id) ++{ ++ return g_object_new(TYPE_STREAM_CHANNEL, ++ "spice-server", server, ++ "core-interface", reds_get_core_interface(server), ++ "channel-type", SPICE_CHANNEL_DISPLAY, ++ // TODO this id should be after all qxl devices ++ "id", id, ++ "migration-flags", 0, ++ "handle-acks", TRUE, // TODO sure ?? ++ NULL); ++} ++ ++static void ++stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream, ++ int migration, RedChannelCapabilities *caps) ++{ ++ StreamChannel *channel = STREAM_CHANNEL(red_channel); ++ StreamChannelClient *client; ++ ++ spice_return_if_fail(stream != NULL); ++ ++ client = stream_channel_client_new(channel, red_client, stream, migration, caps); ++ spice_return_if_fail(client != NULL); ++} ++ ++static void ++stream_channel_constructed(GObject *object) ++{ ++ ClientCbs client_cbs = { NULL, }; ++ RedChannel *red_channel = RED_CHANNEL(object); ++ RedsState *reds = red_channel_get_server(red_channel); ++ ++ G_OBJECT_CLASS(stream_channel_parent_class)->constructed(object); ++ ++ client_cbs.connect = stream_channel_connect; ++ red_channel_register_client_cbs(red_channel, &client_cbs, NULL); ++ ++ reds_register_channel(reds, red_channel); ++} ++ ++static void ++stream_channel_class_init(StreamChannelClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS(klass); ++ RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); ++ ++ object_class->constructed = stream_channel_constructed; ++ ++ channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL); ++ channel_class->handle_message = red_channel_client_handle_message; ++ ++ channel_class->send_item = stream_channel_send_item; ++} ++ ++static void ++stream_channel_init(StreamChannel *channel) ++{ ++} +diff --git a/server/stream-channel.h b/server/stream-channel.h +new file mode 100644 +index 000000000..e50e17e9e +--- /dev/null ++++ b/server/stream-channel.h +@@ -0,0 +1,53 @@ ++/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* ++ Copyright (C) 2017 Red Hat, Inc. ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, see . ++*/ ++ ++#ifndef STREAM_CHANNEL_H_ ++#define STREAM_CHANNEL_H_ ++ ++#include "red-channel.h" ++ ++G_BEGIN_DECLS ++ ++/** ++ * This type it's a RedChannel class which implement display ++ * channel with input only by stream. ++ * A pointer to StreamChannel can be converted to a RedChannel. ++ */ ++typedef struct StreamChannel StreamChannel; ++typedef struct StreamChannelClass StreamChannelClass; ++ ++#define TYPE_STREAM_CHANNEL stream_channel_get_type() ++ ++#define STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL, StreamChannel)) ++#define STREAM_CHANNEL_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL, StreamChannelClass)) ++#define IS_STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL)) ++#define IS_STREAM_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL)) ++#define STREAM_CHANNEL_GET_CLASS(obj) \ ++ (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL, StreamChannelClass)) ++ ++GType stream_channel_get_type(void) G_GNUC_CONST; ++ ++/** ++ * Create StreamChannel. ++ */ ++StreamChannel* stream_channel_new(RedsState *server, uint32_t id); ++ ++G_END_DECLS ++ ++#endif /* STREAM_CHANNEL_H_ */ diff --git a/SOURCES/0009-stream-channel-Start-implementing-DisplayChannel-pro.patch b/SOURCES/0009-stream-channel-Start-implementing-DisplayChannel-pro.patch new file mode 100644 index 0000000..d35ed88 --- /dev/null +++ b/SOURCES/0009-stream-channel-Start-implementing-DisplayChannel-pro.patch @@ -0,0 +1,88 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Tue, 14 Mar 2017 12:12:22 +0000 +Subject: [spice-server] stream-channel: Start implementing DisplayChannel + properly + +Handle messages from clients. +Send some messages to clients. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 40 insertions(+), 1 deletion(-) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index fd735f562..df6936513 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -113,6 +113,25 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) + red_channel_client_begin_send_message(rcc); + } + ++static bool ++handle_message(RedChannelClient *rcc, uint16_t type, uint32_t size, void *msg) ++{ ++ switch (type) { ++ case SPICE_MSGC_DISPLAY_INIT: ++ case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION: ++ return true; ++ case SPICE_MSGC_DISPLAY_STREAM_REPORT: ++ /* TODO these will help tune the streaming reducing/increasing quality */ ++ return true; ++ case SPICE_MSGC_DISPLAY_GL_DRAW_DONE: ++ /* client should not send this message */ ++ return false; ++ default: ++ return red_channel_client_handle_message(rcc, type, size, msg); ++ } ++} ++ ++ + StreamChannel* + stream_channel_new(RedsState *server, uint32_t id) + { +@@ -138,6 +157,22 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea + + client = stream_channel_client_new(channel, red_client, stream, migration, caps); + spice_return_if_fail(client != NULL); ++ ++ // TODO set capabilities like SPICE_DISPLAY_CAP_MONITORS_CONFIG ++ // see guest_set_client_capabilities ++ RedChannelClient *rcc = RED_CHANNEL_CLIENT(client); ++ red_channel_client_push_set_ack(rcc); ++ ++ // TODO what should happen on migration, dcc return if on migration wait ?? ++ red_channel_client_ack_zero_messages_window(rcc); ++ ++ // "emulate" dcc_start ++ // TODO only if "surface" ++ red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); ++ // TODO red_surface_create_item_new ++ // TODO surface data ?? ++ // TODO monitor configs ?? ++ red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK); + } + + static void +@@ -152,6 +187,10 @@ stream_channel_constructed(GObject *object) + client_cbs.connect = stream_channel_connect; + red_channel_register_client_cbs(red_channel, &client_cbs, NULL); + ++ // TODO, send monitor to support multiple monitors in the future ++// red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG); ++ red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_STREAM_REPORT); ++ + reds_register_channel(reds, red_channel); + } + +@@ -164,7 +203,7 @@ stream_channel_class_init(StreamChannelClass *klass) + object_class->constructed = stream_channel_constructed; + + channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL); +- channel_class->handle_message = red_channel_client_handle_message; ++ channel_class->handle_message = handle_message; + + channel_class->send_item = stream_channel_send_item; + } diff --git a/SOURCES/0010-stream-device-Create-channel-for-stream-device.patch b/SOURCES/0010-stream-device-Create-channel-for-stream-device.patch new file mode 100644 index 0000000..41b7341 --- /dev/null +++ b/SOURCES/0010-stream-device-Create-channel-for-stream-device.patch @@ -0,0 +1,151 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 14:02:29 +0000 +Subject: [spice-server] stream-device: Create channel for stream device + +So can be used by the device to communicate with the clients. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- + server/stream-device.c | 14 +++++++++++++ + 2 files changed, 64 insertions(+), 2 deletions(-) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index df6936513..baf3d58a6 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -19,6 +19,8 @@ + #include + #endif + ++#include ++ + #include "red-channel-client.h" + #include "stream-channel.h" + #include "reds.h" +@@ -64,6 +66,13 @@ struct StreamChannelClass { + + G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) + ++enum { ++ RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST, ++ RED_PIPE_ITEM_TYPE_FILL_SURFACE, ++}; ++ ++#define PRIMARY_SURFACE_ID 0 ++ + static void stream_channel_client_on_disconnect(RedChannelClient *rcc); + + static void +@@ -103,9 +112,46 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream + } + + static void ++fill_base(SpiceMarshaller *m) ++{ ++ SpiceMsgDisplayBase base; ++ ++ base.surface_id = PRIMARY_SURFACE_ID; ++ base.box = (SpiceRect) { 0, 0, 1024, 768 }; ++ base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; ++ ++ spice_marshall_DisplayBase(m, &base); ++} ++ ++static void + stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) + { ++ SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); ++ + switch (pipe_item->type) { ++ case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: { ++ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE); ++ SpiceMsgSurfaceCreate surface_create = { ++ PRIMARY_SURFACE_ID, ++ 1024, 768, ++ SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY ++ }; ++ spice_marshall_msg_display_surface_create(m, &surface_create); ++ break; ++ } ++ case RED_PIPE_ITEM_TYPE_FILL_SURFACE: { ++ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL); ++ ++ fill_base(m); ++ ++ SpiceFill fill; ++ fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } }; ++ fill.rop_descriptor = SPICE_ROPD_OP_PUT; ++ fill.mask = (SpiceQMask) { 0, { 0, 0 }, NULL }; ++ SpiceMarshaller *brush_pat_out, *mask_bitmap_out; ++ spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out); ++ break; ++ } + default: + spice_error("invalid pipe item type"); + } +@@ -169,8 +215,10 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea + // "emulate" dcc_start + // TODO only if "surface" + red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); +- // TODO red_surface_create_item_new +- // TODO surface data ?? ++ // TODO pass proper data ++ red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); ++ // surface data ++ red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE); + // TODO monitor configs ?? + red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK); + } +diff --git a/server/stream-device.c b/server/stream-device.c +index 58edb3d11..0c9173ae0 100644 +--- a/server/stream-device.c ++++ b/server/stream-device.c +@@ -22,6 +22,8 @@ + #include + + #include "char-device.h" ++#include "stream-channel.h" ++#include "reds.h" + + #define TYPE_STREAM_DEVICE stream_device_get_type() + +@@ -37,9 +39,11 @@ typedef struct StreamDeviceClass StreamDeviceClass; + + struct StreamDevice { + RedCharDevice parent; ++ + StreamDevHeader hdr; + uint8_t hdr_pos; + bool has_error; ++ StreamChannel *stream_channel; + }; + + struct StreamDeviceClass { +@@ -204,7 +208,10 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) + { + SpiceCharDeviceInterface *sif; + ++ StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id ++ + StreamDevice *dev = stream_device_new(sin, reds); ++ dev->stream_channel = stream_channel; + + sif = spice_char_device_get_interface(sin); + if (sif->state) { +@@ -217,6 +224,13 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) + static void + stream_device_dispose(GObject *object) + { ++ StreamDevice *dev = STREAM_DEVICE(object); ++ ++ if (dev->stream_channel) { ++ // close all current connections and drop the reference ++ red_channel_destroy(RED_CHANNEL(dev->stream_channel)); ++ dev->stream_channel = NULL; ++ } + } + + static void diff --git a/SOURCES/0011-stream-device-Handle-streaming-data-from-device-to-c.patch b/SOURCES/0011-stream-device-Handle-streaming-data-from-device-to-c.patch new file mode 100644 index 0000000..2e53f13 --- /dev/null +++ b/SOURCES/0011-stream-device-Handle-streaming-data-from-device-to-c.patch @@ -0,0 +1,231 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 10:47:40 +0000 +Subject: [spice-server] stream-device: Handle streaming data from device to + channel + +Handle stream data from device sending to the channel. +The StreamChannel will forward the data to the clients using standard +DisplayChannel messages, and will create and destroy streams as +necessary. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ + server/stream-channel.h | 8 ++++ + server/stream-device.c | 4 +- + 3 files changed, 113 insertions(+), 2 deletions(-) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index baf3d58a6..8931d8794 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -20,11 +20,13 @@ + #endif + + #include ++#include + + #include "red-channel-client.h" + #include "stream-channel.h" + #include "reds.h" + #include "common-graphics-channel.h" ++#include "display-limits.h" + + #define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type() + +@@ -46,6 +48,10 @@ typedef struct StreamChannelClientClass StreamChannelClientClass; + * to get buffer handling */ + struct StreamChannelClient { + CommonGraphicsChannelClient parent; ++ ++ /* current video stream id, <0 if not initialized or ++ * we are not sending a stream */ ++ int stream_id; + }; + + struct StreamChannelClientClass { +@@ -58,6 +64,10 @@ G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_C + + struct StreamChannel { + RedChannel parent; ++ ++ /* current video stream id, <0 if not initialized or ++ * we are not sending a stream */ ++ int stream_id; + }; + + struct StreamChannelClass { +@@ -69,8 +79,22 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) + enum { + RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST, + RED_PIPE_ITEM_TYPE_FILL_SURFACE, ++ RED_PIPE_ITEM_TYPE_STREAM_CREATE, ++ RED_PIPE_ITEM_TYPE_STREAM_DATA, ++ RED_PIPE_ITEM_TYPE_STREAM_DESTROY, + }; + ++typedef struct StreamCreateItem { ++ RedPipeItem base; ++ SpiceMsgDisplayStreamCreate stream_create; ++} StreamCreateItem; ++ ++typedef struct StreamDataItem { ++ RedPipeItem base; ++ // NOTE: this must be the last field in the structure ++ SpiceMsgDisplayStreamData data; ++} StreamDataItem; ++ + #define PRIMARY_SURFACE_ID 0 + + static void stream_channel_client_on_disconnect(RedChannelClient *rcc); +@@ -86,6 +110,7 @@ stream_channel_client_class_init(StreamChannelClientClass *klass) + static void + stream_channel_client_init(StreamChannelClient *client) + { ++ client->stream_id = -1; + } + + static void +@@ -127,6 +152,7 @@ static void + stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) + { + SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); ++ StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc); + + switch (pipe_item->type) { + case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: { +@@ -152,6 +178,32 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) + spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out); + break; + } ++ case RED_PIPE_ITEM_TYPE_STREAM_CREATE: { ++ StreamCreateItem *item = SPICE_UPCAST(StreamCreateItem, pipe_item); ++ client->stream_id = item->stream_create.id; ++ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE); ++ spice_marshall_msg_display_stream_create(m, &item->stream_create); ++ break; ++ } ++ case RED_PIPE_ITEM_TYPE_STREAM_DATA: { ++ StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item); ++ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA); ++ spice_marshall_msg_display_stream_data(m, &item->data); ++ red_pipe_item_ref(pipe_item); ++ spice_marshaller_add_by_ref_full(m, item->data.data, item->data.data_size, ++ marshaller_unref_pipe_item, pipe_item); ++ break; ++ } ++ case RED_PIPE_ITEM_TYPE_STREAM_DESTROY: { ++ if (client->stream_id < 0) { ++ return; ++ } ++ SpiceMsgDisplayStreamDestroy stream_destroy = { client->stream_id }; ++ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY); ++ spice_marshall_msg_display_stream_destroy(m, &stream_destroy); ++ client->stream_id = -1; ++ break; ++ } + default: + spice_error("invalid pipe item type"); + } +@@ -259,4 +311,55 @@ stream_channel_class_init(StreamChannelClass *klass) + static void + stream_channel_init(StreamChannel *channel) + { ++ channel->stream_id = -1; ++} ++ ++void ++stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) ++{ ++ RedChannel *red_channel = RED_CHANNEL(channel); ++ ++ // send destroy old stream ++ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY); ++ ++ // TODO send new create surface if required ++ ++ // allocate a new stream id ++ channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS; ++ ++ // send create stream ++ StreamCreateItem *item = g_new0(StreamCreateItem, 1); ++ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_CREATE); ++ item->stream_create.id = channel->stream_id; ++ item->stream_create.flags = SPICE_STREAM_FLAGS_TOP_DOWN; ++ item->stream_create.codec_type = fmt->codec; ++ item->stream_create.stream_width = fmt->width; ++ item->stream_create.stream_height = fmt->height; ++ item->stream_create.src_width = fmt->width; ++ item->stream_create.src_height = fmt->height; ++ item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height }; ++ item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; ++ red_channel_pipes_add(red_channel, &item->base); ++} ++ ++void ++stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time) ++{ ++ if (channel->stream_id < 0) { ++ // this condition can happen if the guest didn't handle ++ // the format stop that we send so think the stream is still ++ // started ++ return; ++ } ++ ++ RedChannel *red_channel = RED_CHANNEL(channel); ++ ++ StreamDataItem *item = g_malloc(sizeof(*item) + size); ++ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA); ++ item->data.base.id = channel->stream_id; ++ item->data.base.multi_media_time = mm_time; ++ item->data.data_size = size; ++ // TODO try to optimize avoiding the copy ++ memcpy(item->data.data, data, size); ++ red_channel_pipes_add(red_channel, &item->base); + } +diff --git a/server/stream-channel.h b/server/stream-channel.h +index e50e17e9e..156a75d31 100644 +--- a/server/stream-channel.h ++++ b/server/stream-channel.h +@@ -48,6 +48,14 @@ GType stream_channel_get_type(void) G_GNUC_CONST; + */ + StreamChannel* stream_channel_new(RedsState *server, uint32_t id); + ++struct StreamMsgFormat; ++ ++void stream_channel_change_format(StreamChannel *channel, ++ const struct StreamMsgFormat *fmt); ++void stream_channel_send_data(StreamChannel *channel, ++ const void *data, size_t size, ++ uint32_t mm_time); ++ + G_END_DECLS + + #endif /* STREAM_CHANNEL_H_ */ +diff --git a/server/stream-device.c b/server/stream-device.c +index 0c9173ae0..6e78b1a99 100644 +--- a/server/stream-device.c ++++ b/server/stream-device.c +@@ -164,6 +164,7 @@ handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin) + } + fmt.width = GUINT32_FROM_LE(fmt.width); + fmt.height = GUINT32_FROM_LE(fmt.height); ++ stream_channel_change_format(dev->stream_channel, &fmt); + + return true; + } +@@ -176,11 +177,10 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin) + while (1) { + uint8_t buf[16 * 1024]; + n = sif->read(sin, buf, sizeof(buf)); +- /* TODO */ +- spice_debug("read %d bytes from device", n); + if (n <= 0) { + break; + } ++ stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time()); + dev->hdr.size -= n; + } + diff --git a/SOURCES/0012-stream-channel-Allows-not-fixed-size.patch b/SOURCES/0012-stream-channel-Allows-not-fixed-size.patch new file mode 100644 index 0000000..354002e --- /dev/null +++ b/SOURCES/0012-stream-channel-Allows-not-fixed-size.patch @@ -0,0 +1,115 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 19:03:11 +0000 +Subject: [spice-server] stream-channel: Allows not fixed size + +Remove the fixed size stream and support any display size. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 31 +++++++++++++++++++++++++------ + 1 file changed, 25 insertions(+), 6 deletions(-) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index 8931d8794..59f566f50 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -68,6 +68,8 @@ struct StreamChannel { + /* current video stream id, <0 if not initialized or + * we are not sending a stream */ + int stream_id; ++ /* size of the current video stream */ ++ unsigned width, height; + }; + + struct StreamChannelClass { +@@ -78,6 +80,7 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) + + enum { + RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST, ++ RED_PIPE_ITEM_TYPE_SURFACE_DESTROY, + RED_PIPE_ITEM_TYPE_FILL_SURFACE, + RED_PIPE_ITEM_TYPE_STREAM_CREATE, + RED_PIPE_ITEM_TYPE_STREAM_DATA, +@@ -137,12 +140,12 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream + } + + static void +-fill_base(SpiceMarshaller *m) ++fill_base(SpiceMarshaller *m, const StreamChannel *channel) + { + SpiceMsgDisplayBase base; + + base.surface_id = PRIMARY_SURFACE_ID; +- base.box = (SpiceRect) { 0, 0, 1024, 768 }; ++ base.box = (SpiceRect) { 0, 0, channel->width, channel->height }; + base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; + + spice_marshall_DisplayBase(m, &base); +@@ -153,22 +156,29 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) + { + SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); + StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc); ++ StreamChannel *channel = STREAM_CHANNEL(red_channel_client_get_channel(rcc)); + + switch (pipe_item->type) { + case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: { + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE); + SpiceMsgSurfaceCreate surface_create = { + PRIMARY_SURFACE_ID, +- 1024, 768, ++ channel->width, channel->height, + SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY + }; + spice_marshall_msg_display_surface_create(m, &surface_create); + break; + } ++ case RED_PIPE_ITEM_TYPE_SURFACE_DESTROY: { ++ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY); ++ SpiceMsgSurfaceDestroy surface_destroy = { PRIMARY_SURFACE_ID }; ++ spice_marshall_msg_display_surface_destroy(m, &surface_destroy); ++ break; ++ } + case RED_PIPE_ITEM_TYPE_FILL_SURFACE: { + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL); + +- fill_base(m); ++ fill_base(m, channel); + + SpiceFill fill; + fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } }; +@@ -267,7 +277,7 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea + // "emulate" dcc_start + // TODO only if "surface" + red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); +- // TODO pass proper data ++ // pass proper data + red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); + // surface data + red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE); +@@ -312,6 +322,8 @@ static void + stream_channel_init(StreamChannel *channel) + { + channel->stream_id = -1; ++ channel->width = 1024; ++ channel->height = 768; + } + + void +@@ -322,7 +334,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) + // send destroy old stream + red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY); + +- // TODO send new create surface if required ++ // send new create surface if required ++ if (channel->width != fmt->width || channel->height != fmt->height) { ++ channel->width = fmt->width; ++ channel->height = fmt->height; ++ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); ++ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); ++ // TODO monitors config ?? ++ } + + // allocate a new stream id + channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS; diff --git a/SOURCES/0013-stream-channel-Allows-to-register-callback-to-get-ne.patch b/SOURCES/0013-stream-channel-Allows-to-register-callback-to-get-ne.patch new file mode 100644 index 0000000..7ed0b16 --- /dev/null +++ b/SOURCES/0013-stream-channel-Allows-to-register-callback-to-get-ne.patch @@ -0,0 +1,67 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 19:08:12 +0000 +Subject: [spice-server] stream-channel: Allows to register callback to get new + stream request + +The channel needs to communicate when it receive a new +stream request from the guest. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 12 ++++++++++++ + server/stream-channel.h | 6 ++++++ + 2 files changed, 18 insertions(+) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index 59f566f50..be6f7c7d1 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -70,6 +70,10 @@ struct StreamChannel { + int stream_id; + /* size of the current video stream */ + unsigned width, height; ++ ++ /* callback to notify when a stream should be started or stopped */ ++ stream_channel_start_proc start_cb; ++ void *start_opaque; + }; + + struct StreamChannelClass { +@@ -382,3 +386,11 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, + memcpy(item->data.data, data, size); + red_channel_pipes_add(red_channel, &item->base); + } ++ ++void ++stream_channel_register_start_cb(StreamChannel *channel, ++ stream_channel_start_proc cb, void *opaque) ++{ ++ channel->start_cb = cb; ++ channel->start_opaque = opaque; ++} +diff --git a/server/stream-channel.h b/server/stream-channel.h +index 156a75d31..ba098df49 100644 +--- a/server/stream-channel.h ++++ b/server/stream-channel.h +@@ -49,6 +49,7 @@ GType stream_channel_get_type(void) G_GNUC_CONST; + StreamChannel* stream_channel_new(RedsState *server, uint32_t id); + + struct StreamMsgFormat; ++struct StreamMsgStartStop; + + void stream_channel_change_format(StreamChannel *channel, + const struct StreamMsgFormat *fmt); +@@ -56,6 +57,11 @@ void stream_channel_send_data(StreamChannel *channel, + const void *data, size_t size, + uint32_t mm_time); + ++typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartStop *start, ++ StreamChannel *channel); ++void stream_channel_register_start_cb(StreamChannel *channel, ++ stream_channel_start_proc cb, void *opaque); ++ + G_END_DECLS + + #endif /* STREAM_CHANNEL_H_ */ diff --git a/SOURCES/0014-stream-channel-Support-client-connection-disconnecti.patch b/SOURCES/0014-stream-channel-Support-client-connection-disconnecti.patch new file mode 100644 index 0000000..d71ee50 --- /dev/null +++ b/SOURCES/0014-stream-channel-Support-client-connection-disconnecti.patch @@ -0,0 +1,131 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 19:03:19 +0000 +Subject: [spice-server] stream-channel: Support client + connection/disconnection + +When a new client is connected we must restart the stream so new +clients can receive correct data without having to wait for the +next full screen (which on idle screen could take ages). +On disconnection we should tell the guest to stop streaming +not wasting resources to stream not needed data. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 81 insertions(+) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index be6f7c7d1..2ad9ebae3 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -121,8 +121,32 @@ stream_channel_client_init(StreamChannelClient *client) + } + + static void ++request_new_stream(StreamChannel *channel, StreamMsgStartStop *start) ++{ ++ if (channel->start_cb) { ++ channel->start_cb(channel->start_opaque, start, channel); ++ } ++} ++ ++static void + stream_channel_client_on_disconnect(RedChannelClient *rcc) + { ++ RedChannel *red_channel = red_channel_client_get_channel(rcc); ++ ++ // if there are still some client connected keep streaming ++ // TODO, maybe would be worth sending new codecs if they are better ++ if (red_channel_is_connected(red_channel)) { ++ return; ++ } ++ ++ StreamChannel *channel = STREAM_CHANNEL(red_channel); ++ channel->stream_id = -1; ++ channel->width = 0; ++ channel->height = 0; ++ ++ // send stream stop to device ++ StreamMsgStartStop stop = { 0, }; ++ request_new_stream(channel, &stop); + } + + static StreamChannelClient* +@@ -258,18 +282,75 @@ stream_channel_new(RedsState *server, uint32_t id) + NULL); + } + ++#define MAX_SUPPORTED_CODECS SPICE_VIDEO_CODEC_TYPE_ENUM_END ++ ++// find common codecs supported by all clients ++static uint8_t ++stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs) ++{ ++ RedChannelClient *rcc; ++ int codec; ++ ++ static const uint16_t codec2cap[] = { ++ 0, // invalid ++ SPICE_DISPLAY_CAP_CODEC_MJPEG, ++ SPICE_DISPLAY_CAP_CODEC_VP8, ++ SPICE_DISPLAY_CAP_CODEC_H264, ++ SPICE_DISPLAY_CAP_CODEC_VP9, ++ }; ++ ++ bool supported[SPICE_N_ELEMENTS(codec2cap)]; ++ ++ for (codec = 0; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) { ++ supported[codec] = true; ++ } ++ ++ FOREACH_CLIENT(channel, rcc) { ++ for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) { ++ // if do not support codec delete from list ++ if (!red_channel_client_test_remote_cap(rcc, codec2cap[codec])) { ++ supported[codec] = false; ++ } ++ } ++ } ++ ++ // surely mjpeg is supported ++ supported[SPICE_VIDEO_CODEC_TYPE_MJPEG] = true; ++ ++ int num = 0; ++ for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) { ++ if (supported[codec]) { ++ out_codecs[num++] = codec; ++ } ++ } ++ ++ return num; ++} ++ + static void + stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream, + int migration, RedChannelCapabilities *caps) + { + StreamChannel *channel = STREAM_CHANNEL(red_channel); + StreamChannelClient *client; ++ struct { ++ StreamMsgStartStop base; ++ uint8_t codecs_buffer[MAX_SUPPORTED_CODECS]; ++ } start_msg; ++ StreamMsgStartStop *const start = &start_msg.base; + + spice_return_if_fail(stream != NULL); + + client = stream_channel_client_new(channel, red_client, stream, migration, caps); + spice_return_if_fail(client != NULL); + ++ // request new stream ++ start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs); ++ // send in any case, even if list is not changed ++ // notify device about changes ++ request_new_stream(channel, start); ++ ++ + // TODO set capabilities like SPICE_DISPLAY_CAP_MONITORS_CONFIG + // see guest_set_client_capabilities + RedChannelClient *rcc = RED_CHANNEL_CLIENT(client); diff --git a/SOURCES/0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch b/SOURCES/0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch new file mode 100644 index 0000000..8d52cf4 --- /dev/null +++ b/SOURCES/0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sat, 21 Jan 2017 09:01:18 +0000 +Subject: [spice-server] stream-channel: Do not show an empty blank screen on + start + +Start showing something when we have a surface and stream +instead of showing a blank screen which is now not useful. +Was useful for debugging purposes to understand that the +new channel was sending messages correctly to client and +client could handle them. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index 2ad9ebae3..e89563b82 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -360,8 +360,13 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea + red_channel_client_ack_zero_messages_window(rcc); + + // "emulate" dcc_start +- // TODO only if "surface" + red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); ++ ++ // only if "surface" ++ if (channel->width == 0 || channel->height == 0) { ++ return; ++ } ++ + // pass proper data + red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); + // surface data +@@ -407,8 +412,8 @@ static void + stream_channel_init(StreamChannel *channel) + { + channel->stream_id = -1; +- channel->width = 1024; +- channel->height = 768; ++ channel->width = 0; ++ channel->height = 0; + } + + void +@@ -421,11 +426,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) + + // send new create surface if required + if (channel->width != fmt->width || channel->height != fmt->height) { ++ if (channel->width != 0 && channel->height != 0) { ++ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); ++ } + channel->width = fmt->width; + channel->height = fmt->height; +- red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); + red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); + // TODO monitors config ?? ++ red_channel_pipes_add_empty_msg(red_channel, SPICE_MSG_DISPLAY_MARK); + } + + // allocate a new stream id diff --git a/SOURCES/0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch b/SOURCES/0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch new file mode 100644 index 0000000..adb8442 --- /dev/null +++ b/SOURCES/0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch @@ -0,0 +1,67 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Tue, 24 Jan 2017 11:36:27 +0000 +Subject: [spice-server] char-device: Do not stop and clear interface on reset + +Currently, red_char_device_reset() stops the device, clears all pending +messages, and clears its device instance. After this function is called, +the char device will not work again until it is assigned a new device +instance and restarted. This is fine for the vdagent char device, which +is currently the only user of this function. But for the stream device, +we want to be able to reset the char device to a working state (e.g. +clear all pending messages, etc) without stopping or disabling the char +device. So this function will now only reset the char device to a clean +working state, and the _stop() and _reset_dev_instance() calls will be +moved up to the caller. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/char-device.c | 2 -- + server/reds.c | 6 +++++- + 2 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/server/char-device.c b/server/char-device.c +index 658f9f364..f8a098bd8 100644 +--- a/server/char-device.c ++++ b/server/char-device.c +@@ -823,7 +823,6 @@ void red_char_device_reset(RedCharDevice *dev) + GList *client_item; + RedCharDeviceWriteBuffer *buf; + +- red_char_device_stop(dev); + dev->priv->wait_for_migrate_data = FALSE; + spice_debug("char device %p", dev); + while ((buf = g_queue_pop_tail(&dev->priv->write_queue))) { +@@ -845,7 +844,6 @@ void red_char_device_reset(RedCharDevice *dev) + dev_client->num_client_tokens += dev_client->num_client_tokens_free; + dev_client->num_client_tokens_free = 0; + } +- red_char_device_reset_dev_instance(dev, NULL); + } + + void red_char_device_wakeup(RedCharDevice *dev) +diff --git a/server/reds.c b/server/reds.c +index b24f61ab2..401d242fb 100644 +--- a/server/reds.c ++++ b/server/reds.c +@@ -470,6 +470,7 @@ static void reds_reset_vdp(RedsState *reds) + { + RedCharDeviceVDIPort *dev = reds->agent_dev; + SpiceCharDeviceInterface *sif; ++ RedCharDevice *char_dev; + + dev->priv->read_state = VDI_PORT_READ_STATE_READ_HEADER; + dev->priv->receive_pos = (uint8_t *)&dev->priv->vdi_chunk_header; +@@ -502,7 +503,10 @@ static void reds_reset_vdp(RedsState *reds) + * The tokens are also reset to avoid mismatch in upon agent reconnection. + */ + dev->priv->agent_attached = FALSE; +- red_char_device_reset(RED_CHAR_DEVICE(dev)); ++ char_dev = RED_CHAR_DEVICE(dev); ++ red_char_device_stop(char_dev); ++ red_char_device_reset(char_dev); ++ red_char_device_reset_dev_instance(char_dev, NULL); + + sif = spice_char_device_get_interface(reds->vdagent); + if (sif->state) { diff --git a/SOURCES/0017-stream-device-Start-supporting-resetting-device-when.patch b/SOURCES/0017-stream-device-Start-supporting-resetting-device-when.patch new file mode 100644 index 0000000..b26eb53 --- /dev/null +++ b/SOURCES/0017-stream-device-Start-supporting-resetting-device-when.patch @@ -0,0 +1,166 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Wed, 25 Jan 2017 22:42:00 +0000 +Subject: [spice-server] stream-device: Start supporting resetting device when + close/open on guest + +When guest close the device the host device has to be reset too. +This make easier to restart the guest device which can happen in case +of reboot, agent issues or if we want to update the agent. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 34 ++++++++++++++++++++++++++++++++++ + server/stream-channel.h | 7 ++++++- + server/stream-device.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 89 insertions(+), 1 deletion(-) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index e89563b82..51b8badf9 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -483,3 +483,37 @@ stream_channel_register_start_cb(StreamChannel *channel, + channel->start_cb = cb; + channel->start_opaque = opaque; + } ++ ++void ++stream_channel_reset(StreamChannel *channel) ++{ ++ struct { ++ StreamMsgStartStop base; ++ uint8_t codecs_buffer[MAX_SUPPORTED_CODECS]; ++ } start_msg; ++ StreamMsgStartStop *const start = &start_msg.base; ++ RedChannel *red_channel = RED_CHANNEL(channel); ++ ++ // send destroy old stream ++ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY); ++ ++ // destroy display surface ++ if (channel->width != 0 && channel->height != 0) { ++ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); ++ } ++ ++ channel->stream_id = -1; ++ channel->width = 0; ++ channel->height = 0; ++ ++ if (!red_channel_is_connected(red_channel)) { ++ return; ++ } ++ ++ // try to request a new stream, this should start a new stream ++ // if the guest is connected to the device and a client is already connected ++ start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs); ++ // send in any case, even if list is not changed ++ // notify device about changes ++ request_new_stream(channel, start); ++} +diff --git a/server/stream-channel.h b/server/stream-channel.h +index ba098df49..bd075a951 100644 +--- a/server/stream-channel.h ++++ b/server/stream-channel.h +@@ -48,7 +48,12 @@ GType stream_channel_get_type(void) G_GNUC_CONST; + */ + StreamChannel* stream_channel_new(RedsState *server, uint32_t id); + +-struct StreamMsgFormat; ++/** ++ * Reset channel at initial state ++ */ ++void stream_channel_reset(StreamChannel *channel); ++ ++struct StreamMsgStreamFormat; + struct StreamMsgStartStop; + + void stream_channel_change_format(StreamChannel *channel, +diff --git a/server/stream-device.c b/server/stream-device.c +index 6e78b1a99..9e401f8ed 100644 +--- a/server/stream-device.c ++++ b/server/stream-device.c +@@ -43,6 +43,7 @@ struct StreamDevice { + StreamDevHeader hdr; + uint8_t hdr_pos; + bool has_error; ++ bool opened; + StreamChannel *stream_channel; + }; + +@@ -203,6 +204,35 @@ stream_device_remove_client(RedCharDevice *self, RedClient *client) + { + } + ++static void ++stream_device_stream_start(void *opaque, StreamMsgStartStop *start, ++ StreamChannel *stream_channel G_GNUC_UNUSED) ++{ ++ StreamDevice *dev = (StreamDevice *) opaque; ++ ++ if (!dev->opened) { ++ return; ++ } ++ ++ int msg_size = sizeof(*start) + sizeof(start->codecs[0]) * start->num_codecs; ++ int total_size = sizeof(StreamDevHeader) + msg_size; ++ ++ RedCharDevice *char_dev = RED_CHAR_DEVICE(dev); ++ RedCharDeviceWriteBuffer *buf = ++ red_char_device_write_buffer_get_server_no_token(char_dev, total_size); ++ buf->buf_used = total_size; ++ ++ StreamDevHeader *hdr = (StreamDevHeader *)buf->buf; ++ hdr->protocol_version = STREAM_DEVICE_PROTOCOL; ++ hdr->padding = 0; ++ hdr->type = GUINT16_TO_LE(STREAM_TYPE_START_STOP); ++ hdr->size = GUINT32_TO_LE(msg_size); ++ ++ memcpy(&hdr[1], start, msg_size); ++ ++ red_char_device_write_buffer_add(char_dev, buf); ++} ++ + RedCharDevice * + stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) + { +@@ -212,6 +242,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) + + StreamDevice *dev = stream_device_new(sin, reds); + dev->stream_channel = stream_channel; ++ stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); + + sif = spice_char_device_get_interface(sin); + if (sif->state) { +@@ -234,6 +265,23 @@ stream_device_dispose(GObject *object) + } + + static void ++stream_device_port_event(RedCharDevice *char_dev, uint8_t event) ++{ ++ if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) { ++ return; ++ } ++ ++ StreamDevice *dev = STREAM_DEVICE(char_dev); ++ ++ // reset device and channel on close/open ++ dev->opened = (event == SPICE_PORT_EVENT_OPENED); ++ dev->hdr_pos = 0; ++ dev->has_error = false; ++ red_char_device_reset(char_dev); ++ stream_channel_reset(dev->stream_channel); ++} ++ ++static void + stream_device_class_init(StreamDeviceClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS(klass); +@@ -245,6 +293,7 @@ stream_device_class_init(StreamDeviceClass *klass) + char_dev_class->send_msg_to_client = stream_device_send_msg_to_client; + char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client; + char_dev_class->remove_client = stream_device_remove_client; ++ char_dev_class->port_event = stream_device_port_event; + } + + static void diff --git a/SOURCES/0018-stream-device-Create-channel-when-needed.patch b/SOURCES/0018-stream-device-Create-channel-when-needed.patch new file mode 100644 index 0000000..5d9cec6 --- /dev/null +++ b/SOURCES/0018-stream-device-Create-channel-when-needed.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Fri, 25 Aug 2017 00:02:54 +0100 +Subject: [spice-server] stream-device: Create channel when needed + +This allows a better id allocation as devices are created after +fixed ones. +Also will allow to support more easily multiple monitor. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-device.c | 38 ++++++++++++++++++++++++++++++++------ + 1 file changed, 32 insertions(+), 6 deletions(-) + +diff --git a/server/stream-device.c b/server/stream-device.c +index 9e401f8ed..ae108788b 100644 +--- a/server/stream-device.c ++++ b/server/stream-device.c +@@ -72,7 +72,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si + int n; + bool handled = false; + +- if (dev->has_error) { ++ if (dev->has_error || !dev->stream_channel) { + return NULL; + } + +@@ -238,11 +238,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) + { + SpiceCharDeviceInterface *sif; + +- StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id +- + StreamDevice *dev = stream_device_new(sin, reds); +- dev->stream_channel = stream_channel; +- stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); + + sif = spice_char_device_get_interface(sin); + if (sif->state) { +@@ -265,6 +261,33 @@ stream_device_dispose(GObject *object) + } + + static void ++allocate_channels(StreamDevice *dev) ++{ ++ if (dev->stream_channel) { ++ return; ++ } ++ ++ SpiceServer* reds = red_char_device_get_server(RED_CHAR_DEVICE(dev)); ++ ++ int id = reds_get_free_channel_id(reds, SPICE_CHANNEL_DISPLAY); ++ g_return_if_fail(id >= 0); ++ ++ StreamChannel *stream_channel = stream_channel_new(reds, id); ++ ++ dev->stream_channel = stream_channel; ++ ++ stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); ++} ++ ++static void ++reset_channels(StreamDevice *dev) ++{ ++ if (dev->stream_channel) { ++ stream_channel_reset(dev->stream_channel); ++ } ++} ++ ++static void + stream_device_port_event(RedCharDevice *char_dev, uint8_t event) + { + if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) { +@@ -275,10 +298,13 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event) + + // reset device and channel on close/open + dev->opened = (event == SPICE_PORT_EVENT_OPENED); ++ if (dev->opened) { ++ allocate_channels(dev); ++ } + dev->hdr_pos = 0; + dev->has_error = false; + red_char_device_reset(char_dev); +- stream_channel_reset(dev->stream_channel); ++ reset_channels(dev); + } + + static void diff --git a/SOURCES/0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch b/SOURCES/0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch new file mode 100644 index 0000000..a242052 --- /dev/null +++ b/SOURCES/0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch @@ -0,0 +1,206 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Wed, 19 Apr 2017 16:24:54 +0100 +Subject: [spice-server] stream-device: Limit sending queue from guest to + server + +Do not allow the guest to fill host memory. +Also having a huge queue mainly cause to have a higher video +latency. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++- + server/stream-channel.h | 10 ++++++++++ + server/stream-device.c | 35 ++++++++++++++++++++++++++++++++++- + 3 files changed, 84 insertions(+), 2 deletions(-) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index 51b8badf9..ec4bf021d 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -71,9 +71,15 @@ struct StreamChannel { + /* size of the current video stream */ + unsigned width, height; + ++ StreamQueueStat queue_stat; ++ + /* callback to notify when a stream should be started or stopped */ + stream_channel_start_proc start_cb; + void *start_opaque; ++ ++ /* callback to notify when queue statistics changes */ ++ stream_channel_queue_stat_proc queue_cb; ++ void *queue_opaque; + }; + + struct StreamChannelClass { +@@ -98,6 +104,7 @@ typedef struct StreamCreateItem { + + typedef struct StreamDataItem { + RedPipeItem base; ++ StreamChannel *channel; + // NOTE: this must be the last field in the structure + SpiceMsgDisplayStreamData data; + } StreamDataItem; +@@ -454,6 +461,27 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) + red_channel_pipes_add(red_channel, &item->base); + } + ++static inline void ++stream_channel_update_queue_stat(StreamChannel *channel, ++ int32_t num_diff, int32_t size_diff) ++{ ++ channel->queue_stat.num_items += num_diff; ++ channel->queue_stat.size += size_diff; ++ if (channel->queue_cb) { ++ channel->queue_cb(channel->queue_opaque, &channel->queue_stat, channel); ++ } ++} ++ ++static void ++data_item_free(RedPipeItem *base) ++{ ++ StreamDataItem *pipe_item = SPICE_UPCAST(StreamDataItem, base); ++ ++ stream_channel_update_queue_stat(pipe_item->channel, -1, -pipe_item->data.data_size); ++ ++ g_free(pipe_item); ++} ++ + void + stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time) + { +@@ -467,10 +495,13 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, + RedChannel *red_channel = RED_CHANNEL(channel); + + StreamDataItem *item = g_malloc(sizeof(*item) + size); +- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA); ++ red_pipe_item_init_full(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA, ++ data_item_free); + item->data.base.id = channel->stream_id; + item->data.base.multi_media_time = mm_time; + item->data.data_size = size; ++ item->channel = channel; ++ stream_channel_update_queue_stat(channel, 1, size); + // TODO try to optimize avoiding the copy + memcpy(item->data.data, data, size); + red_channel_pipes_add(red_channel, &item->base); +@@ -485,6 +516,14 @@ stream_channel_register_start_cb(StreamChannel *channel, + } + + void ++stream_channel_register_queue_stat_cb(StreamChannel *channel, ++ stream_channel_queue_stat_proc cb, void *opaque) ++{ ++ channel->queue_cb = cb; ++ channel->queue_opaque = opaque; ++} ++ ++void + stream_channel_reset(StreamChannel *channel) + { + struct { +diff --git a/server/stream-channel.h b/server/stream-channel.h +index bd075a951..f961d7157 100644 +--- a/server/stream-channel.h ++++ b/server/stream-channel.h +@@ -67,6 +67,16 @@ typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartSto + void stream_channel_register_start_cb(StreamChannel *channel, + stream_channel_start_proc cb, void *opaque); + ++typedef struct StreamQueueStat { ++ uint32_t num_items; ++ uint32_t size; ++} StreamQueueStat; ++ ++typedef void (*stream_channel_queue_stat_proc)(void *opaque, const StreamQueueStat *stats, ++ StreamChannel *channel); ++void stream_channel_register_queue_stat_cb(StreamChannel *channel, ++ stream_channel_queue_stat_proc cb, void *opaque); ++ + G_END_DECLS + + #endif /* STREAM_CHANNEL_H_ */ +diff --git a/server/stream-device.c b/server/stream-device.c +index ae108788b..f87538d49 100644 +--- a/server/stream-device.c ++++ b/server/stream-device.c +@@ -44,6 +44,7 @@ struct StreamDevice { + uint8_t hdr_pos; + bool has_error; + bool opened; ++ bool flow_stopped; + StreamChannel *stream_channel; + }; + +@@ -72,7 +73,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si + int n; + bool handled = false; + +- if (dev->has_error || !dev->stream_channel) { ++ if (dev->has_error || dev->flow_stopped || !dev->stream_channel) { + return NULL; + } + +@@ -181,6 +182,9 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin) + if (n <= 0) { + break; + } ++ // TODO collect all message ?? ++ // up: we send a single frame together ++ // down: guest can cause a crash + stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time()); + dev->hdr.size -= n; + } +@@ -233,6 +237,33 @@ stream_device_stream_start(void *opaque, StreamMsgStartStop *start, + red_char_device_write_buffer_add(char_dev, buf); + } + ++static void ++stream_device_stream_queue_stat(void *opaque, const StreamQueueStat *stats G_GNUC_UNUSED, ++ StreamChannel *stream_channel G_GNUC_UNUSED) ++{ ++ StreamDevice *dev = (StreamDevice *) opaque; ++ ++ if (!dev->opened) { ++ return; ++ } ++ ++ // very easy control flow... if any data stop ++ // this seems a very small queue but as we use tcp ++ // there's already that queue ++ if (stats->num_items) { ++ dev->flow_stopped = true; ++ return; ++ } ++ ++ if (dev->flow_stopped) { ++ dev->flow_stopped = false; ++ // TODO resume flow... ++ // avoid recursion if we need to call get data from data handling from ++ // data handling ++ red_char_device_wakeup(&dev->parent); ++ } ++} ++ + RedCharDevice * + stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) + { +@@ -277,6 +308,7 @@ allocate_channels(StreamDevice *dev) + dev->stream_channel = stream_channel; + + stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); ++ stream_channel_register_queue_stat_cb(stream_channel, stream_device_stream_queue_stat, dev); + } + + static void +@@ -303,6 +335,7 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event) + } + dev->hdr_pos = 0; + dev->has_error = false; ++ dev->flow_stopped = false; + red_char_device_reset(char_dev); + reset_channels(dev); + } diff --git a/SOURCES/0020-stream-channel-Activate-streaming-report-from-client.patch b/SOURCES/0020-stream-channel-Activate-streaming-report-from-client.patch new file mode 100644 index 0000000..859488f --- /dev/null +++ b/SOURCES/0020-stream-channel-Activate-streaming-report-from-client.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Wed, 7 Jun 2017 13:13:21 +0100 +Subject: [spice-server] stream-channel: Activate streaming report from client + +Setting the capability is not enough, each stream must be enabled +so do so if client support them. + +Signed-off-by: Frediano Ziglio +Acked-by: Jonathon Jongsma +--- + server/stream-channel.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/server/stream-channel.c b/server/stream-channel.c +index ec4bf021d..7e15dd363 100644 +--- a/server/stream-channel.c ++++ b/server/stream-channel.c +@@ -27,6 +27,7 @@ + #include "reds.h" + #include "common-graphics-channel.h" + #include "display-limits.h" ++#include "stream.h" // TODO remove, put common stuff + + #define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type() + +@@ -95,6 +96,7 @@ enum { + RED_PIPE_ITEM_TYPE_STREAM_CREATE, + RED_PIPE_ITEM_TYPE_STREAM_DATA, + RED_PIPE_ITEM_TYPE_STREAM_DESTROY, ++ RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT, + }; + + typedef struct StreamCreateItem { +@@ -230,6 +232,20 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) + spice_marshall_msg_display_stream_create(m, &item->stream_create); + break; + } ++ case RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: { ++ if (client->stream_id < 0 ++ || !red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_STREAM_REPORT)) { ++ return; ++ } ++ SpiceMsgDisplayStreamActivateReport msg; ++ msg.stream_id = client->stream_id; ++ msg.unique_id = 1; // TODO useful ? ++ msg.max_window_size = RED_STREAM_CLIENT_REPORT_WINDOW; ++ msg.timeout_ms = RED_STREAM_CLIENT_REPORT_TIMEOUT; ++ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT); ++ spice_marshall_msg_display_stream_activate_report(m, &msg); ++ break; ++ } + case RED_PIPE_ITEM_TYPE_STREAM_DATA: { + StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item); + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA); +@@ -459,6 +475,9 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) + item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height }; + item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; + red_channel_pipes_add(red_channel, &item->base); ++ ++ // activate stream report if possible ++ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT); + } + + static inline void diff --git a/SPECS/spice.spec b/SPECS/spice.spec index 1a5835d..2838eb9 100644 --- a/SPECS/spice.spec +++ b/SPECS/spice.spec @@ -1,27 +1,50 @@ Name: spice Version: 0.14.0 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Implements the SPICE protocol Group: User Interface/Desktops License: LGPLv2+ URL: http://www.spice-space.org/ Source0: http://www.spice-space.org/download/releases/%{name}-%{version}.tar.bz2 -Source1: http://www.spice-space.org/download/releases/%{name}-%{version}.tar.bz2.sign -Source2: cfergeau-29AC6C82.keyring +Patch1: 0001-inputs-channel-Check-message-size-handling-migration.patch +Patch2: 0002-red-channel-Remove-red_channel_init_outgoing_message.patch +Patch3: 0003-reds-Remove-leak-allocating-migration-state.patch +Patch4: 0004-tests-Check-leaks-registering-migration-interface.patch +Patch5: 0005-Notify-client-of-the-creation-of-new-channels-dynami.patch +Patch6: 0006-stream-device-Add-device-to-handle-streaming.patch +Patch7: 0007-stream-device-Start-parsing-new-protocol-from-guest.patch +Patch8: 0008-stream-channel-Write-a-base-channel-to-implement-the.patch +Patch9: 0009-stream-channel-Start-implementing-DisplayChannel-pro.patch +Patch10: 0010-stream-device-Create-channel-for-stream-device.patch +Patch11: 0011-stream-device-Handle-streaming-data-from-device-to-c.patch +Patch12: 0012-stream-channel-Allows-not-fixed-size.patch +Patch13: 0013-stream-channel-Allows-to-register-callback-to-get-ne.patch +Patch14: 0014-stream-channel-Support-client-connection-disconnecti.patch +Patch15: 0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch +Patch16: 0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch +Patch17: 0017-stream-device-Start-supporting-resetting-device-when.patch +Patch18: 0018-stream-device-Create-channel-when-needed.patch +Patch19: 0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch +Patch20: 0020-stream-channel-Activate-streaming-report-from-client.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=613529 +%if 0%{?rhel} +ExclusiveArch: x86_64 +%else +ExclusiveArch: i686 x86_64 armv6l armv7l armv7hl +%endif + BuildRequires: pkgconfig BuildRequires: glib2-devel >= 2.22 -BuildRequires: spice-protocol >= 0.12.3 +BuildRequires: spice-protocol >= 0.12.10 BuildRequires: celt051-devel -BuildRequires: opus-devel -BuildRequires: pixman-devel openssl-devel libjpeg-devel +BuildRequires: pixman-devel alsa-lib-devel openssl-devel libjpeg-turbo-devel BuildRequires: libcacard-devel cyrus-sasl-devel BuildRequires: lz4-devel -BuildRequires: gstreamer1-devel gstreamer1-plugins-base-devel -BuildRequires: orc-devel BuildRequires: pyparsing -BuildRequires: python-six -BuildRequires: gnupg2 -BuildRequires: git-core +BuildRequires: opus-devel +BuildRequires: git +BuildRequires: autoconf automake libtool %description The Simple Protocol for Independent Computing Environments (SPICE) is @@ -52,6 +75,7 @@ Summary: Header files, libraries and development documentation for spice- Group: Development/Libraries Requires: %{name}-server%{?_isa} = %{version}-%{release} Requires: pkgconfig +Requires: spice-protocol >= 0.12.3 %description server-devel This package contains the header files, static libraries and development @@ -60,13 +84,12 @@ using spice-server, you will need to install spice-server-devel. %prep -gpgv2 --quiet --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0} %autosetup -S git_am %build -%define configure_client --disable-client -%configure --enable-smartcard --disable-client --enable-lz4 --enable-gstreamer=1.0 +autoreconf -fi +%configure --enable-smartcard --disable-client make %{?_smp_mflags} WARN_CFLAGS='' V=1 @@ -82,9 +105,7 @@ mkdir -p %{buildroot}%{_libexecdir} %files server -%{!?_licensedir:%global license %%doc} -%license COPYING -%doc README NEWS +%doc COPYING README NEWS %{_libdir}/libspice-server.so.1* %files server-devel @@ -94,87 +115,165 @@ mkdir -p %{buildroot}%{_libexecdir} %changelog -* Wed Oct 11 2017 Christophe Fergeau - 0.14.0-1 -- Update to new stable release - -* Tue Sep 26 2017 Christophe Fergeau - 0.13.91-1 -- Update to latest upstream release +* Thu Oct 12 2017 Christophe Fergeau - 0.14.0-2 +- Add streaming patches for use with spice-streaming-agent + Related: rhbz#1478356 -* Thu Aug 24 2017 Christophe Fergeau - 0.13.90-3 -- Add missing (new) BuildRequires, remove obsolete one +* Wed Oct 11 2017 Christophe Fergeau - 0.14.0-1 +- Rebase to 0.14.0 release + Resolves: rhbz#1472948 -* Thu Aug 03 2017 Fedora Release Engineering - 0.13.90-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild +* Fri Sep 22 2017 Christophe Fergeau 0.13.90-2 +- Add lz4-devel BuildRequires + Resolves: rhbz#1460191 * Wed Jul 26 2017 Christophe Fergeau 0.13.90-1 -- Update to latest upstream release (0.13.90) - -* Mon Feb 06 2017 Christophe Fergeau 0.13.3-2 -- Add upstream patches fixing CVE-2016-9577 and CVE-2016-9578 - -* Mon Nov 21 2016 Christophe Fergeau 0.13.3-1 -- Update to spice 0.13.3 - -* Fri Aug 05 2016 Christophe Fergeau - 0.13.2-1 -- Update to spice 0.13.2 - -* Tue Jun 14 2016 Peter Robinson 0.13.1-2 -- Use %%license -- Build on aarch64 - -* Wed Apr 13 2016 Christophe Fergeau 0.13.1-1 -- Update to 0.13.1 release. This is a development release, but by the - time Fedora 25 gets released, a stable 0.14.0 should be released. - -* Fri Feb 05 2016 Fedora Release Engineering - 0.12.6-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild - -* Tue Oct 06 2015 Christophe Fergeau 0.12.6-1 -- Update to new 0.12.6 upstream release - -* Wed Jul 29 2015 Christophe Fergeau 0.12.5-9 -- Drop patch added in previous build which is no longer needed with - spice-protocol 0.12.9 (and actually is actually breaking QEMU compilation - without an additional patch) - -* Fri Jul 03 2015 Christophe Fergeau 0.12.5-8 -- Add upstream patch avoiding a regression in spice-protocol 0.12.8 which - breaks SPICE support in QEMU - -* Thu Jul 02 2015 Christophe Fergeau 0.12.5-7 -- Fix migration race condition which causes a crash when triggered - Resolves: rhbz#1238212 - -* Fri Jun 19 2015 Fedora Release Engineering - 0.12.5-6 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild - -* Mon Aug 25 2014 Christophe Fergeau 0.12.5-5 -- Fix advertised sound playback/recording rates in public headers - Resolves: rhbz#1129961 (QEMU would need a rebuild though) - -* Mon Aug 18 2014 Fedora Release Engineering - 0.12.5-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild - -* Sun Jun 08 2014 Fedora Release Engineering - 0.12.5-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild - -* Mon May 19 2014 Christophe Fergeau 0.12.5-2 -- Add missing BuildRequires in order to enable Opus support - -* Mon May 19 2014 Christophe Fergeau 0.12.5-1 -- Update to new 0.12.5 release - -* Wed Oct 30 2013 Christophe Fergeau 0.12.4-3 -- Add patch fixing CVE-2013-4282 +- Rebase to latest upstream release + Resolves: rhbz#1472948 + +* Fri Jul 14 2017 Jonathon Jongsma - 0.12.8-4 +- build with opus support + Resolves: rhbz#1456832 + +* Fri Jun 30 2017 Christophe Fergeau 0.12.8-3 +- Prevent potential buffer/integer overflows with invalid MonitorsConfig messages + sent from an authenticated client + Resolves: CVE-2017-7506 + +* Tue Apr 25 2017 Christophe Fergeau 0.12.8-2 +- Drop clients immediatly if the magic they send is wrong + Resolves: rhbz#1416692 + +* Mon Jan 16 2017 Christophe Fergeau 0.12.8-1 +- Rebase to spice-server 0.12.8 + Resolves: rhbz#1388947 + Resolves: rhbz#1377551 + Resolves: rhbz#1283202 +* Fri Dec 09 2016 Frediano Ziglio - 0.12.4-20 +- Fix buffer overflow in main_channel_alloc_msg_rcv_buf when reading large + messages. + Resolves: CVE-2016-9577 +- Fix remote DoS via crafted message. + Resolves: CVE-2016-9578 +* Fri Sep 09 2016 Christophe Fergeau 0.12.4-19 +- Ensure SPICE_MIGRATE_COMPLETED is sent in all cases when it's needed. + Resolves: rhbz#1352836 +* Fri Jul 01 2016 Christophe Fergeau - 0.12.4-18 +- Fix crash when connecting to VM using smartcard passthrough + Resolves: rhbz#1340899 +- Fix hang after unredirecting a USB device + Resolves: rhbz#1338752 +- Backport spice_qxl_set_max_monitors() + Resolves: rhbz#1283202 +* Wed Apr 27 2016 Christophe Fergeau - 0.12.4-17 +- Fix crash when the client sends a wrong header (for example when using spice-html5) + Resolves: rhbz#1281442 +- Fix crash when guest provides wrong address + Resolves: rhbz#1264356 +- Fix thread-safety issue causing a crash when playing a Youtube video spanning + multiple monitors + Resolves: rhbz#1253375 +- Add patches reducing QEMU wake-ups + Related: rhbz#912763, rhbz#1186146 +- Fix use-after-free after resetting a VM + Resolves: rhbz#1281455 +- Send KeepAlive probes every 10 minutes + Resolves: rhbz#1298590 +- Add client to guest volume synchronization + Resolves: rhbz#1264107 + +* Mon Apr 25 2016 Christophe Fergeau - 0.12.4-16 +- Use autosetup + Related: CVE-2016-0749 +- Fix heap-based memory corruption within smartcard handling + Resolves: CVE-2016-0749 +- Fix host memory access from guest with invalid primary surface parameters + Resolves: CVE-2016-2150 + +* Wed Sep 23 2015 Frediano Ziglio 0.12.4-15 +- CVE-2015-5260 CVE-2015-5261 fixed various security flaws + Resolves: rhbz#1267134 + +* Thu Sep 10 2015 Frediano Ziglio 0.12.4-14 +- Validate surface_id + Resolves: rhbz#1260971 + +* Tue Jul 21 2015 Frediano Ziglio 0.12.4-13 +- Clean stale statistics file before creating a new one + Resolves: rhbz#1177326 + +* Fri Jul 10 2015 Fabiano FidĂȘncio 0.12.4-12 +- Fix a backport issue on Patch0040. + Related: rhbz#1071176 + Resolves: rhbz#1241860 + +* Thu Jul 09 2015 Fabiano FidĂȘncio 0.12.4-11 +- Don't assert on invalid client message + Resolves: rhbz#1227410 +- Don't truncate large 'now' values in _spice_timer_set + Resolves: rhbz#1227408 +- Avoid race conditions reading monitor configs from guest + Resolves: rhbz#1239128 +- Lock the pixmap image cache for the entire fill_bits call + Resolves: rhbz#1235443 + +* Wed Jul 08 2015 Fabiano FidĂȘncio 0.12.4-10 +- Fix qemu segmentation fault (core dumped) when boot KVM guest with + spice in FIPS enabled mode. + Resolves: rhbz#1071176 + +* Mon Jan 05 2015 Marc-Andre Lureau 0.12.4-9 +- Allow recent TLS/SSL methods, block SSLv2/SSLv3. Resolves: rhbz#1175540 + +* Tue Oct 21 2014 Christophe Fergeau 0.12.4-8 +- Fix defects reported by Coverity + Resolves: rhbz#885717 +- Validate surface bounding box sent from QXL driver + Resolves: rhbz#1052856 +- Fix assertion sometimes happening during migration while a client is + connected + Resolves: rhbz#1035184 +- Fix crash when restarting VM with old client + Resolves: rhbz#1145919 + +* Thu Sep 18 2014 Christophe Fergeau 0.12.4-7 +- Fix assert in mjpeg_encoder_adjust_params_to_bit_rate() + Resolves: rhbz#1086823 +- Fix "Spice-ERROR **: reds.c:1464:reds_send_link_ack: assertion + `link->link_mess->channel_type == SPICE_CHANNEL_MAIN' failed" assertion + Resolves: rhbz#1058625 +- Lower a monitor-config warning to debug level + Resolves: rhbz#1119220 +- mjpeg: Don't warn on unsupported image formats + Resolves: rhbz#1070028 + +* Thu Aug 07 2014 Marc-Andre Lureau 0.12.4-6 +- Fix invalid surface clearing + Resolves: rhbz#1029646 + +* Wed Jan 29 2014 Christophe Fergeau 0.12.4-5 +- Fix qemu crash during migration with reboot + Resolves: rhbz#1016795 +- Monitor whether the client is alive + Resolves: rhbz#1016790 + +* Tue Oct 15 2013 Christophe Fergeau 0.12.4-3 +- Fix spice-server crash when client sends a password which is too long + Resolves: CVE-2013-4282 * Fri Sep 13 2013 Christophe Fergeau 0.12.4-2 - Add upstream patch fixing rhbz#995041 * Fri Aug 2 2013 Hans de Goede - 0.12.4-1 -- New upstream bug-fix release 0.12.4 - Add patches from upstream git to fix sound-channel-free crash (rhbz#986407) - Add Obsoletes for dropped spice-client sub-package +* Mon Jul 22 2013 Yonit Halperin 0.12.4-1 +- New upstream release 0.12.4 +- Require libjpeg-turbo-devel instead of libjpeg-devel +- Remove "BuildRequires: spice-protocol" from spice-server +- Add "Requires: spice-protocol" to spice-server-devel. + * Thu May 23 2013 Christophe Fergeau 0.12.3-2 - Stop building spicec, it's obsolete and superseded by remote-viewer (part of virt-viewer)