From 063737b0ebd1e3aa01ce98532eda3eeadce298c6 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Oct 23 2019 01:35:17 +0000 Subject: import spice-0.14.0-7.el7 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e95719 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/spice-0.14.0.tar.bz2 diff --git a/.spice.metadata b/.spice.metadata new file mode 100644 index 0000000..f0a186a --- /dev/null +++ b/.spice.metadata @@ -0,0 +1 @@ +93e42588d1aac0a3c127ada1e5d8f40be84776a9 SOURCES/spice-0.14.0.tar.bz2 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/SOURCES/0021-reds-Disable-TLS-1.0.patch b/SOURCES/0021-reds-Disable-TLS-1.0.patch new file mode 100644 index 0000000..6b338dd --- /dev/null +++ b/SOURCES/0021-reds-Disable-TLS-1.0.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Sun, 11 Feb 2018 18:27:41 +0000 +Subject: [spice-server] reds: Disable TLS 1.0 + +TLS 1.0 is considered now insecure. +TLS 1.1 was introduced in 2006. +Our SPICE clients uses OpenSSL to use TLS and the support for TLS 1.1 +in OpenSSL was introduced in 2006 too so even in systems like +Windows XP which are not officially supporting TLS 1.0 will work +with SPICE and TLS 1.1. +This fixes https://bugzilla.redhat.com/show_bug.cgi?id=1521053. + +Signed-off-by: Frediano Ziglio +Acked-by: Victor Toso +--- + server/reds.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/server/reds.c b/server/reds.c +index 401d242..0af5643 100644 +--- a/server/reds.c ++++ b/server/reds.c +@@ -2836,9 +2836,10 @@ static int reds_init_ssl(RedsState *reds) + SSL_METHOD *ssl_method; + #endif + int return_code; +- /* When some other SSL/TLS version becomes obsolete, add it to this ++ /* Limit connection to TLSv1.1 or newer. ++ * When some other SSL/TLS version becomes obsolete, add it to this + * variable. */ +- long ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; ++ long ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; + + /* Global system initialization*/ + g_once(&openssl_once, openssl_global_init, NULL); diff --git a/SOURCES/0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch b/SOURCES/0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch new file mode 100644 index 0000000..f6d4e69 --- /dev/null +++ b/SOURCES/0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch @@ -0,0 +1,71 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christophe Fergeau +Date: Tue, 10 Apr 2018 17:32:48 +0200 +Subject: [spice-server] cursor: Delay release of QXL guest cursor resources + +There's an implicit API/ABI contract between QEMU and SPICE that SPICE +will keep the guest QXL resources alive as long as QEMU can hold a +pointer to them. This implicit contract was broken in 1c6e7cf7 "Release +cursor as soon as possible", causing crashes at migration time. +While the proper fix would be in QEMU so that spice-server does not need +to have that kind of knowledge regarding QEMU internal implementation, +this commit reverts to the pre-1c6e7cf7 behaviour to avoid a regression +while QEMU is being fixed. + +This version of the fix is based on a suggestion from Frediano Ziglio. + +https://bugzilla.redhat.com/show_bug.cgi?id=1540919 + +Signed-off-by: Christophe Fergeau +Acked-by: Frediano Ziglio +--- + server/red-parse-qxl.c | 4 ++++ + server/red-parse-qxl.h | 1 + + server/red-worker.c | 2 +- + 3 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/server/red-parse-qxl.c b/server/red-parse-qxl.c +index 33f3692..c436214 100644 +--- a/server/red-parse-qxl.c ++++ b/server/red-parse-qxl.c +@@ -24,6 +24,7 @@ + #include + #include "spice-bitmap-utils.h" + #include "red-common.h" ++#include "red-qxl.h" + #include "memslot.h" + #include "red-parse-qxl.h" + +@@ -1497,4 +1498,7 @@ void red_put_cursor_cmd(RedCursorCmd *red) + red_put_cursor(&red->u.set.shape); + break; + } ++ if (red->qxl) { ++ red_qxl_release_resource(red->qxl, red->release_info_ext); ++ } + } +diff --git a/server/red-parse-qxl.h b/server/red-parse-qxl.h +index 4a576ca..f0407b5 100644 +--- a/server/red-parse-qxl.h ++++ b/server/red-parse-qxl.h +@@ -99,6 +99,7 @@ typedef struct RedSurfaceCmd { + } RedSurfaceCmd; + + typedef struct RedCursorCmd { ++ QXLInstance *qxl; + QXLReleaseInfoExt release_info_ext; + uint8_t type; + union { +diff --git a/server/red-worker.c b/server/red-worker.c +index 8a63fde..ccf5df9 100644 +--- a/server/red-worker.c ++++ b/server/red-worker.c +@@ -112,7 +112,7 @@ static gboolean red_process_cursor_cmd(RedWorker *worker, const QXLCommandExt *e + free(cursor_cmd); + return FALSE; + } +- red_qxl_release_resource(worker->qxl, cursor_cmd->release_info_ext); ++ cursor_cmd->qxl = worker->qxl; + cursor_channel_process_cmd(worker->cursor_channel, cursor_cmd); + return TRUE; + } diff --git a/SOURCES/0023-sound-Don-t-mute-recording-when-client-reconnects.patch b/SOURCES/0023-sound-Don-t-mute-recording-when-client-reconnects.patch new file mode 100644 index 0000000..7afa627 --- /dev/null +++ b/SOURCES/0023-sound-Don-t-mute-recording-when-client-reconnects.patch @@ -0,0 +1,225 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christophe Fergeau +Date: Fri, 25 May 2018 11:12:40 +0200 +Subject: [spice-server] sound: Don't mute recording when client reconnects + +When a new record channel is added, the code relies on a snd_send() call +in record_channel_client_constructed() to send RECORD_START to the +client. However, at this point, snd_send() is non-functional because +the red_channel_client_pipe_add() call it makes is a no-op because +prepare_pipe_add() makes a connection check through +red_channel_client_is_connected() queueing the item. This connection +check returns FALSE at ::constructed() time as the channel client will +only become connected towards the end of +red_channel_client_initable_init() which runs after the object +instantiation is complete. + +This commit solves this issue by making PlaybackChannelClient and +RecordChannelClient implement GInitable, and move the code interacting +with the client in their _initable_init() function, as at this point the +objects will be able to send data. + +This causes a bug where starting recording and then +disconnecting/reconnecting the client does not successfully reenable +recording. This is a regression introduced by commit d8dc09 +'sound: Convert SndChannelClient to RedChannelClient' + +https://bugzilla.redhat.com/show_bug.cgi?id=1549132 + +Signed-off-by: Christophe Fergeau +Acked-by: Victor Toso +--- + server/sound.c | 120 ++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 83 insertions(+), 37 deletions(-) + +diff --git a/server/sound.c b/server/sound.c +index 9073626..8c6cf8a 100644 +--- a/server/sound.c ++++ b/server/sound.c +@@ -103,6 +103,11 @@ typedef struct SndChannelClientClass { + RedChannelClientClass parent_class; + } SndChannelClientClass; + ++static void playback_channel_client_initable_interface_init(GInitableIface *iface); ++static void record_channel_client_initable_interface_init(GInitableIface *iface); ++static GInitableIface *playback_channel_client_parent_initable_iface; ++static GInitableIface *record_channel_client_parent_initable_iface; ++ + G_DEFINE_TYPE(SndChannelClient, snd_channel_client, RED_TYPE_CHANNEL_CLIENT) + + +@@ -149,7 +154,9 @@ typedef struct PlaybackChannelClientClass { + SndChannelClientClass parent_class; + } PlaybackChannelClientClass; + +-G_DEFINE_TYPE(PlaybackChannelClient, playback_channel_client, TYPE_SND_CHANNEL_CLIENT) ++G_DEFINE_TYPE_WITH_CODE(PlaybackChannelClient, playback_channel_client, TYPE_SND_CHANNEL_CLIENT, ++ G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, playback_channel_client_initable_interface_init)) ++ + + + typedef struct SpiceVolumeState { +@@ -207,6 +214,7 @@ typedef struct RecordChannelClass { + } RecordChannelClass; + + G_DEFINE_TYPE(RecordChannel, record_channel, TYPE_SND_CHANNEL) ++ + + + #define TYPE_RECORD_CHANNEL_CLIENT record_channel_client_get_type() +@@ -230,7 +238,8 @@ typedef struct RecordChannelClientClass { + SndChannelClientClass parent_class; + } RecordChannelClientClass; + +-G_DEFINE_TYPE(RecordChannelClient, record_channel_client, TYPE_SND_CHANNEL_CLIENT) ++G_DEFINE_TYPE_WITH_CODE(RecordChannelClient, record_channel_client, TYPE_SND_CHANNEL_CLIENT, ++ G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, record_channel_client_initable_interface_init)) + + + /* A list of all Spice{Playback,Record}State objects */ +@@ -1044,7 +1053,6 @@ playback_channel_client_constructed(GObject *object) + RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client); + RedChannel *red_channel = red_channel_client_get_channel(rcc); + SndChannel *channel = SND_CHANNEL(red_channel); +- RedClient *red_client = red_channel_client_get_client(rcc); + SndChannelClient *scc = SND_CHANNEL_CLIENT(playback_client); + + G_OBJECT_CLASS(playback_channel_client_parent_class)->constructed(object); +@@ -1070,18 +1078,6 @@ playback_channel_client_constructed(GObject *object) + + spice_debug("playback client %p using mode %s", playback_client, + spice_audio_data_mode_to_string(playback_client->mode)); +- +- if (!red_client_during_migrate_at_target(red_client)) { +- snd_set_command(scc, SND_PLAYBACK_MODE_MASK); +- if (channel->volume.volume_nchannels) { +- snd_set_command(scc, SND_VOLUME_MUTE_MASK); +- } +- } +- +- if (channel->active) { +- playback_channel_client_start(scc); +- } +- snd_send(scc); + } + + static void snd_set_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream, +@@ -1250,27 +1246,6 @@ record_channel_client_finalize(GObject *object) + G_OBJECT_CLASS(record_channel_client_parent_class)->finalize(object); + } + +-static void +-record_channel_client_constructed(GObject *object) +-{ +- RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(object); +- RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(record_client)); +- SndChannel *channel = SND_CHANNEL(red_channel); +- SndChannelClient *scc = SND_CHANNEL_CLIENT(record_client); +- +- G_OBJECT_CLASS(record_channel_client_parent_class)->constructed(object); +- +- if (channel->volume.volume_nchannels) { +- snd_set_command(scc, SND_VOLUME_MUTE_MASK); +- } +- +- if (channel->active) { +- record_channel_client_start(scc); +- } +- snd_send(scc); +-} +- +- + static void snd_set_record_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream, + G_GNUC_UNUSED int migration, + RedChannelCapabilities *caps) +@@ -1478,6 +1453,43 @@ snd_channel_client_init(SndChannelClient *self) + { + } + ++static gboolean playback_channel_client_initable_init(GInitable *initable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ gboolean success; ++ RedClient *red_client = red_channel_client_get_client(RED_CHANNEL_CLIENT(initable)); ++ SndChannelClient *scc = SND_CHANNEL_CLIENT(initable); ++ RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(initable)); ++ SndChannel *channel = SND_CHANNEL(red_channel); ++ ++ success = playback_channel_client_parent_initable_iface->init(initable, cancellable, error); ++ if (!success) { ++ return FALSE; ++ } ++ ++ if (!red_client_during_migrate_at_target(red_client)) { ++ snd_set_command(scc, SND_PLAYBACK_MODE_MASK); ++ if (channel->volume.volume_nchannels) { ++ snd_set_command(scc, SND_VOLUME_MUTE_MASK); ++ } ++ } ++ ++ if (channel->active) { ++ playback_channel_client_start(scc); ++ } ++ snd_send(SND_CHANNEL_CLIENT(initable)); ++ ++ return TRUE; ++} ++ ++static void playback_channel_client_initable_interface_init(GInitableIface *iface) ++{ ++ playback_channel_client_parent_initable_iface = g_type_interface_peek_parent (iface); ++ ++ iface->init = playback_channel_client_initable_init; ++} ++ + static void + playback_channel_client_class_init(PlaybackChannelClientClass *klass) + { +@@ -1505,11 +1517,45 @@ playback_channel_client_init(PlaybackChannelClient *playback) + snd_playback_alloc_frames(playback); + } + ++static gboolean record_channel_client_initable_init(GInitable *initable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ gboolean success; ++ RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(initable); ++ RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(record_client)); ++ SndChannel *channel = SND_CHANNEL(red_channel); ++ SndChannelClient *scc = SND_CHANNEL_CLIENT(record_client); ++ ++ success = record_channel_client_parent_initable_iface->init(initable, cancellable, error); ++ if (!success) { ++ return FALSE; ++ } ++ ++ if (channel->volume.volume_nchannels) { ++ snd_set_command(scc, SND_VOLUME_MUTE_MASK); ++ } ++ ++ if (channel->active) { ++ record_channel_client_start(scc); ++ } ++ snd_send(SND_CHANNEL_CLIENT(initable)); ++ ++ return TRUE; ++} ++ ++static void record_channel_client_initable_interface_init(GInitableIface *iface) ++{ ++ record_channel_client_parent_initable_iface = g_type_interface_peek_parent (iface); ++ ++ iface->init = record_channel_client_initable_init; ++} ++ ++ + static void + record_channel_client_class_init(RecordChannelClientClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS(klass); +- object_class->constructed = record_channel_client_constructed; + object_class->finalize = record_channel_client_finalize; + } + diff --git a/SOURCES/0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch b/SOURCES/0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch new file mode 100644 index 0000000..72ad808 --- /dev/null +++ b/SOURCES/0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch @@ -0,0 +1,190 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christophe Fergeau +Date: Mon, 18 Jun 2018 12:39:37 +0200 +Subject: [PATCH] tls: Parse spice.cnf OpenSSL configuration file + +SPICE tries to use the OpenSSL system-wide defaults as much as possible +for the TLS ciphers and protocols it uses. However, this is not enough +for some customers who want it to use a more restrictive set of TLS +features. spice-server should not try to override the system defaults +OpenSSL uses, so this is not going to be hardcoded in spice-server code. + +This is addressed with crypto policies in recent fedoras, and is being +solved upstream through https://github.com/openssl/openssl/pull/4848 +This issue has become pressing enough that we need to solve it in el7 +which unfortunately does not have any of these system-wide settings. + +As a stop-gap measure, this downstream-only patch adds a +/etc/pki/tls/spice.cnf configuration file which can be used to configure +the TLS ciphers/protocols used for SPICE. This is only meant to be a +temporary solution, and will be superseded by crypto-policies when they +land in RHEL. + +Signed-off-by: Christophe Fergeau +--- + docs/Makefile.am | 1 + + docs/spice.cnf.sample | 15 +++++++ + server/reds.c | 102 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 118 insertions(+) + create mode 100644 docs/spice.cnf.sample + +diff --git a/docs/Makefile.am b/docs/Makefile.am +index 45667a6..909ed15 100644 +--- a/docs/Makefile.am ++++ b/docs/Makefile.am +@@ -6,6 +6,7 @@ EXTRA_DIST = \ + spice_style.txt \ + spice_threading_model.html \ + spice_threading_model.txt \ ++ spice.cnf.sample \ + $(NULL) + + if BUILD_MANUAL +diff --git a/docs/spice.cnf.sample b/docs/spice.cnf.sample +new file mode 100644 +index 0000000..e5404ae +--- /dev/null ++++ b/docs/spice.cnf.sample +@@ -0,0 +1,15 @@ ++# SPICE OpenSSL configuration file ++# ++# Sample configuration file for SPICE TLS communication ++# Edit the file according to your needs, and put it in ++# /etc/pki/tls/spice.cnf ++# ++# See "SUPPORTED CONFIGURATION FILE COMMANDS" in SSL_CONF_cmd(3) ++# for the valid options, as well a ciphers(1) for the format ++# of CipherString ++ ++# Configure available ciphers ++CipherString = DEFAULT:-RC4:-3DES:-DES ++ ++# Only enable TLSv1.2 (and newer TLS versions the day OpenSSL supports them) ++Protocol = ALL,-SSLv2,-SSLv3,-TLSv1,-TLSv1.1 +diff --git a/server/reds.c b/server/reds.c +index 0af5643..846e44d 100644 +--- a/server/reds.c ++++ b/server/reds.c +@@ -33,6 +33,7 @@ + #include + #include + ++#include + #include + + #if HAVE_SASL +@@ -2827,6 +2828,102 @@ static gpointer openssl_global_init(gpointer arg) + return NULL; + } + ++#define SPICE_OPENSSL_CNF_FILENAME "/etc/pki/tls/spice.cnf" ++ ++static int reds_ssl_config_file_apply(RedsState *reds, STACK_OF(CONF_VALUE) *sect) ++{ ++ int openssl_status; ++ int return_value = 0; ++ SSL_CONF_CTX *cctx = NULL; ++ unsigned int i; ++ ++ cctx = SSL_CONF_CTX_new(); ++ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); ++ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); ++ SSL_CONF_CTX_set_ssl_ctx(cctx, reds->ctx); ++ ++ for (i = 0; i < sk_CONF_VALUE_num(sect); i++) { ++ CONF_VALUE *option_value; ++ option_value = sk_CONF_VALUE_value(sect, i); ++ g_message("setting TLS option '%s' to '%s' from %s configuration file", ++ option_value->name, option_value->value, ++ SPICE_OPENSSL_CNF_FILENAME); ++ openssl_status = SSL_CONF_cmd(cctx, option_value->name, option_value->value); ++ switch(openssl_status) { ++ case 1: /* fallthrough */ ++ case 2: ++ /* The option was successfully processed */ ++ break; ++ case 0: ++ g_warning("failure to set option '%s'", option_value->name); ++ return_value = -1; ++ break; ++ case -2: ++ g_warning("unknown option '%s'", option_value->name); ++ return_value = -1; ++ break; ++ case -3: ++ g_warning("missing value for option '%s'", option_value->name); ++ return_value = -1; ++ break; ++ default: ++ g_warning("unknown SSL_CONF_cmd return value: %d", openssl_status); ++ return_value = -1; ++ break; ++ } ++ } ++ ++ openssl_status = SSL_CONF_CTX_finish(cctx); ++ if (!openssl_status) { ++ g_warning("SSL_CONF_CTX_finish() failed"); ++ return_value = -1; ++ } ++ ++ SSL_CONF_CTX_free(cctx); ++ ++ return return_value; ++} ++ ++static int reds_ssl_config_file_try_load(RedsState *reds) ++{ ++ int status = -1; ++ int openssl_status; ++ CONF *ssl_conf = NULL; ++ STACK_OF(CONF_VALUE) *default_section; ++ long error_line = -1; ++ ++ if (!g_file_test(SPICE_OPENSSL_CNF_FILENAME, G_FILE_TEST_IS_REGULAR)) { ++ /* The configuration file is not mandatory, it's only meant to be used ++ * when the sysadmin does not want to use the system-wide OpenSSL defaults ++ */ ++ return 0; ++ } ++ ++ ssl_conf = NCONF_new(NULL); ++ openssl_status = NCONF_load(ssl_conf, SPICE_OPENSSL_CNF_FILENAME, &error_line); ++ if (openssl_status <= 0) { ++ if (error_line <= 0) { ++ g_warning("error loading config file %s", SPICE_OPENSSL_CNF_FILENAME); ++ } else { ++ g_warning("error parsing config file %s at %ld", SPICE_OPENSSL_CNF_FILENAME, error_line); ++ } ++ goto end; ++ } ++ ++ default_section = NCONF_get_section(ssl_conf, "default"); ++ if (default_section == NULL) { ++ g_warning("could not find any content in %s config file (no toplevel section?)", SPICE_OPENSSL_CNF_FILENAME); ++ goto end; ++ } ++ ++ status = reds_ssl_config_file_apply(reds, default_section); ++ ++end: ++ NCONF_free(ssl_conf); ++ ++ return status; ++} ++ + static int reds_init_ssl(RedsState *reds) + { + static GOnce openssl_once = G_ONCE_INIT; +@@ -2911,6 +3008,11 @@ static int reds_init_ssl(RedsState *reds) + sk_zero(cmp_stack); + #endif + ++ /* must be last to override whatever was configured previously */ ++ if (reds_ssl_config_file_try_load(reds) != 0) { ++ return -1; ++ } ++ + return 0; + } + diff --git a/SOURCES/0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch b/SOURCES/0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch new file mode 100644 index 0000000..16ad761 --- /dev/null +++ b/SOURCES/0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christophe Fergeau +Date: Wed, 20 Jun 2018 17:02:14 +0200 +Subject: [spice-server] ssl: Allow to use ECDH ciphers with OpenSSL 1.0 + +Without an explicit call to SSL_CTX_set_ecdh_auto(reds->ctx, 1), OpenSSL +1.0 (still used by el7) would not use ECDH ciphers (this is now +automatic with OpenSSL 1.1.0). This commit adds this missing call. It's +based on a suggestion from David Jasa + +Signed-off-by: Christophe Fergeau +Acked-by: Frediano Ziglio + +https://bugzilla.redhat.com/show_bug.cgi?id=1566597 +--- + server/reds.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/server/reds.c b/server/reds.c +index 846e44d..a7b9c38 100644 +--- a/server/reds.c ++++ b/server/reds.c +@@ -2955,6 +2955,7 @@ static int reds_init_ssl(RedsState *reds) + ssl_options |= SSL_OP_NO_COMPRESSION; + #endif + SSL_CTX_set_options(reds->ctx, ssl_options); ++ SSL_CTX_set_ecdh_auto(reds->ctx, 1); + + /* Load our keys and certificates*/ + return_code = SSL_CTX_use_certificate_chain_file(reds->ctx, reds->config->ssl_parameters.certs_file); diff --git a/SOURCES/0026-Fix-flexible-array-buffer-overflow.patch b/SOURCES/0026-Fix-flexible-array-buffer-overflow.patch new file mode 100644 index 0000000..6c5eaec --- /dev/null +++ b/SOURCES/0026-Fix-flexible-array-buffer-overflow.patch @@ -0,0 +1,301 @@ +From c182f8e4a445e93842faf6c2bd8583894da36a1a Mon Sep 17 00:00:00 2001 +From: Frediano Ziglio +Date: Fri, 18 May 2018 11:41:57 +0100 +Subject: [PATCH] Fix flexible array buffer overflow + +This is kind of a DoS, possibly flexible array in the protocol +causes the network size check to be ignored due to integer overflows. + +The size of flexible array is computed as (message_end - position), +then this size is added to the number of bytes before the array and +this number is used to check if we overflow initial message. + +An example is: + + message { + uint32 dummy[2]; + uint8 data[] @end; + } LenMessage; + +which generated this (simplified remove useless code) code: + + { /* data */ + data__nelements = message_end - (start + 8); + + data__nw_size = data__nelements; + } + + nw_size = 8 + data__nw_size; + + /* Check if message fits in reported side */ + if (nw_size > (uintptr_t) (message_end - start)) { + return NULL; + } + +Following code: +- data__nelements == message_end - (start + 8) +- data__nw_size == data__nelements == message_end - (start + 8) +- nw_size == 8 + data__nw_size == 8 + message_end - (start + 8) == + 8 + message_end - start - 8 == message_end -start +- the check for overflow is (nw_size > (message_end - start)) but + nw_size == message_end - start so the check is doing + ((message_end - start) > (message_end - start)) which is always false. + +If message_end - start < 8 then data__nelements (number of element +on the array above) computation generate an integer underflow that +later create a buffer overflow. + +Add a check to make sure that the array starts before the message ends +to avoid the overflow. + +Difference is: + diff -u save/generated_client_demarshallers1.c common/generated_client_demarshallers1.c + --- save/generated_client_demarshallers1.c 2018-06-22 22:13:48.626793919 +0100 + +++ common/generated_client_demarshallers1.c 2018-06-22 22:14:03.408163291 +0100 + @@ -225,6 +225,9 @@ + uint64_t data__nelements; + + { /* data */ + + if (SPICE_UNLIKELY((start + 0) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 0); + + data__nw_size = data__nelements; + @@ -243,6 +246,9 @@ + *free_message = nofree; + return data; + + + error: + + free(data); + + return NULL; + } + + static uint8_t * parse_msg_set_ack(uint8_t *message_start, uint8_t *message_end, SPICE_GNUC_UNUSED int minor, size_t *size, message_destructor_t *free_message) + @@ -301,6 +307,9 @@ + SpiceMsgPing *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 12) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 12); + + data__nw_size = data__nelements; + @@ -5226,6 +5235,9 @@ + uint64_t cursor_data__nw_size; + uint64_t cursor_data__nelements; + { /* data */ + + if (SPICE_UNLIKELY((start2 + 22) > message_end)) { + + goto error; + + } + cursor_data__nelements = message_end - (start2 + 22); + + cursor_data__nw_size = cursor_data__nelements; + @@ -5305,6 +5317,9 @@ + uint64_t cursor_data__nw_size; + uint64_t cursor_data__nelements; + { /* data */ + + if (SPICE_UNLIKELY((start2 + 22) > message_end)) { + + goto error; + + } + cursor_data__nelements = message_end - (start2 + 22); + + cursor_data__nw_size = cursor_data__nelements; + @@ -5540,6 +5555,9 @@ + SpiceMsgPlaybackPacket *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 4) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 4); + + data__nw_size = data__nelements; + @@ -5594,6 +5612,9 @@ + SpiceMsgPlaybackMode *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 8) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 8); + + data__nw_size = data__nelements; + diff -u save/generated_client_demarshallers.c common/generated_client_demarshallers.c + --- save/generated_client_demarshallers.c 2018-06-22 22:13:48.626793919 +0100 + +++ common/generated_client_demarshallers.c 2018-06-22 22:14:03.004153195 +0100 + @@ -225,6 +225,9 @@ + uint64_t data__nelements; + + { /* data */ + + if (SPICE_UNLIKELY((start + 0) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 0); + + data__nw_size = data__nelements; + @@ -243,6 +246,9 @@ + *free_message = nofree; + return data; + + + error: + + free(data); + + return NULL; + } + + static uint8_t * parse_msg_set_ack(uint8_t *message_start, uint8_t *message_end, SPICE_GNUC_UNUSED int minor, size_t *size, message_destructor_t *free_message) + @@ -301,6 +307,9 @@ + SpiceMsgPing *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 12) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 12); + + data__nw_size = data__nelements; + @@ -6574,6 +6583,9 @@ + } + + { /* data */ + + if (SPICE_UNLIKELY((start2 + 2 + cursor_u__nw_size) > message_end)) { + + goto error; + + } + cursor_data__nelements = message_end - (start2 + 2 + cursor_u__nw_size); + + cursor_data__nw_size = cursor_data__nelements; + @@ -6670,6 +6682,9 @@ + } + + { /* data */ + + if (SPICE_UNLIKELY((start2 + 2 + cursor_u__nw_size) > message_end)) { + + goto error; + + } + cursor_data__nelements = message_end - (start2 + 2 + cursor_u__nw_size); + + cursor_data__nw_size = cursor_data__nelements; + @@ -6907,6 +6922,9 @@ + SpiceMsgPlaybackPacket *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 4) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 4); + + data__nw_size = data__nelements; + @@ -6961,6 +6979,9 @@ + SpiceMsgPlaybackMode *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 6) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 6); + + data__nw_size = data__nelements; + @@ -7559,6 +7580,9 @@ + SpiceMsgTunnelSocketData *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 2) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 2); + + data__nw_size = data__nelements; + @@ -7840,6 +7864,9 @@ + } + + { /* compressed_data */ + + if (SPICE_UNLIKELY((start + 1 + u__nw_size) > message_end)) { + + goto error; + + } + compressed_data__nelements = message_end - (start + 1 + u__nw_size); + + compressed_data__nw_size = compressed_data__nelements; + diff -u save/generated_server_demarshallers.c common/generated_server_demarshallers.c + --- save/generated_server_demarshallers.c 2018-06-22 22:13:48.627793944 +0100 + +++ common/generated_server_demarshallers.c 2018-06-22 22:14:05.231208847 +0100 + @@ -306,6 +306,9 @@ + uint64_t data__nelements; + + { /* data */ + + if (SPICE_UNLIKELY((start + 0) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 0); + + data__nw_size = data__nelements; + @@ -324,6 +327,9 @@ + *free_message = nofree; + return data; + + + error: + + free(data); + + return NULL; + } + + static uint8_t * parse_msgc_disconnecting(uint8_t *message_start, uint8_t *message_end, SPICE_GNUC_UNUSED int minor, size_t *size, message_destructor_t *free_message) + @@ -1259,6 +1265,9 @@ + SpiceMsgcRecordPacket *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 4) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 4); + + data__nw_size = data__nelements; + @@ -1313,6 +1322,9 @@ + SpiceMsgcRecordMode *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 6) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 6); + + data__nw_size = data__nelements; + @@ -1841,6 +1853,9 @@ + SpiceMsgcTunnelSocketData *out; + + { /* data */ + + if (SPICE_UNLIKELY((start + 2) > message_end)) { + + goto error; + + } + data__nelements = message_end - (start + 2); + + data__nw_size = data__nelements; + @@ -2057,6 +2072,9 @@ + } + + { /* compressed_data */ + + if (SPICE_UNLIKELY((start + 1 + u__nw_size) > message_end)) { + + goto error; + + } + compressed_data__nelements = message_end - (start + 1 + u__nw_size); + + compressed_data__nw_size = compressed_data__nelements; + +Signed-off-by: Frediano Ziglio +--- + spice-common/python_modules/demarshal.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/spice-common/python_modules/demarshal.py b/spice-common/python_modules/demarshal.py +index 1ea131d..7172762 100644 +--- a/spice-common/python_modules/demarshal.py ++++ b/spice-common/python_modules/demarshal.py +@@ -318,6 +318,7 @@ def write_validate_array_item(writer, container, item, scope, parent_scope, star + writer.assign(nelements, array.size) + elif array.is_remaining_length(): + if element_type.is_fixed_nw_size(): ++ writer.error_check("%s > message_end" % item.get_position()) + if element_type.get_fixed_nw_size() == 1: + writer.assign(nelements, "message_end - %s" % item.get_position()) + else: +-- +2.17.1 + diff --git a/SOURCES/0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch b/SOURCES/0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch new file mode 100644 index 0000000..f64c2cb --- /dev/null +++ b/SOURCES/0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christophe Fergeau +Date: Fri, 20 Jul 2018 11:44:51 +0200 +Subject: [PATCH] dcc: Fix QUIC fallback in get_compression_for_bitmap() + +There was a small regression introduced in get_compression_for_bitmap() +by f401eb07f dcc: Rewrite dcc_image_compress. +If SPICE_IMAGE_COMPRESSION_AUTO_GLZ is specified, and the bitmap has a +stride which is bigger than its width (ie it has padding), then +get_compression_for_bitmap() will return SPICE_IMAGE_COMPRESSION_OFF +while in that case, we used to use QUIC for compression. + +This happens because that function in the AUTO_GLZ case first checks if +QUIC should be used, if not, it decides to use GLZ, but then decides it +can't because of the stride, so falls back to OFF, while it used to +fall back to QUIC. + +This commit only slightly reworks a preexisting if (!can_lz_compress()) +check so that it's unconditional rather than depending on the previous +checks having been unsuccessful. + +This issue could be observed by using a spice-html5 without support for +uncompressed bitmaps with end-of-line padding by simply starting a f28 +VM and connecting to it/moving the mouse cursor in it. + +Signed-off-by: Christophe Fergeau +Acked-by: Frediano Ziglio +--- + server/dcc.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/server/dcc.c b/server/dcc.c +index 3bf75a7..b632905 100644 +--- a/server/dcc.c ++++ b/server/dcc.c +@@ -806,8 +806,10 @@ static SpiceImageCompression get_compression_for_bitmap(SpiceBitmap *bitmap, + bitmap_get_graduality_level(bitmap) == BITMAP_GRADUAL_HIGH) { + return SPICE_IMAGE_COMPRESSION_QUIC; + } +- } else if (!can_lz_compress(bitmap) || +- drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH) { ++ } else if (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH) { ++ return SPICE_IMAGE_COMPRESSION_QUIC; ++ } ++ if (!can_lz_compress(bitmap)) { + return SPICE_IMAGE_COMPRESSION_QUIC; + } + } diff --git a/SOURCES/0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.patch b/SOURCES/0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.patch new file mode 100644 index 0000000..ad8a9aa --- /dev/null +++ b/SOURCES/0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.patch @@ -0,0 +1,100 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christophe Fergeau +Date: Thu, 29 Nov 2018 14:18:39 +0100 +Subject: [PATCH] memslot: Fix off-by-one error in group/slot boundary check + +RedMemSlotInfo keeps an array of groups, and each group contains an +array of slots. Unfortunately, these checks are off by 1, they check +that the index is greater or equal to the number of elements in the +array, while these arrays are 0 based. The check should only check for +strictly greater than the number of elements. + +For the group array, this is not a big issue, as these memslot groups +are created by spice-server users (eg QEMU), and the group ids used to +index that array are also generated by the spice-server user, so it +should not be possible for the guest to set them to arbitrary values. + +The slot id is more problematic, as it's calculated from a QXLPHYSICAL +address, and such addresses are usually set by the guest QXL driver, so +the guest can set these to arbitrary values, including malicious values, +which are probably easy to build from the guest PCI configuration. + +This patch fixes the arrays bound check, and adds a test case for this. + +Signed-off-by: Christophe Fergeau +--- + server/memslot.c | 4 ++-- + server/tests/test-qxl-parsing.c | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 34 insertions(+), 2 deletions(-) + +diff --git a/server/memslot.c b/server/memslot.c +index 7074b43..8c59c38 100644 +--- a/server/memslot.c ++++ b/server/memslot.c +@@ -99,14 +99,14 @@ unsigned long memslot_get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t + MemSlot *slot; + + *error = 0; +- if (group_id > info->num_memslots_groups) { ++ if (group_id >= info->num_memslots_groups) { + spice_critical("group_id too big"); + *error = 1; + return 0; + } + + slot_id = memslot_get_id(info, addr); +- if (slot_id > info->num_memslots) { ++ if (slot_id >= info->num_memslots) { + print_memslots(info); + spice_critical("slot_id %d too big, addr=%" PRIx64, slot_id, addr); + *error = 1; +diff --git a/server/tests/test-qxl-parsing.c b/server/tests/test-qxl-parsing.c +index 9c0c3b1..83f2083 100644 +--- a/server/tests/test-qxl-parsing.c ++++ b/server/tests/test-qxl-parsing.c +@@ -85,6 +85,33 @@ static void deinit_qxl_surface(QXLSurfaceCmd *qxl) + free(from_physical(qxl->u.surface_create.data)); + } + ++static void test_memslot_invalid_group_id(void) ++{ ++ RedMemSlotInfo mem_info; ++ int error; ++ init_meminfo(&mem_info); ++ ++ memslot_get_virt(&mem_info, 0, 16, 1, &error); ++} ++ ++static void test_memslot_invalid_slot_id(void) ++{ ++ RedMemSlotInfo mem_info; ++ int error; ++ init_meminfo(&mem_info); ++ ++ memslot_get_virt(&mem_info, 1 << mem_info.memslot_id_shift, 16, 0, &error); ++} ++ ++static void test_memslot_invalid_addresses(void) ++{ ++ g_test_trap_subprocess("/server/memslot-invalid-addresses/subprocess/group_id", 0, 0); ++ g_test_trap_assert_stderr("*group_id too big*"); ++ ++ g_test_trap_subprocess("/server/memslot-invalid-addresses/subprocess/slot_id", 0, 0); ++ g_test_trap_assert_stderr("*slot_id 1 too big*"); ++} ++ + static void test_no_issues(void) + { + RedMemSlotInfo mem_info; +@@ -262,6 +289,11 @@ int main(int argc, char *argv[]) + { + g_test_init(&argc, &argv, NULL); + ++ /* try to use invalid memslot group/slot */ ++ g_test_add_func("/server/memslot-invalid-addresses", test_memslot_invalid_addresses); ++ g_test_add_func("/server/memslot-invalid-addresses/subprocess/group_id", test_memslot_invalid_group_id); ++ g_test_add_func("/server/memslot-invalid-addresses/subprocess/slot_id", test_memslot_invalid_slot_id); ++ + /* try to create a surface with no issues, should succeed */ + g_test_add_func("/server/qxl-parsing-no-issues", test_no_issues); + diff --git a/SPECS/spice.spec b/SPECS/spice.spec new file mode 100644 index 0000000..e75c578 --- /dev/null +++ b/SPECS/spice.spec @@ -0,0 +1,468 @@ +Name: spice +Version: 0.14.0 +Release: 7%{?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 +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 +Patch21: 0021-reds-Disable-TLS-1.0.patch +Patch22: 0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch +Patch23: 0023-sound-Don-t-mute-recording-when-client-reconnects.patch +Patch24: 0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch +Patch25: 0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch +Patch26: 0026-Fix-flexible-array-buffer-overflow.patch +Patch27: 0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch +Patch28: 0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.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.10 +BuildRequires: celt051-devel +BuildRequires: pixman-devel alsa-lib-devel openssl-devel libjpeg-turbo-devel +BuildRequires: libcacard-devel cyrus-sasl-devel +BuildRequires: lz4-devel +BuildRequires: pyparsing python-six +BuildRequires: opus-devel +BuildRequires: git +BuildRequires: autoconf automake libtool + +%description +The Simple Protocol for Independent Computing Environments (SPICE) is +a remote display system built for virtual environments which allows +you to view a computing 'desktop' environment not only on the machine +where it is running, but from anywhere on the Internet and from a wide +variety of machine architectures. + + +%package server +Summary: Implements the server side of the SPICE protocol +Group: System Environment/Libraries +Obsoletes: spice-client < %{version}-%{release} +# Ensure SSL_CONF_CTX_set_ssl_ctx (needed by Patch24) is present +# https://bugzilla.redhat.com/show_bug.cgi?id=1627693 +Requires: openssl-libs >= 1.0.2k-16 + +%description server +The Simple Protocol for Independent Computing Environments (SPICE) is +a remote display system built for virtual environments which allows +you to view a computing 'desktop' environment not only on the machine +where it is running, but from anywhere on the Internet and from a wide +variety of machine architectures. + +This package contains the run-time libraries for any application that wishes +to be a SPICE server. + + +%package server-devel +Summary: Header files, libraries and development documentation for spice-server +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 +documentation for spice-server. If you like to develop programs +using spice-server, you will need to install spice-server-devel. + + +%prep +%autosetup -S git_am + + +%build +autoreconf -fi +%configure --enable-smartcard --disable-client +make %{?_smp_mflags} WARN_CFLAGS='' V=1 + + +%install +make DESTDIR=%{buildroot} install +rm -f %{buildroot}%{_libdir}/libspice-server.a +rm -f %{buildroot}%{_libdir}/libspice-server.la +mkdir -p %{buildroot}%{_libexecdir} + + +%post server -p /sbin/ldconfig +%postun server -p /sbin/ldconfig + + +%files server +%doc COPYING README NEWS docs/spice.cnf.sample +%{_libdir}/libspice-server.so.1* + +%files server-devel +%{_includedir}/spice-server +%{_libdir}/libspice-server.so +%{_libdir}/pkgconfig/spice-server.pc + + +%changelog +* Tue Dec 18 2018 Christophe Fergeau - 0.14.0-7 +- Fix off-by-one error during guest-to-host memory address conversion + Resolves: CVE-2019-3813 +- Add patch for upstream commit 48179332d9da0. This should help with corrupted + spice-html5 displays + Resolves: rhbz#1573739 +- Add missing minimum openssl version Requires for patch #24 + Resolves: rhbz#1627693 + +* Thu Aug 09 2018 Frediano Ziglio - 0.14.0-6 +- Fix flexible array buffer overflow + Resolves: rhbz#1596008 + +* Wed Jun 20 2018 Christophe Fergeau - 0.14.0-5 +- Don't mute Record channel on client reconnection + Resolves: rhbz#1549132 +- Allow to configure TLS protocol versions and ciphers which SPICE will use for + TLS communications + Resolves: rhbz#1562213 +- Enable ECDH ciphers with OpenSSL 1.0 + Resolves: rhbz#1566597 + +* Fri Apr 27 2018 Christophe Fergeau - 0.14.0-4 +- Revert back to spice 0.12 behaviour where QXL guest resources for cursor + commands are only released when the current cursor is replaced. This avoids + a QEMU regression causing crashes during migration + Resolves: rhbz#1567944 + +* Tue Apr 03 2018 Christophe Fergeau - 0.14.0-3 +- Disable TLSv1.0 + Resolves: rhbz#1521053 + +* Thu Oct 12 2017 Christophe Fergeau - 0.14.0-2 +- Add streaming patches for use with spice-streaming-agent + Related: rhbz#1478356 + +* Wed Oct 11 2017 Christophe Fergeau - 0.14.0-1 +- Rebase to 0.14.0 release + Resolves: rhbz#1472948 + +* 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 +- 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 +- 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) + +* Tue May 21 2013 Christophe Fergeau 0.12.3-1 +- New upstream release 0.12.3 +- Drop all patches (they were all upstreamed) + +* Mon Apr 15 2013 Hans de Goede - 0.12.2-4 +- Add fix from upstream for a crash when the guest uses RGBA (rhbz#952242) + +* Thu Mar 07 2013 Adam Jackson 0.12.2-4 +- Rebuild for new libsasl2 soname in F19 + +* Mon Jan 21 2013 Hans de Goede - 0.12.2-3 +- Add a number of misc. bug-fixes from upstream + +* Fri Dec 21 2012 Adam Tkac - 0.12.2-2 +- rebuild against new libjpeg + +* Thu Dec 20 2012 Hans de Goede - 0.12.2-1 +- New upstream release 0.12.2 + +* Fri Sep 28 2012 Hans de Goede - 0.12.0-1 +- New upstream release 0.12.0 +- Some minor spec file cleanups +- Enable building on arm + +* Thu Sep 6 2012 Soren Sandmann - 0.11.3-1 +- BuildRequire pyparsing + +* Thu Sep 6 2012 Soren Sandmann - 0.11.3-1 +- Add capability patches +- Add capability patches to the included copy of spice-protocol + + Please see the comment above Patch6 and Patch7 + regarding this situation. + +* Thu Sep 6 2012 Soren Sandmann - 0.11.3-1 +- Update to 0.11.3 and drop upstreamed patches +- BuildRequire spice-protocol 0.12.1 + +* Sat Jul 21 2012 Fedora Release Engineering - 0.10.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Mon May 14 2012 Alon Levy +- Fix mjpeg memory leak and bad behavior. +- Add usbredir to list of channels for security purposes. (#819484) + +* Sun May 13 2012 Alon Levy +- Add double free fix. (#808936) + +* Tue Apr 24 2012 Alon Levy +- Add 32 bit fixes from git master. (#815717) + +* Tue Feb 28 2012 Fedora Release Engineering - 0.10.1-2 +- Rebuilt for c++ ABI breakage + +* Mon Jan 23 2012 Hans de Goede - 0.10.1-1 +- New upstream release 0.10.1 + +* Sat Jan 14 2012 Fedora Release Engineering - 0.10.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Nov 10 2011 Alon Levy - 0.10.0-1 +- New upstream release 0.10.0 +- support spice-server.i686 + +* Wed Sep 28 2011 Marc-André Lureau - 0.9.1-2 +- Provides spice-xpi-client alternative in spice-client + +* Thu Aug 25 2011 Hans de Goede - 0.9.1-1 +- New upstream release 0.9.1 + +* Mon Jul 25 2011 Marc-André Lureau - 0.9.0-1 +- New upstream release 0.9.0 + +* Wed Apr 20 2011 Hans de Goede - 0.8.1-1 +- New upstream release 0.8.1 + +* Fri Mar 11 2011 Hans de Goede - 0.8.0-2 +- Fix being unable to send ctrl+alt+key when release mouse is bound to + ctrl+alt (which can happen when used from RHEV-M) + +* Tue Mar 1 2011 Hans de Goede - 0.8.0-1 +- New upstream release 0.8.0 + +* Fri Feb 11 2011 Hans de Goede - 0.7.3-1 +- New upstream release 0.7.3 + +* Wed Feb 09 2011 Fedora Release Engineering - 0.7.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Jan 19 2011 Hans de Goede - 0.7.2-1 +- New upstream release 0.7.2 + +* Fri Dec 17 2010 Hans de Goede - 0.7.1-1 +- New upstream release 0.7.1 +- Drop all patches (all upstreamed) +- Enable smartcard (CAC) support + +* Wed Nov 17 2010 Hans de Goede - 0.6.3-4 +- Fix the info layer not showing when used through the XPI +- Do not let the connection gui flash by when a hostname has been specified + on the cmdline +- Fix spice client locking up when dealing with XIM input (#654265) +- Fix modifier keys getting stuck (#655048) +- Fix spice client crashing when dealing with XIM ibus input (#655836) +- Fix spice client only showing a white screen in full screen mode + +* Sat Nov 6 2010 Hans de Goede - 0.6.3-3 +- Log to ~/.spicec/cegui.log rather then to CEGUI.log in the cwd, this + fixes spicec from aborting when run in a non writable dir (#650253) + +* Fri Nov 5 2010 Hans de Goede - 0.6.3-2 +- Various bugfixes from upstream git: + - Make spicec work together with the Firefox XPI for RHEV-M + - Make sure the spicec window gets properly raised when first shown + +* Mon Oct 18 2010 Hans de Goede - 0.6.3-1 +- Update to 0.6.3 +- Enable GUI + +* Thu Sep 30 2010 Gerd Hoffmann - 0.6.1-1 +- Update to 0.6.1. + +* Tue Aug 31 2010 Alexander Larsson - 0.6.0-1 +- Update to 0.6.0 (stable release) + +* Tue Jul 20 2010 Alexander Larsson - 0.5.3-1 +- Update to 0.5.3 + +* Tue Jul 13 2010 Gerd Hoffmann - 0.5.2-4 +- Quote %% in changelog to avoid macro expansion. + +* Mon Jul 12 2010 Gerd Hoffmann - 0.5.2-3 +- %%configure handles CFLAGS automatically, no need to fiddle + with %%{optflags} manually. + +* Mon Jul 12 2010 Gerd Hoffmann - 0.5.2-2 +- Fix license: LGPL. +- Cleanup specfile, drop bits not needed any more with + recent rpm versions (F13+). +- Use optflags as-is. +- + +* Fri Jul 9 2010 Gerd Hoffmann - 0.5.2-1 +- initial package. +