[Spice-commits] 19 commits - client/audio_channels.h client/canvas.cpp client/canvas.h client/display_channel.cpp client/display_channel.h client/playback_channel.cpp client/record_channel.cpp client/red_channel.cpp client/red_channel.h client/red_client.cpp client/red_client.h client/red_gdi_canvas.cpp client/red_gdi_canvas.h client/red_gl_canvas.cpp client/red_gl_canvas.h client/red_sw_canvas.cpp client/red_sw_canvas.h common/messages.h configure.ac NEWS server/inputs_channel.c server/main_channel.c server/main_channel.h server/red_channel.c server/red_channel.h server/reds.c server/reds.h server/red_tunnel_worker.c server/red_worker.c server/smartcard.c server/snd_worker.c server/spice-experimental.h server/spice.h server/spice-server.syms server/spicevmc.c spice.proto
Yonit Halperin
yhalperi at kemper.freedesktop.org
Wed Nov 2 02:36:22 PDT 2011
NEWS | 8
client/audio_channels.h | 12
client/canvas.cpp | 25 +
client/canvas.h | 152 ----------
client/display_channel.cpp | 316 ++++++++++++++-------
client/display_channel.h | 29 --
client/playback_channel.cpp | 22 +
client/record_channel.cpp | 39 +-
client/red_channel.cpp | 80 +++++
client/red_channel.h | 21 +
client/red_client.cpp | 156 +++++++++-
client/red_client.h | 17 +
client/red_gdi_canvas.cpp | 4
client/red_gdi_canvas.h | 2
client/red_gl_canvas.cpp | 4
client/red_gl_canvas.h | 2
client/red_sw_canvas.cpp | 6
client/red_sw_canvas.h | 2
common/messages.h | 2
configure.ac | 4
server/inputs_channel.c | 4
server/main_channel.c | 332 ++++++++++++++--------
server/main_channel.h | 38 +-
server/red_channel.c | 109 ++++++-
server/red_channel.h | 39 ++
server/red_tunnel_worker.c | 3
server/red_worker.c | 3
server/reds.c | 636 +++++++++++++++++++++++++-------------------
server/reds.h | 22 +
server/smartcard.c | 4
server/snd_worker.c | 66 +---
server/spice-experimental.h | 3
server/spice-server.syms | 1
server/spice.h | 27 +
server/spicevmc.c | 4
spice.proto | 9
36 files changed, 1411 insertions(+), 792 deletions(-)
New commits:
commit aefe01c02c452bbc79532cb2011eaf12f8ecf58f
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Oct 9 14:44:14 2011 +0200
Release 0.9.2
diff --git a/NEWS b/NEWS
index 8b56038..a862449 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,11 @@
+Major changes in 0.9.2:
+=======================
+* !Development Release!
+* server: semi-seamless migration support (RHBZ 738266)
+* client: semi-seamless migration support (RHBZ 725009, 738270)
+* Various bugfixes / cleanups
+* require spice-protocol >= 0.9.1
+
Major changes in 0.9.1:
=======================
* !Development Release!
diff --git a/configure.ac b/configure.ac
index 203e82f..afdb926 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ AC_PREREQ([2.57])
m4_define([SPICE_MAJOR], 0)
m4_define([SPICE_MINOR], 9)
-m4_define([SPICE_MICRO], 1)
+m4_define([SPICE_MICRO], 2)
AC_INIT(spice, [SPICE_MAJOR.SPICE_MINOR.SPICE_MICRO], [], spice)
diff --git a/server/spice-server.syms b/server/spice-server.syms
index f1374bd..a8361a2 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -89,5 +89,6 @@ global:
spice_server_record_set_mute;
spice_server_record_set_volume;
spice_server_get_num_clients;
+ spice_server_migrate_connect;
} SPICE_SERVER_0.8.2;
commit c39d842ff16be4303c48b4faa19f22cd7312b3c7
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Tue Sep 20 14:59:36 2011 +0300
client: support semi-seamless migration between spice servers with different protocols.
It can't actually happen right now, since switch-host migration scheme will take
place if the src/target server has protocol 1.
(cherry picked from commit 4b2bf4d88c253502003aa5e4b93a045742eec9b4 branch 0.8)
diff --git a/client/red_channel.cpp b/client/red_channel.cpp
index d85265b..e8c058c 100644
--- a/client/red_channel.cpp
+++ b/client/red_channel.cpp
@@ -298,6 +298,20 @@ bool RedChannelBase::test_capability(uint32_t cap)
return test_capability(_remote_caps, cap);
}
+void RedChannelBase::swap(RedChannelBase* other)
+{
+ int tmp_ver;
+
+ RedPeer::swap(other);
+ tmp_ver = _remote_major;
+ _remote_major = other->_remote_major;
+ other->_remote_major = tmp_ver;
+
+ tmp_ver = _remote_minor;
+ _remote_minor = other->_remote_minor;
+ other->_remote_minor = tmp_ver;
+}
+
SendTrigger::SendTrigger(RedChannel& channel)
: _channel (channel)
{
@@ -493,6 +507,12 @@ void RedChannel::do_migration_disconnect_src()
void RedChannel::do_migration_connect_target()
{
LOG_INFO("");
+ ASSERT(get_client().get_protocol() != 0);
+ if (get_client().get_protocol() == 1) {
+ _marshallers = spice_message_marshallers_get1();
+ } else {
+ _marshallers = spice_message_marshallers_get();
+ }
_loop.add_socket(*this);
_socket_in_loop = true;
on_connect_mig_target();
diff --git a/client/red_channel.h b/client/red_channel.h
index ba78acc..ee5b185 100644
--- a/client/red_channel.h
+++ b/client/red_channel.h
@@ -66,6 +66,8 @@ public:
uint32_t get_peer_major() { return _remote_major;}
uint32_t get_peer_minor() { return _remote_minor;}
+ virtual void swap(RedChannelBase* other);
+
protected:
void set_common_capability(uint32_t cap);
void set_capability(uint32_t cap);
diff --git a/client/red_client.cpp b/client/red_client.cpp
index 4fc442f..2f6c6ce 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -136,6 +136,7 @@ Migrate::Migrate(RedClient& client)
, _connected (false)
, _thread (NULL)
, _pending_con (0)
+ , _protocol (0)
{
}
@@ -192,7 +193,7 @@ void Migrate::swap_peer(RedChannelBase& other)
curr->set_valid(false);
if (!--_pending_con) {
lock.unlock();
- _client.set_target(_host.c_str(), _port, _sport);
+ _client.set_target(_host.c_str(), _port, _sport, _protocol);
abort();
}
return;
@@ -215,6 +216,13 @@ void Migrate::connect_one(MigChannel& channel, const RedPeer::ConnectionOptions&
channel.connect(options, connection_id, _host.c_str(), _password);
++_pending_con;
channel.set_valid(true);
+ if (_protocol == 0) {
+ if (channel.get_peer_major() == 1) {
+ _protocol = 1;
+ } else {
+ _protocol = 2;
+ }
+ }
}
void Migrate::run()
@@ -235,7 +243,7 @@ void Migrate::run()
for (++iter; iter != _channels.end(); ++iter) {
conn_type = _client.get_connection_options((*iter)->get_type());
con_opt = RedPeer::ConnectionOptions(conn_type, _port, _sport,
- _client.get_protocol(),
+ _protocol,
_auth_options, _con_ciphers);
connect_one(**iter, con_opt, connection_id);
}
@@ -445,11 +453,16 @@ RedClient::~RedClient()
delete[] _agent_caps;
}
-void RedClient::set_target(const std::string& host, int port, int sport)
+void RedClient::set_target(const std::string& host, int port, int sport, int protocol)
{
+ if (protocol != get_protocol()) {
+ LOG_INFO("old protocol %d, new protocol %d", get_protocol(), protocol);
+ }
+
_port = port;
_sport = sport;
_host.assign(host);
+ set_protocol(protocol);
}
void RedClient::push_event(Event* event)
@@ -684,10 +697,8 @@ void RedClient::on_channel_disconnect_mig_src_completed(RedChannel& channel)
_pixmap_cache.clear();
_glz_window.clear();
memset(_sync_info, 0, sizeof(_sync_info));
-
LOG_INFO("calling main to connect and wait for handle_init to tell all the other channels to connect");
RedChannel::connect_migration_target();
-
AutoRef<MigrateEndEvent> mig_end_event(new MigrateEndEvent());
get_process_loop().push_event(*mig_end_event);
}
diff --git a/client/red_client.h b/client/red_client.h
index 20d5ffb..f4cd46b 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -82,6 +82,7 @@ private:
Mutex _lock;
Condition _cond;
int _pending_con;
+ int _protocol;
};
class ChannelFactory {
@@ -238,7 +239,7 @@ public:
void activate_interval_timer(Timer* timer, unsigned int millisec);
void deactivate_interval_timer(Timer* timer);
- void set_target(const std::string& host, int port, int sport);
+ void set_target(const std::string& host, int port, int sport, int protocol = 0);
void set_password(const std::string& password) { _password = password;}
void set_auto_display_res(bool auto_display_res) { _auto_display_res = auto_display_res;}
void set_display_setting(DisplaySetting& setting) { _display_setting = setting;}
commit 3a1473760c0bb5da719e39ebe5cce02f5325904c
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Mon Sep 19 09:19:40 2011 +0300
client: display channel - destroy all surfaces on disconnect
Fix not destroying surfaces and other data (e.g., streams) upon disconnection.
(cherry picked from commit 010b22cd771b7e81363b4b6521e4265b093fcd25 branch 0.8)
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index f7fdbbc..2e0613c 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -1045,9 +1045,7 @@ void DisplayChannel::on_disconnect()
_surfaces_cache[0]->clear();
}
- if (screen()) {
- screen()->set_update_interrupt_trigger(NULL);
- }
+ clear();
AutoRef<DetachChannelsEvent> detach_channels(new DetachChannelsEvent(*this));
get_client().push_event(*detach_channels);
@@ -1056,7 +1054,6 @@ void DisplayChannel::on_disconnect()
get_client().push_event(*unlock_event);
detach_from_screen(get_client().get_application());
}
- get_client().deactivate_interval_timer(*_streams_timer);
AutoRef<SyncEvent> sync_event(new SyncEvent());
get_client().push_event(*sync_event);
(*sync_event)->wait();
@@ -1112,7 +1109,7 @@ void DisplayChannel::destroy_off_screen_surfaces()
}
}
-void DisplayChannel::on_disconnect_mig_src()
+void DisplayChannel::clear(bool destroy_primary)
{
_palette_cache.clear();
destroy_streams();
@@ -1122,7 +1119,16 @@ void DisplayChannel::on_disconnect_mig_src()
_update_mark = 0;
_next_timer_time = 0;
get_client().deactivate_interval_timer(*_streams_timer);
- destroy_off_screen_surfaces();
+ if (destroy_primary) {
+ destroy_all_surfaces();
+ } else {
+ destroy_off_screen_surfaces();
+ }
+}
+
+void DisplayChannel::on_disconnect_mig_src()
+{
+ clear(false);
// Not clrearing the primary surface till we receive a new one (or a timeout).
if (_surfaces_cache.exist(0)) {
AutoRef<MigPrimarySurfaceTimer> mig_timer(new MigPrimarySurfaceTimer());
diff --git a/client/display_channel.h b/client/display_channel.h
index 30a76e2..61109ed 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -179,6 +179,7 @@ private:
void activate_streams_timer();
void stream_update_request(uint32_t update_time);
void reset_screen();
+ void clear(bool destroy_primary = true);
static void set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_rects, SpiceRect*& clip_rects);
commit 00ff038cc90ef49f350a2fc033115d10b72995a7
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 21:50:06 2011 +0300
client: display channel migration
(cherry picked from commit cad3c585444f940f60c12789f4174f2d32bec70f branch 0.8)
Conflicts:
client/display_channel.cpp
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index b166c70..f7fdbbc 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -87,6 +87,28 @@ private:
DisplayChannel& _channel;
};
+class DestroyAllSurfacesEvent: public SyncEvent {
+public:
+ DestroyAllSurfacesEvent(DisplayChannel& channel, bool include_primary = true)
+ : _channel(channel)
+ , _include_primary(include_primary)
+ {
+ }
+
+ virtual void do_response(AbstractProcessLoop& events_loop)
+ {
+ if (_include_primary) {
+ _channel.do_destroy_all_surfaces();
+ } else {
+ _channel.do_destroy_off_screen_surfaces();
+ }
+ }
+
+private:
+ DisplayChannel& _channel;
+ bool _include_primary;
+};
+
class CreateSurfaceEvent: public SyncEvent {
public:
CreateSurfaceEvent(DisplayChannel& channel, int surface_id, int width, int height,
@@ -547,6 +569,21 @@ void ResetTimer::response(AbstractProcessLoop& events_loop)
_client.deactivate_interval_timer(this);
}
+#define MIGRATION_PRIMARY_SURFACE_TIMEOUT (1000 * 5)
+
+class MigPrimarySurfaceTimer: public Timer {
+public:
+ virtual void response(AbstractProcessLoop& events_loop)
+ {
+ DisplayChannel *channel = static_cast<DisplayChannel*>(events_loop.get_owner());
+ if (channel->_mig_wait_primary) {
+ channel->destroy_primary_surface();
+ channel->_mig_wait_primary = false;
+ }
+ channel->get_process_loop().deactivate_interval_timer(this);
+ }
+};
+
class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_CHANNEL_DISPLAY> {
public:
DisplayHandler(DisplayChannel& channel)
@@ -574,6 +611,7 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
, _gl_interrupt_recreate (*this)
#endif
, _interrupt_update (*this)
+ , _mig_wait_primary (false)
{
DisplayHandler* handler = static_cast<DisplayHandler*>(get_message_handler());
@@ -621,11 +659,11 @@ DisplayChannel::~DisplayChannel()
screen()->set_update_interrupt_trigger(NULL);
}
- //destroy_canvas(); fixme destroy all
- destroy_strams();
+ destroy_streams();
+ do_destroy_all_surfaces();
}
-void DisplayChannel::destroy_strams()
+void DisplayChannel::destroy_streams()
{
Lock lock(_streams_lock);
for (unsigned int i = 0; i < _streams.size(); i++) {
@@ -1024,6 +1062,75 @@ void DisplayChannel::on_disconnect()
(*sync_event)->wait();
}
+void DisplayChannel::do_destroy_all_surfaces()
+{
+ SurfacesCache::iterator s_iter;
+
+ for (s_iter = _surfaces_cache.begin(); s_iter != _surfaces_cache.end(); s_iter++) {
+ delete (*s_iter).second;
+ }
+ _surfaces_cache.clear();
+}
+
+void DisplayChannel::do_destroy_off_screen_surfaces()
+{
+ SurfacesCache::iterator s_iter;
+ Canvas *primary_canvas = NULL;
+
+ for (s_iter = _surfaces_cache.begin(); s_iter != _surfaces_cache.end(); s_iter++) {
+ if (s_iter->first == 0) {
+ primary_canvas = s_iter->second;
+ } else {
+ delete s_iter->second;
+ }
+ }
+ _surfaces_cache.clear();
+ if (primary_canvas) {
+ _surfaces_cache[0] = primary_canvas;
+ }
+}
+
+void DisplayChannel::destroy_all_surfaces()
+{
+ AutoRef<DestroyAllSurfacesEvent> destroy_event(new DestroyAllSurfacesEvent(*this));
+
+ get_client().push_event(*destroy_event);
+ (*destroy_event)->wait();
+ if (!(*destroy_event)->success()) {
+ THROW("destroy all surfaces failed");
+ }
+}
+
+void DisplayChannel::destroy_off_screen_surfaces()
+{
+ AutoRef<DestroyAllSurfacesEvent> destroy_event(new DestroyAllSurfacesEvent(*this, false));
+
+ get_client().push_event(*destroy_event);
+ (*destroy_event)->wait();
+ if (!(*destroy_event)->success()) {
+ THROW("destroy all surfaces failed");
+ }
+}
+
+void DisplayChannel::on_disconnect_mig_src()
+{
+ _palette_cache.clear();
+ destroy_streams();
+ if (screen()) {
+ screen()->set_update_interrupt_trigger(NULL);
+ }
+ _update_mark = 0;
+ _next_timer_time = 0;
+ get_client().deactivate_interval_timer(*_streams_timer);
+ destroy_off_screen_surfaces();
+ // Not clrearing the primary surface till we receive a new one (or a timeout).
+ if (_surfaces_cache.exist(0)) {
+ AutoRef<MigPrimarySurfaceTimer> mig_timer(new MigPrimarySurfaceTimer());
+ get_process_loop().activate_interval_timer(*mig_timer, MIGRATION_PRIMARY_SURFACE_TIMEOUT);
+ _mig_wait_primary = true;
+ }
+}
+
bool DisplayChannel::create_sw_canvas(int surface_id, int width, int height, uint32_t format)
{
try {
@@ -1365,26 +1472,50 @@ void DisplayChannel::handle_stream_destroy(RedPeer::InMessage* message)
void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message)
{
- destroy_strams();
+ destroy_streams();
}
void DisplayChannel::create_primary_surface(int width, int height, uint32_t format)
{
+ bool do_create_primary = true;
#ifdef USE_OPENGL
- Canvas *canvas;
+ Canvas *canvas;
#endif
- _mark = false;
- attach_to_screen(get_client().get_application(), get_id());
- clear_area();
+ _mark = false;
+
+ /*
+ * trying to avoid artifacts when the display hasn't changed much
+ * between the disconnection from the migration src and the
+ * connection to the target.
+ */
+ if (_mig_wait_primary) {
+ ASSERT(_surfaces_cache.exist(0));
+ if (_x_res != width || _y_res != height || format != format) {
+ LOG_INFO("destroy the primary surface of the mig src session");
+ destroy_primary_surface();
+ } else {
+ LOG_INFO("keep the primary surface of the mig src session");
+ _surfaces_cache[0]->clear();
+ clear_area();
+ do_create_primary = false;
+ }
+ }
- AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height,
+ if (do_create_primary) {
+ LOG_INFO("");
+ attach_to_screen(get_client().get_application(), get_id());
+ clear_area();
+ AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height,
format));
- get_client().push_event(*event);
- (*event)->wait();
- if (!(*event)->success()) {
- THROW("Create primary surface failed");
+ get_client().push_event(*event);
+ (*event)->wait();
+ if (!(*event)->success()) {
+ THROW("Create primary surface failed");
+ }
}
+ _mig_wait_primary = false;
+
_x_res = width;
_y_res = height;
_format = format;
diff --git a/client/display_channel.h b/client/display_channel.h
index f30311d..30a76e2 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -114,6 +114,7 @@ public:
protected:
virtual void on_connect();
virtual void on_disconnect();
+ virtual void on_disconnect_mig_src();
private:
void set_draw_handlers();
@@ -129,13 +130,17 @@ private:
void destroy_canvas(int surface_id);
void create_canvas(int surface_id, const std::vector<int>& canvas_type, int width, int height,
uint32_t format);
- void destroy_strams();
+ void destroy_streams();
void update_cursor();
void create_primary_surface(int width, int height, uint32_t format);
void create_surface(int surface_id, int width, int height, uint32_t format);
void destroy_primary_surface();
void destroy_surface(int surface_id);
+ void destroy_all_surfaces();
+ void do_destroy_all_surfaces();
+ void destroy_off_screen_surfaces();
+ void do_destroy_off_screen_surfaces();
void handle_mode(RedPeer::InMessage* message);
void handle_mark(RedPeer::InMessage* message);
@@ -217,11 +222,13 @@ private:
#endif
InterruptUpdate _interrupt_update;
+ bool _mig_wait_primary;
friend class SetModeEvent;
friend class CreatePrimarySurfaceEvent;
friend class DestroyPrimarySurfaceEvent;
friend class CreateSurfaceEvent;
friend class DestroySurfaceEvent;
+ friend class DestroyAllSurfacesEvent;
friend class ActivateTimerEvent;
friend class VideoStream;
friend class StreamsTrigger;
@@ -229,6 +236,7 @@ private:
friend class StreamsTimer;
friend class AttachChannelsEvent;
friend class DetachChannelsEvent;
+ friend class MigPrimarySurfaceTimer;
};
#endif
commit 87664af999a534090c37ffb53aec2a6cd970cdcc
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 21:42:18 2011 +0300
client: playback/record channels: implement on_disconnect
(cherry picked from commit d3ed9d5e9d52ddcadcb3c8c77dd827b50071d813 branch 0.8)
diff --git a/client/audio_channels.h b/client/audio_channels.h
index 695573a..2722e20 100644
--- a/client/audio_channels.h
+++ b/client/audio_channels.h
@@ -37,6 +37,9 @@ public:
static ChannelFactory& Factory();
+protected:
+ virtual void on_disconnect();
+
private:
void handle_mode(RedPeer::InMessage* message);
void handle_start(RedPeer::InMessage* message);
@@ -48,6 +51,8 @@ private:
void set_data_handler();
+ void clear();
+
private:
WavePlaybackAbstract* _wave_player;
uint32_t _mode;
@@ -67,12 +72,14 @@ public:
static ChannelFactory& Factory();
+protected:
+ virtual void on_connect();
+ virtual void on_disconnect();
+
private:
void handle_start(RedPeer::InMessage* message);
void handle_stop(RedPeer::InMessage* message);
- virtual void on_connect();
-
virtual void add_event_source(EventSources::File& event_source);
virtual void remove_event_source(EventSources::File& event_source);
virtual void add_event_source(EventSources::Trigger& event_source);
@@ -82,6 +89,7 @@ private:
void send_start_mark();
void release_message(RecordSamplesMessage *message);
RecordSamplesMessage * get_message();
+ void clear();
private:
WaveRecordAbstract* _wave_recorder;
diff --git a/client/playback_channel.cpp b/client/playback_channel.cpp
index 731b520..d3445e5 100644
--- a/client/playback_channel.cpp
+++ b/client/playback_channel.cpp
@@ -172,19 +172,37 @@ PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
}
-PlaybackChannel::~PlaybackChannel(void)
+void PlaybackChannel::clear()
{
- delete _wave_player;
+ if (_wave_player) {
+ _playing = false;
+ _wave_player->stop();
+ delete _wave_player;
+ _wave_player = NULL;
+ }
+ _mode = SPICE_AUDIO_DATA_MODE_INVALID;
if (_celt_decoder) {
celt051_decoder_destroy(_celt_decoder);
+ _celt_decoder = NULL;
}
if (_celt_mode) {
celt051_mode_destroy(_celt_mode);
+ _celt_mode = NULL;
}
}
+void PlaybackChannel::on_disconnect()
+{
+ clear();
+}
+
+PlaybackChannel::~PlaybackChannel(void)
+{
+ clear();
+}
+
bool PlaybackChannel::abort(void)
{
return (!_wave_player || _wave_player->abort()) && RedChannel::abort();
diff --git a/client/record_channel.cpp b/client/record_channel.cpp
index 738f98f..0476031 100644
--- a/client/record_channel.cpp
+++ b/client/record_channel.cpp
@@ -101,14 +101,7 @@ RecordChannel::~RecordChannel(void)
_messages.pop_front();
delete mes;
}
- delete _wave_recorder;
-
- if (_celt_encoder) {
- celt051_encoder_destroy(_celt_encoder);
- }
- if (_celt_mode) {
- celt051_mode_destroy(_celt_mode);
- }
+ clear();
}
bool RecordChannel::abort(void)
@@ -128,6 +121,11 @@ void RecordChannel::on_connect()
post_message(message);
}
+void RecordChannel::on_disconnect()
+{
+ clear();
+}
+
void RecordChannel::send_start_mark()
{
Message* message = new Message(SPICE_MSGC_RECORD_START_MARK);
@@ -177,6 +175,23 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
_wave_recorder->start();
}
+void RecordChannel::clear()
+{
+ if (_wave_recorder) {
+ _wave_recorder->stop();
+ delete _wave_recorder;
+ _wave_recorder = NULL;
+ }
+ if (_celt_encoder) {
+ celt051_encoder_destroy(_celt_encoder);
+ _celt_encoder = NULL;
+ }
+ if (_celt_mode) {
+ celt051_mode_destroy(_celt_mode);
+ _celt_mode = NULL;
+ }
+}
+
void RecordChannel::handle_stop(RedPeer::InMessage* message)
{
RecordHandler* handler = static_cast<RecordHandler*>(get_message_handler());
@@ -186,13 +201,7 @@ void RecordChannel::handle_stop(RedPeer::InMessage* message)
return;
}
ASSERT(_celt_mode && _celt_encoder);
- _wave_recorder->stop();
- celt051_encoder_destroy(_celt_encoder);
- _celt_encoder = NULL;
- celt051_mode_destroy(_celt_mode);
- _celt_mode = NULL;
- delete _wave_recorder;
- _wave_recorder = NULL;
+ clear();
}
RecordSamplesMessage* RecordChannel::get_message()
commit 0a5e9cbbcf9a3ebc6c7e3ccd3e7026998d2488d9
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 22:50:50 2011 +0300
client: main channel migration: do partial cleanup when switching hosts
Implement on_disconnect_mig_src and on_connect_mig_target in order to avoid
unnecessary cleanups done in on_(disconnet|connect).
In addition, do not request guest display settings changes after migration.
(cherry picked from commit f91d202eb3bf631cf5e70277d1aabffec7da9393 branch 0.8)
diff --git a/client/red_client.cpp b/client/red_client.cpp
index 84440c1..4fc442f 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -498,6 +498,15 @@ void RedClient::on_disconnect()
(*sync_event)->wait();
}
+void RedClient::on_disconnect_mig_src()
+{
+ _application.deactivate_interval_timer(*_agent_timer);
+ delete[] _agent_msg_data;
+ _agent_msg_data = NULL;
+ _agent_msg_pos = 0;
+ _agent_tokens = 0;
+}
+
void RedClient::delete_channels()
{
Lock lock(_channels_lock);
diff --git a/client/red_client.h b/client/red_client.h
index 7b3e507..20d5ffb 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -279,6 +279,8 @@ protected:
virtual void on_connecting();
virtual void on_connect();
virtual void on_disconnect();
+ virtual void on_connect_mig_target() {}
+ virtual void on_disconnect_mig_src();
private:
void on_channel_disconnected(RedChannel& channel);
commit c73d5c10e677b8ab67101545bb17eb46afd1e251
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 21:33:02 2011 +0300
client: handle SPICE_MSG_MAIN_MIGRATE_END
(1) disconnect all channels from the migration src
(2) after all channels are disconnected, clean global resources
(3) send SPICE_MSGC_MAIN_MIGRATE_END to migration target
(4) wait for SPICE_MSG_MAIN_INIT
(4) switch all channels to migration target
(cherry picked from commit 510a4ff7c4f188fe6d0fb12198b8f9fdb74b9a2d branch 0.8)
Conflicts:
client/red_channel.h
diff --git a/client/red_channel.cpp b/client/red_channel.cpp
index 8632600..d85265b 100644
--- a/client/red_channel.cpp
+++ b/client/red_channel.cpp
@@ -30,6 +30,15 @@
#include "openssl/evp.h"
#include "openssl/x509.h"
+void MigrationDisconnectSrcEvent::response(AbstractProcessLoop& events_loop)
+{
+ static_cast<RedChannel*>(events_loop.get_owner())->do_migration_disconnect_src();
+}
+
+void MigrationConnectTargetEvent::response(AbstractProcessLoop& events_loop)
+{
+ static_cast<RedChannel*>(events_loop.get_owner())->do_migration_connect_target();
+}
RedChannelBase::RedChannelBase(uint8_t type, uint8_t id, const ChannelCaps& common_caps,
const ChannelCaps& caps)
@@ -440,6 +449,57 @@ void RedChannel::disconnect()
_action_cond.notify_one();
}
+void RedChannel::disconnect_migration_src()
+{
+ clear_outgoing_messages();
+
+ Lock lock(_action_lock);
+ if (_state == CONNECTING_STATE || _state == CONNECTED_STATE) {
+ AutoRef<MigrationDisconnectSrcEvent> migrate_event(new MigrationDisconnectSrcEvent());
+ _loop.push_event(*migrate_event);
+ }
+}
+
+void RedChannel::connect_migration_target()
+{
+ LOG_INFO("");
+ AutoRef<MigrationConnectTargetEvent> migrate_event(new MigrationConnectTargetEvent());
+ _loop.push_event(*migrate_event);
+}
+
+void RedChannel::do_migration_disconnect_src()
+{
+ if (_socket_in_loop) {
+ _socket_in_loop = false;
+ _loop.remove_socket(*this);
+ }
+
+ clear_outgoing_messages();
+ if (_outgoing_message) {
+ _outgoing_message->release();
+ _outgoing_message = NULL;
+ }
+ _incomming_header_pos = 0;
+ if (_incomming_message) {
+ _incomming_message->unref();
+ _incomming_message = NULL;
+ }
+
+ on_disconnect_mig_src();
+ get_client().migrate_channel(*this);
+ get_client().on_channel_disconnect_mig_src_completed(*this);
+}
+
+void RedChannel::do_migration_connect_target()
+{
+ LOG_INFO("");
+ _loop.add_socket(*this);
+ _socket_in_loop = true;
+ on_connect_mig_target();
+ set_state(CONNECTED_STATE);
+ on_event();
+}
+
void RedChannel::clear_outgoing_messages()
{
Lock lock(_outgoing_lock);
diff --git a/client/red_channel.h b/client/red_channel.h
index 6a5a9f6..ba78acc 100644
--- a/client/red_channel.h
+++ b/client/red_channel.h
@@ -106,6 +106,16 @@ public:
virtual void on_event();
};
+class MigrationDisconnectSrcEvent: public Event {
+public:
+ virtual void response(AbstractProcessLoop& events_loop);
+};
+
+class MigrationConnectTargetEvent: public Event {
+public:
+ virtual void response(AbstractProcessLoop& events_loop);
+};
+
struct SyncInfo {
Mutex* lock;
Condition* condition;
@@ -126,6 +136,9 @@ public:
virtual void disconnect();
virtual bool abort();
+ virtual void disconnect_migration_src();
+ virtual void connect_migration_target();
+
virtual CompoundInMessage *receive();
virtual void post_message(RedChannel::OutMessage* message);
@@ -140,6 +153,8 @@ protected:
virtual void on_connect() {}
virtual void on_disconnect() {}
virtual void on_migrate() {}
+ virtual void on_disconnect_mig_src() { on_disconnect();}
+ virtual void on_connect_mig_target() { on_connect();}
void handle_migrate(RedPeer::InMessage* message);
void handle_set_ack(RedPeer::InMessage* message);
void handle_ping(RedPeer::InMessage* message);
@@ -159,6 +174,8 @@ private:
virtual void on_event();
void on_message_received();
void on_message_complition(uint64_t serial);
+ void do_migration_disconnect_src();
+ void do_migration_connect_target();
static void* worker_main(void *);
@@ -203,6 +220,8 @@ private:
uint64_t _disconnect_reason;
friend class SendTrigger;
+ friend class MigrationDisconnectSrcEvent;
+ friend class MigrationConnectTargetEvent;
};
diff --git a/client/red_client.cpp b/client/red_client.cpp
index afde7d2..84440c1 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -26,6 +26,7 @@
#include "utils.h"
#include "debug.h"
#include "marshallers.h"
+#include <algorithm>
#ifndef INFINITY
#define INFINITY HUGE
@@ -123,6 +124,11 @@ void ClipboardReleaseEvent::response(AbstractProcessLoop& events_loop)
VD_AGENT_CLIPBOARD_RELEASE, 0, NULL);
}
+void MigrateEndEvent::response(AbstractProcessLoop& events_loop)
+{
+ static_cast<RedClient*>(events_loop.get_owner())->send_migrate_end();
+}
+
Migrate::Migrate(RedClient& client)
: _client (client)
, _running (false)
@@ -395,6 +401,7 @@ RedClient::RedClient(Application& application)
, _agent_caps(NULL)
, _migrate (*this)
, _glz_window (_glz_debug)
+ , _during_migration (false)
{
Platform::set_clipboard_listener(this);
MainChannelLoop* message_loop = static_cast<MainChannelLoop*>(get_message_handler());
@@ -413,6 +420,7 @@ RedClient::RedClient(Application& application)
message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_BEGIN, &RedClient::handle_migrate_begin);
message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_CANCEL, &RedClient::handle_migrate_cancel);
+ message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_END, &RedClient::handle_migrate_end);
message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST,
&RedClient::handle_migrate_switch_host);
message_loop->set_handler(SPICE_MSG_MAIN_INIT, &RedClient::handle_init);
@@ -424,6 +432,8 @@ RedClient::RedClient(Application& application)
message_loop->set_handler(SPICE_MSG_MAIN_AGENT_DISCONNECTED, &RedClient::handle_agent_disconnected);
message_loop->set_handler(SPICE_MSG_MAIN_AGENT_DATA, &RedClient::handle_agent_data);
message_loop->set_handler(SPICE_MSG_MAIN_AGENT_TOKEN, &RedClient::handle_agent_tokens);
+
+ set_capability(SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
start();
}
@@ -491,6 +501,7 @@ void RedClient::on_disconnect()
void RedClient::delete_channels()
{
Lock lock(_channels_lock);
+ _pending_mig_disconnect_channels.clear();
while (!_channels.empty()) {
RedChannel *channel = *_channels.begin();
_channels.pop_front();
@@ -618,7 +629,7 @@ bool RedClient::abort()
void RedClient::handle_migrate_begin(RedPeer::InMessage* message)
{
- DBG(0, "");
+ LOG_INFO("");
SpiceMsgMainMigrationBegin* migrate = (SpiceMsgMainMigrationBegin*)message->data();
//add mig channels
_migrate.start(migrate);
@@ -626,9 +637,59 @@ void RedClient::handle_migrate_begin(RedPeer::InMessage* message)
void RedClient::handle_migrate_cancel(RedPeer::InMessage* message)
{
+ LOG_INFO("");
_migrate.abort();
}
+void RedClient::handle_migrate_end(RedPeer::InMessage* message)
+{
+ LOG_INFO("");
+
+ Lock lock(_channels_lock);
+ ASSERT(_pending_mig_disconnect_channels.empty());
+ Channels::iterator iter = _channels.begin();
+ for (; iter != _channels.end(); ++iter) {
+ (*iter)->disconnect_migration_src();
+ _pending_mig_disconnect_channels.push_back(*iter);
+ }
+ RedChannel::disconnect_migration_src();
+ _pending_mig_disconnect_channels.push_back(this);
+ _during_migration = true;
+}
+
+void RedClient::on_channel_disconnect_mig_src_completed(RedChannel& channel)
+{
+ Lock lock(_channels_lock);
+ Channels::iterator pending_iter = std::find(_pending_mig_disconnect_channels.begin(),
+ _pending_mig_disconnect_channels.end(),
+ &channel);
+
+ LOG_INFO("");
+ if (pending_iter == _pending_mig_disconnect_channels.end()) {
+ THROW("unexpected channel");
+ }
+
+ _pending_mig_disconnect_channels.erase(pending_iter);
+ /* clean shared data when all channels have disconnected */
+ if (_pending_mig_disconnect_channels.empty()) {
+ _pixmap_cache.clear();
+ _glz_window.clear();
+ memset(_sync_info, 0, sizeof(_sync_info));
+
+ LOG_INFO("calling main to connect and wait for handle_init to tell all the other channels to connect");
+ RedChannel::connect_migration_target();
+
+ AutoRef<MigrateEndEvent> mig_end_event(new MigrateEndEvent());
+ get_process_loop().push_event(*mig_end_event);
+ }
+}
+
+void RedClient::send_migrate_end()
+{
+ Message* message = new Message(SPICE_MSGC_MAIN_MIGRATE_END);
+ post_message(message);
+}
+
ChannelFactory* RedClient::find_factory(uint32_t type)
{
Factorys::iterator iter = _factorys.begin();
@@ -973,12 +1034,37 @@ void RedClient::set_mouse_mode(uint32_t supported_modes, uint32_t current_mode)
}
}
+/* returns true if we should wait for a response from the agent */
+bool RedClient::init_guest_display()
+{
+ if (_agent_connected) {
+ if (_auto_display_res) {
+ send_agent_monitors_config();
+ }
+
+ if (_auto_display_res || !_display_setting.is_empty()) {
+ _application.activate_interval_timer(*_agent_timer, AGENT_TIMEOUT);
+ } else {
+ return false;
+ }
+ } else {
+ if (_auto_display_res || !_display_setting.is_empty()) {
+ LOG_WARN("no agent running, display options have been ignored");
+ }
+ return false;
+ }
+ return true;
+}
+
void RedClient::handle_init(RedPeer::InMessage* message)
{
SpiceMsgMainInit *init = (SpiceMsgMainInit *)message->data();
+ LOG_INFO("");
_connection_id = init->session_id;
set_mm_time(init->multi_media_time);
- calc_pixmap_cach_and_glz_window_size(init->display_channels_hint, init->ram_hint);
+ if (!_during_migration) {
+ calc_pixmap_cach_and_glz_window_size(init->display_channels_hint, init->ram_hint);
+ }
set_mouse_mode(init->supported_mouse_modes, init->current_mouse_mode);
_agent_tokens = init->agent_tokens;
_agent_connected = !!init->agent_connected;
@@ -989,20 +1075,19 @@ void RedClient::handle_init(RedPeer::InMessage* message)
_marshallers->msgc_main_agent_start(msg->marshaller(), &agent_start);
post_message(msg);
send_agent_announce_capabilities(true);
- if (_auto_display_res) {
- send_agent_monitors_config();
- }
+ }
- if (_auto_display_res || !_display_setting.is_empty()) {
- _application.activate_interval_timer(*_agent_timer, AGENT_TIMEOUT);
- } else {
+ if (!_during_migration) {
+ if (!init_guest_display()) {
send_main_attach_channels();
}
} else {
- if (_auto_display_res || !_display_setting.is_empty()) {
- LOG_WARN("no agent running, display options have been ignored");
+ LOG_INFO("connecting all channels after migration");
+ Channels::iterator iter = _channels.begin();
+ for (; iter != _channels.end(); ++iter) {
+ (*iter)->connect_migration_target();
}
- send_main_attach_channels();
+ _during_migration = false;
}
}
diff --git a/client/red_client.h b/client/red_client.h
index 17391e4..7b3e507 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -207,6 +207,10 @@ public:
virtual void response(AbstractProcessLoop& events_loop);
};
+class MigrateEndEvent: public Event {
+public:
+ virtual void response(AbstractProcessLoop& events_loop);
+};
class RedClient: public RedChannel,
public Platform::ClipboardListener {
@@ -217,6 +221,7 @@ public:
friend class ClipboardRequestEvent;
friend class ClipboardNotifyEvent;
friend class ClipboardReleaseEvent;
+ friend class MigrateEndEvent;
RedClient(Application& application);
~RedClient();
@@ -277,6 +282,8 @@ protected:
private:
void on_channel_disconnected(RedChannel& channel);
+ void on_channel_disconnect_mig_src_completed(RedChannel& channel);
+ void send_migrate_end();
void migrate_channel(RedChannel& channel);
void send_agent_announce_capabilities(bool request);
void send_agent_monitors_config();
@@ -287,6 +294,7 @@ private:
void handle_migrate_begin(RedPeer::InMessage* message);
void handle_migrate_cancel(RedPeer::InMessage* message);
+ void handle_migrate_end(RedPeer::InMessage* message);
void handle_init(RedPeer::InMessage* message);
void handle_channels(RedPeer::InMessage* message);
void handle_mouse_mode(RedPeer::InMessage* message);
@@ -298,6 +306,7 @@ private:
void handle_migrate_switch_host(RedPeer::InMessage* message);
void dispatch_agent_message(VDAgentMessage* msg, void* data);
+ bool init_guest_display();
void on_agent_reply(VDAgentReply* reply);
void on_agent_announce_capabilities(VDAgentAnnounceCapabilities* caps,
uint32_t msg_size);
@@ -354,6 +363,7 @@ private:
Factorys _factorys;
typedef std::list<RedChannel*> Channels;
Channels _channels;
+ Channels _pending_mig_disconnect_channels;
PixmapCache _pixmap_cache;
uint64_t _pixmap_cache_size;
Mutex _sync_lock;
@@ -367,6 +377,8 @@ private:
Mutex _mm_clock_lock;
uint64_t _mm_clock_last_update;
uint32_t _mm_time;
+
+ bool _during_migration;
};
#endif
commit 6cd3ffba6fbb53102bbaf69bcdba29e5a1db458d
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 21:21:00 2011 +0300
client: handle SpiceMsgMainMigrationBegin (semi-seamless migration)
RHBZ 725009, 738270
(cherry picked from commit 31ed2519a752b7332ed40d0d7ab02e938c0e65cb branch 0.8)
Conflicts:
client/red_client.cpp
diff --git a/client/red_client.cpp b/client/red_client.cpp
index efd9feb..afde7d2 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -260,9 +260,15 @@ void* Migrate::worker_main(void *data)
void Migrate::start(const SpiceMsgMainMigrationBegin* migrate)
{
+ std::string cert_subject;
+ uint32_t peer_major;
+ uint32_t peer_minor;
+
DBG(0, "");
abort();
- if ((_client.get_peer_major() == 1) && (_client.get_peer_minor() < 1)) {
+ peer_major = _client.get_peer_major();
+ peer_minor = _client.get_peer_minor();
+ if ((peer_major == 1) && (peer_minor < 1)) {
LOG_INFO("server minor version incompatible for destination authentication"
"(missing dest pubkey in SpiceMsgMainMigrationBegin)");
OldRedMigrationBegin* old_migrate = (OldRedMigrationBegin*)migrate;
@@ -274,8 +280,19 @@ void Migrate::start(const SpiceMsgMainMigrationBegin* migrate)
_host.assign((char *)migrate->host_data);
_port = migrate->port ? migrate->port : -1;
_sport = migrate->sport ? migrate->sport : -1;
- _auth_options.type_flags = SPICE_SSL_VERIFY_OP_PUBKEY;
- _auth_options.host_pubkey.assign(migrate->pub_key_data, migrate->pub_key_data + migrate->pub_key_size);
+ if ((peer_major == 1) || (peer_major == 2 && peer_minor < 1)) {
+ _auth_options.type_flags = SPICE_SSL_VERIFY_OP_PUBKEY;
+ _auth_options.host_pubkey.assign(migrate->pub_key_data, migrate->pub_key_data +
+ migrate->pub_key_size);
+ } else {
+ _auth_options.type_flags = SPICE_SSL_VERIFY_OP_SUBJECT;
+ _auth_options.CA_file = _client.get_host_auth_options().CA_file;
+ if (migrate->cert_subject_size != 0) {
+ _auth_options.host_subject.assign(migrate->cert_subject_data,
+ migrate->cert_subject_data +
+ migrate->cert_subject_size);
+ }
+ }
}
_con_ciphers = _client.get_connection_ciphers();
commit f22caf9aee2aa64313468b04efa615e2e1c7f8b3
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 14:52:04 2011 +0300
client: rewrite surfaces cache
use std::map instead of a specific template (CHash).
There is no need for special template. Moreover, using
std::map will allow easy iteration over the surfaces.
(cherry picked from commit fcb3b4ce5231218bcf949da4270bd85a2cfb3535 branch 0.8)
Conflicts:
client/display_channel.cpp
diff --git a/client/canvas.cpp b/client/canvas.cpp
index 3b5f4b6..139b663 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -24,14 +24,35 @@
#include "utils.h"
#include "debug.h"
+static SpiceCanvas* surfaces_cache_op_get(SpiceImageSurfaces *surfaces, uint32_t surface_id)
+{
+ SurfacesCache* surfaces_cache = static_cast<SurfacesCache*>(surfaces);
+ if (!surfaces_cache->exist(surface_id)) {
+ return NULL;
+ }
+ return (*surfaces_cache)[surface_id]->get_internal_canvas();
+}
+
+SurfacesCache::SurfacesCache()
+{
+ static SpiceImageSurfacesOps surfaces_ops = {
+ surfaces_cache_op_get,
+ };
+ ops = &surfaces_ops;
+}
+
+bool SurfacesCache::exist(uint32_t surface_id)
+{
+ return (this->count(surface_id) != 0);
+}
Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
: _canvas (NULL)
, _pixmap_cache (pixmap_cache)
, _palette_cache (palette_cache)
, _glz_decoder(glz_decoder_window, _glz_handler, _glz_debug)
- , _csurfaces(csurfaces)
+ , _surfaces_cache(csurfaces)
{
}
diff --git a/client/canvas.h b/client/canvas.h
index 0b8f2b6..a9cdecd 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -30,6 +30,7 @@
#include "glz_decoder.h"
#include "jpeg_decoder.h"
#include "zlib_decoder.h"
+#include <map>
enum CanvasType {
CANVAS_TYPE_INVALID,
@@ -38,102 +39,6 @@ enum CanvasType {
CANVAS_TYPE_GDI,
};
-template <class T, int HASH_SIZE, class Base = EmptyBase>
-class CHash : public Base {
-public:
- CHash()
- {
- memset(_hash, 0, sizeof(_hash));
- }
-
- ~CHash()
- {
- }
-
- void add(uint32_t id, T* data)
- {
- Item** item = &_hash[key(id)];
-
- while (*item) {
- PANIC_ON((*item)->id == id);
- item = &(*item)->next;
- }
- *item = new Item(id, data);
- }
-
- bool is_present(uint32_t id)
- {
- Item* item = _hash[key(id)];
-
- for (;;) {
- if (!item) {
- return false;
- }
-
- if (item->id != id) {
- item = item->next;
- continue;
- }
-
- return true;
- }
- }
-
- T* get(uint32_t id)
- {
- Item* item = _hash[key(id)];
-
- for (;;) {
- PANIC_ON(!item);
-
- if (item->id != id) {
- item = item->next;
- continue;
- }
-
- return item->data;
- }
- }
-
- void remove(uint32_t id)
- {
- Item** item = &_hash[key(id)];
-
- while (*item) {
- if ((*item)->id == id) {
- Item *rm_item = *item;
- *item = rm_item->next;
- delete rm_item;
- return;
- }
- item = &(*item)->next;
- }
- THROW("id %lu, not found", id);
- }
-
-private:
- inline uint32_t key(uint32_t id) {return id % HASH_SIZE;}
-
-private:
- class Item {
- public:
- Item(uint32_t in_id, T* data)
- : id (in_id)
- , next (NULL)
- , data (data) {}
-
- ~Item()
- {
- }
-
- uint64_t id;
- Item* next;
- T* data;
- };
-
- Item* _hash[HASH_SIZE];
-};
-
class PixmapCacheTreat {
public:
static inline pixman_image_t *get(pixman_image_t *surf)
@@ -200,44 +105,6 @@ public:
}
};
-class SpiceImageSurfacesBase;
-
-typedef CHash<SpiceCanvas, 1024, SpiceImageSurfacesBase> CSurfaces;
-
-class SpiceImageSurfacesBase {
-public:
- SpiceImageSurfaces base;
-
- static void op_put(SpiceImageSurfaces *c, uint32_t surface_id, SpiceCanvas *surface)
- {
- CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
- cache->add(surface_id, surface);
- }
-
- static SpiceCanvas* op_get(SpiceImageSurfaces *s, uint32_t surface_id)
- {
- CSurfaces* cache = reinterpret_cast<CSurfaces*>(s);
- return cache->get(surface_id);
- }
-
- static void op_del(SpiceImageSurfaces *c, uint32_t surface_id)
- {
- CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
- cache->remove(surface_id);
- }
-
- SpiceImageSurfacesBase()
- {
- static SpiceImageSurfacesOps cache_ops = {
- op_get
- };
- base.ops = &cache_ops;
- }
-};
-
-class Canvas;
-
-typedef CHash<Canvas, 1024, SpiceImageSurfacesBase> CCanvases;
class CachedPalette {
public:
@@ -399,10 +266,20 @@ public:
}
};
+class Canvas;
+
+typedef std::map<uint32_t, Canvas*> SurfacesCanvasesMap;
+
+class SurfacesCache: public SpiceImageSurfaces, public SurfacesCanvasesMap {
+public:
+ SurfacesCache();
+ bool exist(uint32_t surface_id);
+};
+
class Canvas {
public:
Canvas(PixmapCache& bits_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces);
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache& csurfaces);
virtual ~Canvas();
virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
@@ -442,7 +319,7 @@ protected:
PixmapCache& pixmap_cache() { return _pixmap_cache;}
PaletteCache& palette_cache() { return _palette_cache;}
- CSurfaces& csurfaces() { return _csurfaces; }
+ SurfacesCache& surfaces_cache() { return _surfaces_cache;}
GlzDecoder& glz_decoder() {return _glz_decoder;}
JpegDecoder& jpeg_decoder() { return _jpeg_decoder;}
@@ -453,7 +330,6 @@ private:
protected:
SpiceCanvas* _canvas;
- CSurfaces _surfaces;
private:
PixmapCache& _pixmap_cache;
@@ -466,7 +342,7 @@ private:
JpegDecoder _jpeg_decoder;
ZlibDecoder _zlib_decoder;
- CSurfaces& _csurfaces;
+ SurfacesCache& _surfaces_cache;
unsigned long _base;
unsigned long _max;
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index 5ab7493..b166c70 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -547,42 +547,6 @@ void ResetTimer::response(AbstractProcessLoop& events_loop)
_client.deactivate_interval_timer(this);
}
-void DisplaySurfacesManger::add_surface(int surface_id, SpiceCanvas *surface)
-{
- surfaces.add(surface_id, surface);
-}
-
-void DisplaySurfacesManger::del_surface(int surface_id)
-{
- surfaces.remove(surface_id);
-}
-
-void DisplaySurfacesManger::add_canvas(int surface_id, Canvas *canvas)
-{
- canvases.add(surface_id, canvas);
-}
-
-void DisplaySurfacesManger::del_canvas(int surface_id)
-{
- canvases.remove(surface_id);
-}
-
-CSurfaces& DisplaySurfacesManger::get_surfaces()
-{
- return surfaces;
-}
-
-bool DisplaySurfacesManger::is_present_canvas(int surface_id)
-{
-
- return canvases.is_present(surface_id);
-}
-
-Canvas* DisplaySurfacesManger::get_canvas(int surface_id)
-{
- return canvases.get(surface_id);
-}
-
class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_CHANNEL_DISPLAY> {
public:
DisplayHandler(DisplayChannel& channel)
@@ -719,11 +683,11 @@ void DisplayChannel::copy_pixels(const QRegion& dest_region,
{
Canvas *canvas;
- if (!surfaces_mngr.is_present_canvas(0)) {
+ if (!_surfaces_cache.exist(0)) {
return;
}
- canvas = surfaces_mngr.get_canvas(0);
+ canvas = _surfaces_cache[0];
canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
}
@@ -732,8 +696,8 @@ void DisplayChannel::recreate_ogl_context_interrupt()
{
Canvas* canvas;
- if (surfaces_mngr.is_present_canvas(0)) { //fix me to all surfaces
- canvas = surfaces_mngr.get_canvas(0);
+ if (_surfaces_cache.exist(0)) { //fix me to all surfaces
+ canvas = _surfaces_cache[0];
((GCanvas *)(canvas))->touch_context();
((GCanvas *)canvas)->textures_lost();
delete canvas;
@@ -743,13 +707,13 @@ void DisplayChannel::recreate_ogl_context_interrupt()
THROW("create_ogl_canvas failed");
}
- canvas = surfaces_mngr.get_canvas(0);
+ canvas = _surfaces_cache[0];
((GCanvas *)(canvas))->touch_context();
}
void DisplayChannel::recreate_ogl_context()
{
- if (surfaces_mngr.is_present_canvas(0) && surfaces_mngr.get_canvas(0)->get_pixmap_type() ==
+ if (_surfaces_cache.exist(0) && _surfaces_cache[0]->get_pixmap_type() ==
CANVAS_TYPE_GL) {
if (!screen()->need_recreate_context_gl()) {
_gl_interrupt_recreate.trigger();
@@ -889,12 +853,12 @@ void DisplayChannel::update_interrupt()
Canvas *canvas;
#endif
- if (!surfaces_mngr.is_present_canvas(0) || !screen()) {
+ if (!_surfaces_cache.exist(0) || !screen()) {
return;
}
#ifdef USE_OPENGL
- canvas = surfaces_mngr.get_canvas(0);
+ canvas = _surfaces_cache[0];
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->pre_gl_copy();
}
@@ -918,7 +882,7 @@ void DisplayChannel::pre_migrate()
void DisplayChannel::post_migrate()
{
#ifdef USE_OPENGL
- if (surfaces_mngr.get_canvas(0)->get_pixmap_type() == CANVAS_TYPE_GL) {
+ if (_surfaces_cache.exist(0) && _surfaces_cache[0]->get_pixmap_type() == CANVAS_TYPE_GL) {
_gl_interrupt_recreate.trigger();
}
#endif
@@ -929,15 +893,11 @@ void DisplayChannel::post_migrate()
void DisplayChannel::copy_pixels(const QRegion& dest_region,
RedDrawable& dest_dc)
{
- Canvas *canvas;
-
- if (!surfaces_mngr.is_present_canvas(0)) {
+ if (!_surfaces_cache.exist(0)) {
return;
}
- canvas = surfaces_mngr.get_canvas(0);
-
- canvas->copy_pixels(dest_region, dest_dc);
+ _surfaces_cache[0]->copy_pixels(dest_region, dest_dc);
}
class ActivateTimerEvent: public Event {
@@ -1043,11 +1003,8 @@ void DisplayChannel::on_connect()
void DisplayChannel::on_disconnect()
{
- if (surfaces_mngr.is_present_canvas(0)) {
- Canvas *canvas;
-
- canvas = surfaces_mngr.get_canvas(0);
- canvas->clear();
+ if (_surfaces_cache.exist(0)) {
+ _surfaces_cache[0]->clear();
}
if (screen()) {
@@ -1073,9 +1030,8 @@ bool DisplayChannel::create_sw_canvas(int surface_id, int width, int height, uin
SCanvas *canvas = new SCanvas(surface_id == 0, width, height, format,
screen()->get_window(),
_pixmap_cache, _palette_cache, _glz_window,
- surfaces_mngr.get_surfaces());
- surfaces_mngr.add_canvas(surface_id, canvas);
- surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+ _surfaces_cache);
+ _surfaces_cache[surface_id] = canvas;
if (surface_id == 0) {
LOG_INFO("display %d: using sw", get_id());
}
@@ -1097,12 +1053,11 @@ bool DisplayChannel::create_ogl_canvas(int surface_id, int width, int height, ui
_pixmap_cache,
_palette_cache,
_glz_window,
- surfaces_mngr.get_surfaces());
+ _surfaces_cache);
screen()->untouch_context();
- surfaces_mngr.add_canvas(surface_id, canvas);
- surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+ _surfaces_cache[surface_id] = canvas;
_rendertype = rendertype;
if (surface_id == 0) {
LOG_INFO("display %d: using ogl", get_id());
@@ -1121,9 +1076,8 @@ bool DisplayChannel::create_gdi_canvas(int surface_id, int width, int height, ui
try {
GDICanvas *canvas = new GDICanvas(width, height, format,
_pixmap_cache, _palette_cache, _glz_window,
- surfaces_mngr.get_surfaces());
- surfaces_mngr.add_canvas(surface_id, canvas);
- surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+ _surfaces_cache);
+ _surfaces_cache[surface_id] = canvas;
if (surface_id == 0) {
LOG_INFO("display %d: using gdi", get_id());
}
@@ -1139,11 +1093,13 @@ void DisplayChannel::destroy_canvas(int surface_id)
{
Canvas *canvas;
- if (!surfaces_mngr.is_present_canvas(surface_id)) {
+ if (!_surfaces_cache.exist(surface_id)) {
+ LOG_INFO("surface does not exist: %d", surface_id);
return;
}
- canvas = surfaces_mngr.get_canvas(surface_id);
+ canvas = _surfaces_cache[surface_id];
+ _surfaces_cache.erase(surface_id);
#ifdef USE_OPENGL
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
@@ -1151,9 +1107,6 @@ void DisplayChannel::destroy_canvas(int surface_id)
}
#endif
- surfaces_mngr.del_canvas(surface_id);
- surfaces_mngr.del_surface(surface_id);
-
delete canvas;
}
@@ -1171,10 +1124,13 @@ void DisplayChannel::create_canvas(int surface_id, const std::vector<int>& canva
recreate = false;
}
#endif
-
screen()->set_update_interrupt_trigger(NULL);
}
+ if (_surfaces_cache.exist(surface_id)) {
+ LOG_WARN("surface already exists: %d", surface_id);
+ }
+
for (i = 0; i < canvas_types.size(); i++) {
if (canvas_types[i] == CANVAS_OPTION_SW && create_sw_canvas(surface_id, width, height, format)) {
@@ -1208,7 +1164,7 @@ void DisplayChannel::handle_mode(RedPeer::InMessage* message)
{
SpiceMsgDisplayMode *mode = (SpiceMsgDisplayMode *)message->data();
- if (surfaces_mngr.is_present_canvas(0)) {
+ if (_surfaces_cache.exist(0)) {
destroy_primary_surface();
}
create_primary_surface(mode->x_res, mode->y_res,
@@ -1244,10 +1200,8 @@ void DisplayChannel::reset_screen()
void DisplayChannel::handle_reset(RedPeer::InMessage *message)
{
- if (surfaces_mngr.is_present_canvas(0)) {
- Canvas *canvas;
- canvas = surfaces_mngr.get_canvas(0);
- canvas->clear();
+ if (_surfaces_cache.exist(0)) {
+ _surfaces_cache[0]->clear();
}
_palette_cache.clear();
@@ -1326,10 +1280,14 @@ void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
THROW("stream exist");
}
+ if (!_surfaces_cache.exist(surface_id)) {
+ THROW("surface does not exist: %d", surface_id);
+ }
+
uint32_t num_clip_rects;
SpiceRect* clip_rects;
set_clip_rects(stream_create->clip, num_clip_rects, clip_rects);
- _streams[stream_create->id] = new VideoStream(get_client(), *surfaces_mngr.get_canvas(surface_id),
+ _streams[stream_create->id] = new VideoStream(get_client(), *_surfaces_cache[surface_id],
*this, stream_create->codec_type,
!!(stream_create->flags & SPICE_STREAM_FLAGS_TOP_DOWN),
stream_create->stream_width,
@@ -1432,7 +1390,7 @@ void DisplayChannel::create_primary_surface(int width, int height, uint32_t form
_format = format;
#ifdef USE_OPENGL
- canvas = surfaces_mngr.get_canvas(0);
+ canvas = _surfaces_cache[0];
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->touch_context();
@@ -1455,7 +1413,7 @@ void DisplayChannel::create_surface(int surface_id, int width, int height, uint3
#ifdef USE_OPENGL
Canvas *canvas;
- canvas = surfaces_mngr.get_canvas(surface_id);
+ canvas = _surfaces_cache[surface_id];
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->touch_context();
@@ -1467,11 +1425,8 @@ void DisplayChannel::destroy_primary_surface()
{
if (screen()) {
#ifdef USE_OPENGL
- if (surfaces_mngr.is_present_canvas(0)) {
- Canvas *canvas;
-
- canvas = surfaces_mngr.get_canvas(0);
- if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+ if (_surfaces_cache.exist(0)) {
+ if (_surfaces_cache[0]->get_pixmap_type() == CANVAS_TYPE_GL) {
screen()->unset_type_gl();
screen()->untouch_context();
}
@@ -1538,7 +1493,7 @@ void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
Canvas *canvas;
SpiceMsgDisplayCopyBits* copy_bits = (SpiceMsgDisplayCopyBits*)message->data();
PRE_DRAW;
- canvas = surfaces_mngr.get_canvas(copy_bits->base.surface_id);
+ canvas = _surfaces_cache[copy_bits->base.surface_id];
canvas->copy_bits(*copy_bits, message->size());
POST_DRAW;
if (copy_bits->base.surface_id == 0) {
@@ -1550,7 +1505,7 @@ void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawFill* fill = (SpiceMsgDisplayDrawFill*)message->data();
- canvas = surfaces_mngr.get_canvas(fill->base.surface_id);
+ canvas = _surfaces_cache[fill->base.surface_id];
DRAW(fill);
}
@@ -1558,7 +1513,7 @@ void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawOpaque* opaque = (SpiceMsgDisplayDrawOpaque*)message->data();
- canvas = surfaces_mngr.get_canvas(opaque->base.surface_id);
+ canvas = _surfaces_cache[opaque->base.surface_id];
DRAW(opaque);
}
@@ -1566,7 +1521,7 @@ void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawCopy* copy = (SpiceMsgDisplayDrawCopy*)message->data();
- canvas = surfaces_mngr.get_canvas(copy->base.surface_id);
+ canvas = _surfaces_cache[copy->base.surface_id];
DRAW(copy);
}
@@ -1574,7 +1529,7 @@ void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawBlend* blend = (SpiceMsgDisplayDrawBlend*)message->data();
- canvas = surfaces_mngr.get_canvas(blend->base.surface_id);
+ canvas = _surfaces_cache[blend->base.surface_id];
DRAW(blend);
}
@@ -1582,7 +1537,7 @@ void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawBlackness* blackness = (SpiceMsgDisplayDrawBlackness*)message->data();
- canvas = surfaces_mngr.get_canvas(blackness->base.surface_id);
+ canvas = _surfaces_cache[blackness->base.surface_id];
DRAW(blackness);
}
@@ -1590,7 +1545,7 @@ void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawWhiteness* whiteness = (SpiceMsgDisplayDrawWhiteness*)message->data();
- canvas = surfaces_mngr.get_canvas(whiteness->base.surface_id);
+ canvas = _surfaces_cache[whiteness->base.surface_id];
DRAW(whiteness);
}
@@ -1598,7 +1553,7 @@ void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawInvers* invers = (SpiceMsgDisplayDrawInvers*)message->data();
- canvas = surfaces_mngr.get_canvas(invers->base.surface_id);
+ canvas = _surfaces_cache[invers->base.surface_id];
DRAW(invers);
}
@@ -1606,7 +1561,7 @@ void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawRop3* rop3 = (SpiceMsgDisplayDrawRop3*)message->data();
- canvas = surfaces_mngr.get_canvas(rop3->base.surface_id);
+ canvas = _surfaces_cache[rop3->base.surface_id];
DRAW(rop3);
}
@@ -1614,7 +1569,7 @@ void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawStroke* stroke = (SpiceMsgDisplayDrawStroke*)message->data();
- canvas = surfaces_mngr.get_canvas(stroke->base.surface_id);
+ canvas = _surfaces_cache[stroke->base.surface_id];
DRAW(stroke);
}
@@ -1622,7 +1577,7 @@ void DisplayChannel::handle_draw_text(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawText* text = (SpiceMsgDisplayDrawText*)message->data();
- canvas = surfaces_mngr.get_canvas(text->base.surface_id);
+ canvas = _surfaces_cache[text->base.surface_id];
DRAW(text);
}
@@ -1630,7 +1585,7 @@ void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawTransparent* transparent = (SpiceMsgDisplayDrawTransparent*)message->data();
- canvas = surfaces_mngr.get_canvas(transparent->base.surface_id);
+ canvas = _surfaces_cache[transparent->base.surface_id];
DRAW(transparent);
}
@@ -1638,7 +1593,7 @@ void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawAlphaBlend* alpha_blend = (SpiceMsgDisplayDrawAlphaBlend*)message->data();
- canvas = surfaces_mngr.get_canvas(alpha_blend->base.surface_id);
+ canvas = _surfaces_cache[alpha_blend->base.surface_id];
DRAW(alpha_blend);
}
diff --git a/client/display_channel.h b/client/display_channel.h
index 647fb66..f30311d 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -80,21 +80,6 @@ private:
DisplayChannel& _channel;
};
-class DisplaySurfacesManger {
-public:
- void add_surface(int surface_id, SpiceCanvas *surface);
- void del_surface(int surface_id);
- void add_canvas(int surface_id, Canvas *canvas);
- void del_canvas(int surface_id);
-
- CSurfaces& get_surfaces();
- bool is_present_canvas(int surface_id);
- Canvas* get_canvas(int surface_id);
-private:
- CSurfaces surfaces;
- CCanvases canvases;
-};
-
class DisplayChannel: public RedChannel, public ScreenLayer {
public:
DisplayChannel(RedClient& client, uint32_t id,
@@ -191,8 +176,9 @@ private:
void reset_screen();
static void set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_rects, SpiceRect*& clip_rects);
+
private:
- DisplaySurfacesManger surfaces_mngr;
+ SurfacesCache _surfaces_cache;
PixmapCache& _pixmap_cache;
PaletteCache _palette_cache;
GlzDecoderWindow& _glz_window;
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 038f784..ed091af 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -34,7 +34,7 @@
GDICanvas::GDICanvas(int width, int height, uint32_t format,
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
: Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
, _pixmap (0)
{
@@ -45,7 +45,7 @@ GDICanvas::GDICanvas(int width, int height, uint32_t format,
&_pixmap->get_mutex(),
format, &pixmap_cache.base,
&palette_cache.base,
- &csurfaces.base,
+ &csurfaces,
&glz_decoder(),
&jpeg_decoder(),
&zlib_decoder()))) {
diff --git a/client/red_gdi_canvas.h b/client/red_gdi_canvas.h
index 76de12b..02b08b0 100644
--- a/client/red_gdi_canvas.h
+++ b/client/red_gdi_canvas.h
@@ -33,7 +33,7 @@ class GDICanvas: public Canvas {
public:
GDICanvas(int width, int height, uint32_t format,
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
virtual ~GDICanvas();
virtual void thread_touch() {}
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 92529ce..abe0855 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -36,7 +36,7 @@
GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win,
RenderType rendertype,
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
: Canvas(pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
, _pixmap (0)
, _textures_lost (false)
@@ -48,7 +48,7 @@ GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win,
SPICE_SURFACE_FMT_DEPTH(format),
&pixmap_cache.base,
&palette_cache.base,
- &csurfaces.base,
+ &csurfaces,
&glz_decoder(),
&jpeg_decoder(),
&zlib_decoder()))) {
diff --git a/client/red_gl_canvas.h b/client/red_gl_canvas.h
index 83e6512..a8c901c 100644
--- a/client/red_gl_canvas.h
+++ b/client/red_gl_canvas.h
@@ -36,7 +36,7 @@ public:
GCanvas(int width, int height, uint32_t format, RedWindow *win,
RenderType rendertype,
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
virtual ~GCanvas();
void set_mode();
diff --git a/client/red_sw_canvas.cpp b/client/red_sw_canvas.cpp
index 782caca..860e2a5 100644
--- a/client/red_sw_canvas.cpp
+++ b/client/red_sw_canvas.cpp
@@ -37,7 +37,7 @@
SCanvas::SCanvas(bool onscreen,
int width, int height, uint32_t format, RedWindow *win,
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces)
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache& csurfaces)
: Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
, _pixmap (0)
{
@@ -50,7 +50,7 @@ SCanvas::SCanvas(bool onscreen,
_pixmap->get_stride(),
&pixmap_cache.base,
&palette_cache.base,
- &csurfaces.base,
+ &csurfaces,
&glz_decoder(),
&jpeg_decoder(),
&zlib_decoder());
@@ -58,7 +58,7 @@ SCanvas::SCanvas(bool onscreen,
_canvas = canvas_create(width, height, format,
&pixmap_cache.base,
&palette_cache.base,
- &csurfaces.base,
+ &csurfaces,
&glz_decoder(),
&jpeg_decoder(),
&zlib_decoder());
diff --git a/client/red_sw_canvas.h b/client/red_sw_canvas.h
index 2f807c7..807c128 100644
--- a/client/red_sw_canvas.h
+++ b/client/red_sw_canvas.h
@@ -33,7 +33,7 @@ public:
SCanvas(bool onscreen,
int width, int height, uint32_t format, RedWindow *win,
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
- GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
+ GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
virtual ~SCanvas();
virtual void thread_touch() {}
commit 76966571748efa32a706d21830349ab2431aa4e6
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 14:23:36 2011 +0300
server: turn spice_server_migrate_start into a valid call
We will add a qemu call to spice_server_migrate_start when migration starts.
For now, it does nothing, but we may need this notification in the future.
(cherry picked from commit b8213167717979e6f2fb52646e43eb458634e6a1 branch 0.8)
diff --git a/server/reds.c b/server/reds.c
index 045e275..acd8495 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -4019,16 +4019,10 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
SPICE_GNUC_VISIBLE int spice_server_migrate_start(SpiceServer *s)
{
ASSERT(reds == s);
-
- if (1) {
- /* seamless doesn't work, fixing needs protocol change. */
- return -1;
- }
-
+ red_printf("");
if (!reds->mig_spice) {
return -1;
}
- reds_mig_started();
return 0;
}
commit f683815ad53cc39f485ddac9770e23282ca5c340
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Oct 9 13:13:41 2011 +0200
server: handling semi-seamless migration in the target side
(1) not sending anything to a migrated client till we recieve SPICE_MSGC_MIGRATE_END
(2) start a new client migration (handle client_migrate_info) only after SPICE_MSGC_MIGRATE_END
from the previous migration was received for this client
(3) use the correct ticket
Note: we assume the same channles are linked before and ater migration. i.e.,
SPICE_MSGC_MAIN_ATTACH_CHANNELS is not sent from the clients.
diff --git a/server/main_channel.c b/server/main_channel.c
index ffc593d..b2439b2 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -129,6 +129,8 @@ struct MainChannelClient {
#endif
int mig_wait_connect;
int mig_connect_ok;
+ int mig_wait_prev_complete;
+ int init_sent;
};
enum NetTestStage {
@@ -138,6 +140,9 @@ enum NetTestStage {
NET_TEST_STAGE_RATE,
};
+static void main_channel_release_pipe_item(RedChannelClient *rcc,
+ PipeItem *base, int item_pushed);
+
int main_channel_is_connected(MainChannel *main_chan)
{
return red_channel_is_connected(&main_chan->base);
@@ -289,6 +294,11 @@ static PipeItem *main_multi_media_time_item_new(
static void main_channel_push_channels(MainChannelClient *mcc)
{
+ if (red_client_during_migrate_at_target(mcc->base.client)) {
+ red_printf("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
+ "during migration");
+ return;
+ }
red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_CHANNELS_LIST);
}
@@ -451,7 +461,7 @@ static uint64_t main_channel_handle_migrate_data(RedChannelClient *base,
return TRUE;
}
-void main_channel_push_init(MainChannelClient *mcc, int connection_id,
+void main_channel_push_init(MainChannelClient *mcc,
int display_channels_hint, int current_mouse_mode,
int is_client_mouse_allowed, int multi_media_time,
int ram_hint)
@@ -459,7 +469,7 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id,
PipeItem *item;
item = main_init_item_new(mcc,
- connection_id, display_channels_hint, current_mouse_mode,
+ mcc->connection_id, display_channels_hint, current_mouse_mode,
is_client_mouse_allowed, multi_media_time, ram_hint);
red_channel_client_pipe_add_push(&mcc->base, item);
}
@@ -612,6 +622,13 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+ if (!mcc->init_sent && base->type != SPICE_MSG_MAIN_INIT) {
+ red_printf("Init msg for client %p was not sent yet "
+ "(client is probably during migration). Ignoring msg type %d",
+ rcc->client, base->type);
+ main_channel_release_pipe_item(rcc, base, FALSE);
+ return;
+ }
red_channel_client_init_send_data(rcc, base->type, base);
switch (base->type) {
case SPICE_MSG_MAIN_CHANNELS_LIST:
@@ -646,6 +663,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
mcc->ping_id);
break;
case SPICE_MSG_MAIN_INIT:
+ mcc->init_sent = TRUE;
main_channel_marshall_init(m,
SPICE_CONTAINEROF(base, InitPipeItem, base));
break;
@@ -710,6 +728,25 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int su
}
}
+void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
+{
+ if (!red_client_during_migrate_at_target(mcc->base.client)) {
+ red_printf("unexpected SPICE_MSGC_MIGRATE_END");
+ return;
+ }
+ if (!red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+ red_printf("unexpected SPICE_MSGC_MIGRATE_END, "
+ "client does not support semi-seamless migration");
+ return;
+ }
+ red_client_migrate_complete(mcc->base.client);
+ if (mcc->mig_wait_prev_complete) {
+ red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+ mcc->mig_wait_connect = TRUE;
+ mcc->mig_wait_prev_complete = FALSE;
+ }
+}
static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
{
MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
@@ -797,6 +834,9 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
}
case SPICE_MSGC_DISCONNECTING:
break;
+ case SPICE_MSGC_MAIN_MIGRATE_END:
+ main_channel_client_handle_migrate_end(mcc);
+ break;
default:
red_printf("unexpected type %d", type);
}
@@ -989,8 +1029,13 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
if (red_channel_client_test_remote_cap(&mcc->base,
SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
- red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
- mcc->mig_wait_connect = TRUE;
+ if (red_client_during_migrate_at_target(mcc->base.client)) {
+ red_printf("client %p: wait till previous migration completes", mcc->base.client);
+ mcc->mig_wait_prev_complete = TRUE;
+ } else {
+ red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+ mcc->mig_wait_connect = TRUE;
+ }
mcc->mig_connect_ok = FALSE;
main_channel->num_clients_mig_wait++;
}
@@ -1011,6 +1056,7 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan)
mcc->mig_wait_connect = FALSE;
mcc->mig_connect_ok = FALSE;
}
+ mcc->mig_wait_prev_complete = FALSE;
}
main_chan->num_clients_mig_wait = 0;
}
diff --git a/server/main_channel.h b/server/main_channel.h
index d97857d..c5d407e 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -78,7 +78,7 @@ void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t
void main_channel_client_start_net_test(MainChannelClient *mcc);
// TODO: huge. Consider making a reds_* interface for these functions
// and calling from main.
-void main_channel_push_init(MainChannelClient *mcc, int connection_id, int display_channels_hint,
+void main_channel_push_init(MainChannelClient *mcc, int display_channels_hint,
int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
int ram_hint);
void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
diff --git a/server/red_channel.c b/server/red_channel.c
index 51415cb..2ce0094 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -1215,7 +1215,7 @@ void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc,
* pretty tied together.
*/
-RedClient *red_client_new()
+RedClient *red_client_new(int migrated)
{
RedClient *client;
@@ -1223,6 +1223,7 @@ RedClient *red_client_new()
ring_init(&client->channels);
pthread_mutex_init(&client->lock, NULL);
client->thread_id = pthread_self();
+ client->migrated = migrated;
return client;
}
@@ -1286,6 +1287,18 @@ void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
client->mcc = mcc;
}
+void red_client_migrate_complete(RedClient *client)
+{
+ ASSERT(client->migrated);
+ client->migrated = FALSE;
+ reds_on_client_migrate_complete(client);
+}
+
+int red_client_during_migrate_at_target(RedClient *client)
+{
+ return client->migrated;
+}
+
/*
* Functions to push the same item to multiple pipes.
*/
diff --git a/server/red_channel.h b/server/red_channel.h
index e30401c..cce6965 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -450,13 +450,15 @@ struct RedClient {
pthread_t thread_id;
int disconnecting;
-
+ int migrated;
};
-RedClient *red_client_new(void);
+RedClient *red_client_new(int migrated);
MainChannelClient *red_client_get_main(RedClient *client);
// main should be set once before all the other channels are created
void red_client_set_main(RedClient *client, MainChannelClient *mcc);
+void red_client_migrate_complete(RedClient *client);
+int red_client_during_migrate_at_target(RedClient *client);
void red_client_migrate(RedClient *client);
// disconnects all the client's channels (should be called from the client's thread)
diff --git a/server/reds.c b/server/reds.c
index 43fdc2a..045e275 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -191,6 +191,18 @@ typedef struct RedsStatValue {
#endif
+typedef struct RedsMigPendingLink {
+ RingItem ring_link; // list of links that belongs to the same client
+ SpiceLinkMess *link_msg;
+ RedsStream *stream;
+} RedsMigPendingLink;
+
+typedef struct RedsMigTargetClient {
+ RingItem link;
+ RedClient *client;
+ Ring pending_links;
+} RedsMigTargetClient;
+
typedef struct RedsState {
int listen_socket;
int secure_listen_socket;
@@ -206,7 +218,8 @@ typedef struct RedsState {
int mig_wait_disconnect;
int mig_inprogress;
int expect_migrate;
- int mig_target;
+ Ring mig_target_clients;
+ int num_mig_target_clients;
RedsMigSpice *mig_spice;
int num_of_channels;
Ring channels;
@@ -219,7 +232,6 @@ typedef struct RedsState {
SpiceTimer *vdi_port_write_timer;
int vdi_port_write_timer_started;
- TicketAuthentication taTicket;
SSL_CTX *ctx;
#ifdef RED_STATISTICS
@@ -295,6 +307,8 @@ struct ChannelSecurityOptions {
};
static void migrate_timeout(void *opaque);
+static RedsMigTargetClient* reds_mig_target_client_find(RedClient *client);
+static void reds_mig_target_client_free(RedsMigTargetClient *mig_client);
static ChannelSecurityOptions *channels_security = NULL;
static int default_channel_security =
@@ -593,6 +607,8 @@ static int reds_main_channel_connected(void)
void reds_client_disconnect(RedClient *client)
{
+ RedsMigTargetClient *mig_client;
+
if (!client || client->disconnecting) {
return;
}
@@ -607,6 +623,10 @@ void reds_client_disconnect(RedClient *client)
// TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
// We shouldn't initialize the agent when there are still clients connected
+ mig_client = reds_mig_target_client_find(client);
+ if (mig_client) {
+ reds_mig_target_client_free(mig_client);
+ }
ring_remove(&client->link);
reds->num_clients--;
red_client_destroy(client);
@@ -693,14 +713,14 @@ static void reds_update_mouse_mode(void)
static void reds_agent_remove(void)
{
- if (!reds->mig_target) {
- reds_reset_vdp();
- }
+ // TODO: agent is broken with multiple clients. also need to figure out what to do when
+ // part of the clients are during target migration.
+ reds_reset_vdp();
vdagent = NULL;
reds_update_mouse_mode();
- if (reds_main_channel_connected() && !reds->mig_target) {
+ if (reds_main_channel_connected()) {
main_channel_push_agent_disconnected(reds->main_channel);
}
}
@@ -740,7 +760,7 @@ static int write_to_vdi_port(void)
int total = 0;
int n;
- if (!vdagent || reds->mig_target) {
+ if (!vdagent) {
return 0;
}
@@ -844,8 +864,8 @@ static int read_from_vdi_port(void)
}
inside_call = 1;
- if (reds->mig_target || !vdagent) {
- // discard data only if we are migrating or vdagent has not been
+ if (!vdagent) {
+ // discard data only if we are migrating (?) or vdagent has not been
// initialized.
inside_call = 0;
return 0;
@@ -931,7 +951,7 @@ void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state)
if (!inputs_inited()) {
return;
}
- if (reds->mig_target || !(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
+ if (!(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
reds->pending_mouse_event = TRUE;
vdi_port_write_timer_start();
return;
@@ -1346,7 +1366,6 @@ void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end)
reds_main_channel_restore_vdi_wqueue(data, pos, end);
ASSERT(state->num_client_tokens + state->num_tokens == REDS_AGENT_WINDOW_SIZE);
- reds->mig_target = FALSE;
while (write_to_vdi_port() || read_from_vdi_port());
}
@@ -1486,6 +1505,75 @@ int reds_expects_link_id(uint32_t connection_id)
return 1;
}
+static void reds_mig_target_client_add(RedClient *client)
+{
+ RedsMigTargetClient *mig_client;
+
+ ASSERT(reds);
+ red_printf("");
+ mig_client = spice_malloc0(sizeof(RedsMigTargetClient));
+ mig_client->client = client;
+ ring_init(&mig_client->pending_links);
+ ring_add(&reds->mig_target_clients, &mig_client->link);
+ reds->num_mig_target_clients++;
+
+}
+
+static RedsMigTargetClient* reds_mig_target_client_find(RedClient *client)
+{
+ RingItem *item;
+
+ RING_FOREACH(item, &reds->mig_target_clients) {
+ RedsMigTargetClient *mig_client;
+
+ mig_client = SPICE_CONTAINEROF(item, RedsMigTargetClient, link);
+ if (mig_client->client == client) {
+ return mig_client;
+ }
+ }
+ return NULL;
+}
+
+static void reds_mig_target_client_add_pending_link(RedsMigTargetClient *client,
+ SpiceLinkMess *link_msg,
+ RedsStream *stream)
+{
+ RedsMigPendingLink *mig_link;
+
+ ASSERT(reds);
+ ASSERT(client);
+ mig_link = spice_malloc0(sizeof(RedsMigPendingLink));
+ mig_link->link_msg = link_msg;
+ mig_link->stream = stream;
+
+ ring_add(&client->pending_links, &mig_link->ring_link);
+}
+
+static void reds_mig_target_client_free(RedsMigTargetClient *mig_client)
+{
+ RingItem *now, *next;
+
+ ring_remove(&mig_client->link);
+ reds->num_mig_target_clients--;
+
+ RING_FOREACH_SAFE(now, next, &mig_client->pending_links) {
+ RedsMigPendingLink *mig_link = SPICE_CONTAINEROF(now, RedsMigPendingLink, ring_link);
+ ring_remove(now);
+ free(mig_link);
+ }
+ free(mig_client);
+}
+
+static void reds_mig_target_client_disconnect_all()
+{
+ RingItem *now, *next;
+
+ RING_FOREACH_SAFE(now, next, &reds->mig_target_clients) {
+ RedsMigTargetClient *mig_client = SPICE_CONTAINEROF(now, RedsMigTargetClient, link);
+ reds_client_disconnect(mig_client->client);
+ }
+}
+
// TODO: now that main is a separate channel this should
// actually be joined with reds_handle_other_links, become reds_handle_link
static void reds_handle_main_link(RedLinkInfo *link)
@@ -1496,6 +1584,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
uint32_t *caps;
uint32_t connection_id;
MainChannelClient *mcc;
+ int mig_target = FALSE;
red_printf("");
ASSERT(reds->main_channel);
@@ -1509,18 +1598,13 @@ static void reds_handle_main_link(RedLinkInfo *link)
reds_send_link_result(link, SPICE_LINK_ERR_OK);
while((connection_id = rand()) == 0);
reds->agent_state.num_tokens = 0;
- memcpy(&(reds->taTicket), &taTicket, sizeof(reds->taTicket));
- reds->mig_target = FALSE;
+ mig_target = FALSE;
} else {
- // migration - check if this is one of the expected connection_id's
- if (!reds_expects_link_id(link_mess->connection_id)) {
- reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
- reds_link_free(link);
- return;
- }
+ // TODO: make sure link_mess->connection_id is the same
+ // connection id the migration src had (use vmstate to store the connection id)
reds_send_link_result(link, SPICE_LINK_ERR_OK);
connection_id = link_mess->connection_id;
- reds->mig_target = TRUE;
+ mig_target = TRUE;
}
reds->mig_inprogress = FALSE;
@@ -1534,11 +1618,11 @@ static void reds_handle_main_link(RedLinkInfo *link)
link->link_mess = NULL;
reds_link_free(link);
caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
- client = red_client_new();
+ client = red_client_new(mig_target);
ring_add(&reds->clients, &client->link);
reds->num_clients++;
mcc = main_channel_link(reds->main_channel, client,
- stream, connection_id, reds->mig_target,
+ stream, connection_id, mig_target,
link_mess->num_common_caps,
link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
@@ -1551,9 +1635,10 @@ static void reds_handle_main_link(RedLinkInfo *link)
reds->agent_state.plug_generation++;
}
- if (!reds->mig_target) {
- reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
- main_channel_push_init(mcc, connection_id, red_dispatcher_count(),
+ reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
+
+ if (!mig_target) {
+ main_channel_push_init(mcc, red_dispatcher_count(),
reds->mouse_mode, reds->is_client_mouse_allowed,
reds_get_mm_time() - MM_TIME_DELTA,
red_dispatcher_qxl_ram_size());
@@ -1561,6 +1646,8 @@ static void reds_handle_main_link(RedLinkInfo *link)
main_channel_client_start_net_test(mcc);
/* Now that we have a client, forward any pending agent data */
while (read_from_vdi_port());
+ } else {
+ reds_mig_target_client_add(client);
}
}
@@ -1614,7 +1701,8 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client,
}
caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset);
- channel->client_cbs.connect(channel, client, stream, reds->mig_target,
+ channel->client_cbs.connect(channel, client, stream,
+ red_client_during_migrate_at_target(client),
link_msg->num_common_caps,
link_msg->num_common_caps ? caps : NULL,
link_msg->num_channel_caps,
@@ -1622,11 +1710,55 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client,
caps + link_msg->num_common_caps : NULL);
}
+void reds_on_client_migrate_complete(RedClient *client)
+{
+ RedsMigTargetClient *mig_client;
+ MainChannelClient *mcc;
+ RingItem *item;
+
+ red_printf("%p", client);
+ mcc = red_client_get_main(client);
+ mig_client = reds_mig_target_client_find(client);
+ if (!mig_client) {
+ red_printf("Error: mig target client was not found");
+ return;
+ }
+
+ // TODO: not doing net test. consider doing it on client_migrate_info
+ main_channel_push_init(mcc, red_dispatcher_count(),
+ reds->mouse_mode, reds->is_client_mouse_allowed,
+ reds_get_mm_time() - MM_TIME_DELTA,
+ red_dispatcher_qxl_ram_size());
+
+ RING_FOREACH(item, &mig_client->pending_links) {
+ RedsMigPendingLink *mig_link;
+ RedChannel *channel;
+
+ mig_link = SPICE_CONTAINEROF(item, RedsMigPendingLink, ring_link);
+ channel = reds_find_channel(mig_link->link_msg->channel_type,
+ mig_link->link_msg->channel_id);
+ if (!channel) {
+ red_printf("warning: client %p channel (%d, %d) (type, id) wasn't found",
+ client,
+ mig_link->link_msg->channel_type,
+ mig_link->link_msg->channel_id);
+ continue;
+ }
+ reds_channel_do_link(channel, client, mig_link->link_msg, mig_link->stream);
+ }
+
+ reds_mig_target_client_free(mig_client);
+
+ /* Now that we have a client, forward any pending agent data */
+ while (read_from_vdi_port());
+}
+
static void reds_handle_other_links(RedLinkInfo *link)
{
RedChannel *channel;
RedClient *client = NULL;
SpiceLinkMess *link_mess;
+ RedsMigTargetClient *mig_client;
link_mess = link->link_mess;
if (reds->main_channel) {
@@ -1654,8 +1786,16 @@ static void reds_handle_other_links(RedLinkInfo *link)
reds_send_link_result(link, SPICE_LINK_ERR_OK);
reds_show_new_channel(link, link_mess->connection_id);
reds_stream_remove_watch(link->stream);
- reds_channel_do_link(channel, client, link_mess, link->stream);
- free(link_mess);
+
+ mig_client = reds_mig_target_client_find(client);
+ if (red_client_during_migrate_at_target(client)) {
+ ASSERT(mig_client);
+ reds_mig_target_client_add_pending_link(mig_client, link_mess, link->stream);
+ } else {
+ ASSERT(!mig_client);
+ reds_channel_do_link(channel, client, link_mess, link->stream);
+ free(link_mess);
+ }
link->stream = NULL;
link->link_mess = NULL;
reds_link_free(link);
@@ -1683,10 +1823,9 @@ static void reds_handle_ticket(void *opaque)
(unsigned char *)password, link->tiTicketing.rsa, RSA_PKCS1_OAEP_PADDING);
if (ticketing_enabled) {
- int expired = !link->link_mess->connection_id && taTicket.expiration_time < ltime;
- char *actual_sever_pass = link->link_mess->connection_id ? reds->taTicket.password :
- taTicket.password;
- if (strlen(actual_sever_pass) == 0) {
+ int expired = taTicket.expiration_time < ltime;
+
+ if (strlen(taTicket.password) == 0) {
reds_send_link_result(link, SPICE_LINK_ERR_PERMISSION_DENIED);
red_printf("Ticketing is enabled, but no password is set. "
"please set a ticket first");
@@ -1694,7 +1833,7 @@ static void reds_handle_ticket(void *opaque)
return;
}
- if (expired || strncmp(password, actual_sever_pass, SPICE_MAX_PASSWORD_LENGTH) != 0) {
+ if (expired || strncmp(password, taTicket.password, SPICE_MAX_PASSWORD_LENGTH) != 0) {
reds_send_link_result(link, SPICE_LINK_ERR_PERMISSION_DENIED);
reds_link_free(link);
return;
@@ -3049,7 +3188,10 @@ static void migrate_timeout(void *opaque)
red_printf("");
ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
if (reds->mig_wait_connect) {
+ /* we will fall back to the switch host scheme when migration completes */
main_channel_migrate_cancel_wait(reds->main_channel);
+ /* in case part of the client haven't yet completed the previous migration, disconnect them */
+ reds_mig_target_client_disconnect_all();
reds_mig_cleanup();
} else {
reds_mig_disconnect();
@@ -3108,9 +3250,7 @@ static void attach_to_red_agent(SpiceCharDeviceInstance *sin)
state->read_filter.discard_all = FALSE;
reds->agent_state.plug_generation++;
- if (!reds->mig_target) {
- main_channel_push_agent_connected(reds->main_channel);
- }
+ main_channel_push_agent_connected(reds->main_channel);
}
SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin)
@@ -3413,6 +3553,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
reds->num_clients = 0;
main_dispatcher_init(core);
ring_init(&reds->channels);
+ ring_init(&reds->mig_target_clients);
if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) {
red_error("migration timer create failed");
@@ -3847,7 +3988,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
reds->expect_migrate = TRUE;
-
+ /* main channel will take care of clients that are still during migration (at target)*/
if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
reds_mig_started();
} else {
diff --git a/server/reds.h b/server/reds.h
index 9feb9ab..450825d 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -147,6 +147,6 @@ void reds_on_main_migrate_connected(void); //should be called when all the clien
// are connected to the target
void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
void reds_on_main_mouse_mode_request(void *message, size_t size);
-
+void reds_on_client_migrate_complete(RedClient *client);
#endif
commit c88e927fc734fdb8240d925eb9e6a87b203c5bb3
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Thu Oct 6 12:34:22 2011 +0200
server: move the linking of channels to a separate routine
diff --git a/server/reds.c b/server/reds.c
index 8799e00..43fdc2a 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1597,13 +1597,36 @@ static void openssl_init(RedLinkInfo *link)
BN_set_word(link->tiTicketing.bn, f4);
}
+static void reds_channel_do_link(RedChannel *channel, RedClient *client,
+ SpiceLinkMess *link_msg,
+ RedsStream *stream)
+{
+ uint32_t *caps;
+
+ ASSERT(channel);
+ ASSERT(link_msg);
+ ASSERT(stream);
+
+ if (link_msg->channel_type == SPICE_CHANNEL_INPUTS && !stream->ssl) {
+ char *mess = "keyboard channel is insecure";
+ const int mess_len = strlen(mess);
+ main_channel_push_notify(reds->main_channel, (uint8_t*)mess, mess_len);
+ }
+
+ caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset);
+ channel->client_cbs.connect(channel, client, stream, reds->mig_target,
+ link_msg->num_common_caps,
+ link_msg->num_common_caps ? caps : NULL,
+ link_msg->num_channel_caps,
+ link_msg->num_channel_caps ?
+ caps + link_msg->num_common_caps : NULL);
+}
+
static void reds_handle_other_links(RedLinkInfo *link)
{
RedChannel *channel;
RedClient *client = NULL;
- RedsStream *stream;
SpiceLinkMess *link_mess;
- uint32_t *caps;
link_mess = link->link_mess;
if (reds->main_channel) {
@@ -1630,24 +1653,12 @@ static void reds_handle_other_links(RedLinkInfo *link)
reds_send_link_result(link, SPICE_LINK_ERR_OK);
reds_show_new_channel(link, link_mess->connection_id);
- if (link_mess->channel_type == SPICE_CHANNEL_INPUTS && !link->stream->ssl) {
- char *mess = "keyboard channel is insecure";
- const int mess_len = strlen(mess);
- main_channel_push_notify(reds->main_channel, (uint8_t*)mess, mess_len);
- }
- stream = link->stream;
- reds_stream_remove_watch(stream);
+ reds_stream_remove_watch(link->stream);
+ reds_channel_do_link(channel, client, link_mess, link->stream);
+ free(link_mess);
link->stream = NULL;
link->link_mess = NULL;
reds_link_free(link);
- caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
- channel->client_cbs.connect(channel, client, stream, reds->mig_target,
- link_mess->num_common_caps,
- link_mess->num_common_caps ? caps : NULL,
- link_mess->num_channel_caps,
- link_mess->num_channel_caps ?
- caps + link_mess->num_common_caps : NULL);
- free(link_mess);
}
static void reds_handle_link(RedLinkInfo *link)
commit e62521ca51dae8c6d943c345e9ff2124727eacaa
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 13:09:52 2011 +0300
server: handle spice_server_migrate_end
If the migration has completed successfully:
(1) send MSG_MAIN_MIGRATE_END to the clients that are connected to the target
(2) send MSG_MAIN_SWITCH_HOST to all the other clients
If the migration failed, send MSG_MAIN_MIGRATE_CANCEL to clients that are
connected to the target.
(cherry picked from commit 4b82580fc36228af13db4ac3c403753d6b5c40b5 branch 0.8;
Was modified to support multiple clients, and the separation of main_channel from reds)
Conflicts:
server/reds.c
diff --git a/server/main_channel.c b/server/main_channel.c
index 5b16b30..ffc593d 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -548,11 +548,6 @@ static void main_channel_marshall_migrate(SpiceMarshaller *m)
spice_marshall_msg_migrate(m, &migrate);
}
-void main_channel_push_migrate_cancel(MainChannel *main_chan)
-{
- red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
-}
-
void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
{
MultiMediaTimePipeItem info = {
@@ -563,33 +558,43 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
main_multi_media_time_item_new, &info);
}
-static PipeItem *main_migrate_switch_item_new(RedChannelClient *rcc,
- void *data, int num)
+static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
{
- RefsPipeItem *item = spice_malloc(sizeof(*item));
-
- item->refs = data;
- red_channel_pipe_item_init(rcc->channel, &item->base,
- SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
- return &item->base;
+ ASSERT(mig_target);
+ free(main_channel->mig_target.host);
+ main_channel->mig_target.host = strdup(mig_target->host);
+ free(main_channel->mig_target.cert_subject);
+ if (mig_target->cert_subject) {
+ main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
+ }
+ main_channel->mig_target.port = mig_target->port;
+ main_channel->mig_target.sport = mig_target->sport;
}
-void main_channel_push_migrate_switch(MainChannel *main_chan)
+void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
{
- int *refs = spice_malloc0(sizeof(int));
-
- *refs = main_chan->base.clients_num;
- red_channel_pipes_new_add_push(&main_chan->base,
- main_migrate_switch_item_new, (void*)refs);
+ main_channel_fill_mig_target(main_chan, mig_target);
+ red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
}
-static void main_channel_marshall_migrate_switch(SpiceMarshaller *m)
+static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc)
{
SpiceMsgMainMigrationSwitchHost migrate;
+ MainChannel *main_ch;
red_printf("");
-
- reds_fill_mig_switch(&migrate);
+ main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+ migrate.port = main_ch->mig_target.port;
+ migrate.sport = main_ch->mig_target.sport;
+ migrate.host_size = strlen(main_ch->mig_target.host) + 1;
+ migrate.host_data = (uint8_t *)main_ch->mig_target.host;
+ if (main_ch->mig_target.cert_subject) {
+ migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
+ migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
+ } else {
+ migrate.cert_subject_size = 0;
+ migrate.cert_subject_data = NULL;
+ }
spice_marshall_msg_main_migrate_switch_host(m, &migrate);
}
@@ -659,7 +664,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
break;
case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST:
- main_channel_marshall_migrate_switch(m);
+ main_channel_marshall_migrate_switch(m, rcc);
break;
};
red_channel_client_begin_send_message(rcc);
@@ -679,14 +684,6 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
}
break;
}
- case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST: {
- RefsPipeItem *data = (RefsPipeItem*)base;
- if (!--*(data->refs)) {
- free(data->refs);
- reds_mig_release();
- }
- break;
- }
default:
break;
}
@@ -985,16 +982,7 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
{
RingItem *client_link;
- ASSERT(mig_target);
- free(main_channel->mig_target.host);
- main_channel->mig_target.host = strdup(mig_target->host);
- free(main_channel->mig_target.cert_subject);
- if (mig_target->cert_subject) {
- main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
- }
- main_channel->mig_target.port = mig_target->port;
- main_channel->mig_target.sport = mig_target->sport;
-
+ main_channel_fill_mig_target(main_channel, mig_target);
main_channel->num_clients_mig_wait = 0;
RING_FOREACH(client_link, &main_channel->base.clients) {
@@ -1026,3 +1014,44 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan)
}
main_chan->num_clients_mig_wait = 0;
}
+
+int main_channel_migrate_complete(MainChannel *main_chan, int success)
+{
+ RingItem *client_link;
+ int semi_seamless_count = 0;
+
+ red_printf("");
+
+ if (ring_is_empty(&main_chan->base.clients)) {
+ red_printf("no peer connected");
+ return 0;
+ }
+
+ RING_FOREACH(client_link, &main_chan->base.clients) {
+ MainChannelClient *mcc;
+ int semi_seamless_support;
+
+ mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
+ semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
+ if (semi_seamless_support && mcc->mig_connect_ok) {
+ if (success) {
+ red_printf("client %p MIGRATE_END", mcc->base.client);
+ red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
+ semi_seamless_count++;
+ } else {
+ red_printf("client %p MIGRATE_CANCEL", mcc->base.client);
+ red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+ }
+ } else {
+ if (success) {
+ red_printf("client %p SWITCH_HOST", mcc->base.client);
+ red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
+ }
+ }
+ mcc->mig_connect_ok = FALSE;
+ mcc->mig_wait_connect = FALSE;
+ }
+ return semi_seamless_count;
+}
+
diff --git a/server/main_channel.h b/server/main_channel.h
index f3702e7..d97857d 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -83,8 +83,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ
int ram_hint);
void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
void main_channel_push_migrate(MainChannel *main_chan);
-void main_channel_push_migrate_switch(MainChannel *main_chan);
-void main_channel_push_migrate_cancel(MainChannel *main_chan);
void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
@@ -95,10 +93,14 @@ uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
int main_channel_is_connected(MainChannel *main_chan);
RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
+/* switch host migration */
+void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
+
/* semi seamless migration */
/* returns the number of clients that we are waiting for their connection */
int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target);
void main_channel_migrate_cancel_wait(MainChannel *main_chan);
-void main_channel_migrate_complete(MainChannel *main_chan, int success);
+/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
+int main_channel_migrate_complete(MainChannel *main_chan, int success);
#endif
diff --git a/server/reds.c b/server/reds.c
index bc93e4a..8799e00 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -205,6 +205,7 @@ typedef struct RedsState {
int mig_wait_connect;
int mig_wait_disconnect;
int mig_inprogress;
+ int expect_migrate;
int mig_target;
RedsMigSpice *mig_spice;
int num_of_channels;
@@ -2983,7 +2984,7 @@ typedef struct RedsMigCertPubKeyInfo {
uint32_t len;
} RedsMigCertPubKeyInfo;
-void reds_mig_release(void)
+static void reds_mig_release(void)
{
if (reds->mig_spice) {
free(reds->mig_spice->cert_subject);
@@ -2998,29 +2999,14 @@ static void reds_mig_started(void)
red_printf("");
ASSERT(reds->mig_spice);
- reds->mig_wait_connect = TRUE;
reds->mig_inprogress = TRUE;
-
- if (reds->listen_watch != NULL) {
- core->watch_update_mask(reds->listen_watch, 0);
- }
-
- if (reds->secure_listen_watch != NULL) {
- core->watch_update_mask(reds->secure_listen_watch, 0);
- }
+ reds->mig_wait_connect = TRUE;
core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
}
static void reds_mig_finished(int completed)
{
red_printf("");
- if (reds->listen_watch != NULL) {
- core->watch_update_mask(reds->listen_watch, SPICE_WATCH_EVENT_READ);
- }
-
- if (reds->secure_listen_watch != NULL) {
- core->watch_update_mask(reds->secure_listen_watch, SPICE_WATCH_EVENT_READ);
- }
if (!reds_main_channel_connected()) {
red_printf("no peer connected");
@@ -3028,28 +3014,13 @@ static void reds_mig_finished(int completed)
}
reds->mig_inprogress = TRUE;
- if (completed) {
- RingItem *link, *next;
-
+ if (main_channel_migrate_complete(reds->main_channel, completed)) {
reds->mig_wait_disconnect = TRUE;
core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
-
- // TODO: so now that main channel is separate, how exactly does migration of it work?
- // - it can have an empty migrate - that seems ok
- // - I can try to fill it's migrate, then move stuff from reds.c there, but a lot of data
- // is in reds state right now.
- // currently the migrate callback of main_channel does nothing
- main_channel_push_migrate(reds->main_channel);
-
- RING_FOREACH_SAFE(link, next, &reds->clients) {
- red_client_migrate(SPICE_CONTAINEROF(link, RedClient, link));
- }
} else {
- main_channel_push_migrate_cancel(reds->main_channel);
- // TODO: all the seemless migration is broken. Before MC we waited for disconection of one client,
- // no we need to wait to all the clients (see mig_timer)?
reds_mig_cleanup();
}
+ reds_mig_release();
}
static void reds_mig_switch(void)
@@ -3058,30 +3029,8 @@ static void reds_mig_switch(void)
red_printf("warning: reds_mig_switch called without migrate_info set");
return;
}
- main_channel_push_migrate_switch(reds->main_channel);
-}
-
-void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate)
-{
- RedsMigSpice *s = reds->mig_spice;
-
- if (s == NULL) {
- red_printf(
- "error: reds_fill_mig_switch called without migrate info set");
- bzero(migrate, sizeof(*migrate));
- return;
- }
- migrate->port = s->port;
- migrate->sport = s->sport;
- migrate->host_size = strlen(s->host) + 1;
- migrate->host_data = (uint8_t *)s->host;
- if (s->cert_subject) {
- migrate->cert_subject_size = strlen(s->cert_subject) + 1;
- migrate->cert_subject_data = (uint8_t *)s->cert_subject;
- } else {
- migrate->cert_subject_size = 0;
- migrate->cert_subject_data = NULL;
- }
+ main_channel_migrate_switch(reds->main_channel, reds->mig_spice);
+ reds_mig_release();
}
static void migrate_timeout(void *opaque)
@@ -3873,6 +3822,11 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
ASSERT(migration_interface);
ASSERT(reds == s);
+ if (reds->expect_migrate) {
+ red_printf("warning: consecutive calls without migration. Canceling previous call");
+ main_channel_migrate_complete(reds->main_channel, FALSE);
+ }
+
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
@@ -3880,10 +3834,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
return -1;
}
+ reds->expect_migrate = TRUE;
+
+
if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
- reds->mig_wait_connect = TRUE;
reds_mig_started();
} else {
+ if (reds->num_clients == 0) {
+ reds_mig_release();
+ red_printf("no client connected");
+ }
sif->migrate_connect_complete(migration_interface);
}
@@ -3894,13 +3854,13 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
int port, int secure_port,
const char* cert_subject)
{
+ red_printf("");
ASSERT(!migration_interface);
ASSERT(reds == s);
if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
return -1;
}
-
return 0;
}
@@ -3937,20 +3897,40 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_client_state(SpiceServer *s)
SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
{
SpiceMigrateInterface *sif;
+ int ret = 0;
+
+ red_printf("");
+
ASSERT(migration_interface);
ASSERT(reds == s);
- reds_mig_finished(completed);
+
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+ if (!reds->expect_migrate && reds->num_clients) {
+ red_printf("spice_server_migrate_info was not called, disconnecting clients");
+ reds_disconnect();
+ ret = -1;
+ goto complete;
+ }
+
+ reds->expect_migrate = FALSE;
+ reds_mig_finished(completed);
+ ret = 0;
+complete:
if (sif->migrate_end_complete) {
sif->migrate_end_complete(migration_interface);
}
- return 0;
+ return ret;
}
/* interface for switch-host migration */
SPICE_GNUC_VISIBLE int spice_server_migrate_switch(SpiceServer *s)
{
ASSERT(reds == s);
+ red_printf("");
+ if (!reds->num_clients) {
+ return 0;
+ }
+ reds->expect_migrate = FALSE;
reds_mig_switch();
return 0;
}
diff --git a/server/reds.h b/server/reds.h
index 13fec42..9feb9ab 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -133,8 +133,6 @@ void reds_client_disconnect(RedClient *client);
typedef struct MainMigrateData MainMigrateData;
void reds_marshall_migrate_data_item(SpiceMarshaller *m, MainMigrateData *data);
void reds_fill_channels(SpiceMsgChannels *channels_info);
-void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate);
-void reds_mig_release(void);
int reds_num_of_channels(void);
int reds_num_of_clients(void);
#ifdef RED_STATISTICS
commit fdb464bb052cab4a822a9bdc0835e33e0b90857f
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 11:10:44 2011 +0300
spice.proto: add SPICE_MSG_MAIN_MIGRATE_END & SPICE_MSGC_MAIN_MIGRATE_END
(cherry picked from commit cfbd07710562e522179ae5a7085a789489a821bb branch 0.8)
diff --git a/spice.proto b/spice.proto
index 78c1fad..266e03a 100644
--- a/spice.proto
+++ b/spice.proto
@@ -219,6 +219,8 @@ channel MainChannel : BaseChannel {
uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall;
} @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
+ Empty migrate_end;
+
client:
message {
uint64 cache_size;
@@ -243,6 +245,8 @@ channel MainChannel : BaseChannel {
message {
uint32 num_tokens;
} @ctype(SpiceMsgcMainAgentTokens) agent_token;
+
+ Empty migrate_end;
};
enum8 clip_type {
commit 70d1161430fd48c46a75d0742cb30315e11bc043
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 10:31:38 2011 +0300
server,proto: tell the clients to connect to the migration target before migraton starts
(1) send SPICE_MSG_MAIN_MIGRATE_BEGIN upon spice_server_migrate_connect
(to all the clients that support it)
(2) wait for SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR) from all the relevant clients,
or a timeout, in order to complete client_migrate_info monitor command
(cherry picked from commit 5560c56ef05c74da5e0e0825dc1f134019593cad branch 0.8;
Was modified to support the separation of main channel from reds, and multiple clients)
Conflicts:
server/reds.c
diff --git a/common/messages.h b/common/messages.h
index 8151dc0..a54190f 100644
--- a/common/messages.h
+++ b/common/messages.h
@@ -66,6 +66,8 @@ typedef struct SpiceMsgMainMigrationBegin {
uint16_t pub_key_type;
uint32_t pub_key_size;
uint8_t *pub_key_data;
+ uint32_t cert_subject_size;
+ uint8_t *cert_subject_data;
} SpiceMsgMainMigrationBegin;
typedef struct SpiceMsgMainMigrationSwitchHost {
diff --git a/server/main_channel.c b/server/main_channel.c
index 49028b3..5b16b30 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -110,16 +110,6 @@ typedef struct NotifyPipeItem {
int mess_len;
} NotifyPipeItem;
-typedef struct MigrateBeginPipeItem {
- PipeItem base;
- int port;
- int sport;
- char *host;
- uint16_t cert_pub_key_type;
- uint32_t cert_pub_key_len;
- uint8_t *cert_pub_key;
-} MigrateBeginPipeItem;
-
typedef struct MultiMediaTimePipeItem {
PipeItem base;
int time;
@@ -137,6 +127,8 @@ struct MainChannelClient {
SpiceTimer *ping_timer;
int ping_interval;
#endif
+ int mig_wait_connect;
+ int mig_connect_ok;
};
enum NetTestStage {
@@ -283,33 +275,6 @@ static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num
return &item->base;
}
-typedef struct MigrateBeginItemInfo {
- int port;
- int sport;
- char *host;
- uint16_t cert_pub_key_type;
- uint32_t cert_pub_key_len;
- uint8_t *cert_pub_key;
-} MigrateBeginItemInfo;
-
-// TODO: MC: migration is not tested at all with multiclient.
-static PipeItem *main_migrate_begin_item_new(
- RedChannelClient *rcc, void *data, int num)
-{
- MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem));
- MigrateBeginItemInfo *info = data;
-
- red_channel_pipe_item_init(rcc->channel, &item->base,
- SPICE_MSG_MAIN_MIGRATE_BEGIN);
- item->port = info->port;
- item->sport = info->sport;
- item->host = info->host;
- item->cert_pub_key_type = info->cert_pub_key_type;
- item->cert_pub_key_len = info->cert_pub_key_len;
- item->cert_pub_key = info->cert_pub_key;
- return &item->base;
-}
-
static PipeItem *main_multi_media_time_item_new(
RedChannelClient *rcc, void *data, int num)
{
@@ -550,35 +515,23 @@ static void main_channel_marshall_notify(SpiceMarshaller *m, NotifyPipeItem *ite
spice_marshaller_add(m, item->mess, item->mess_len + 1);
}
-void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport,
- char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
- uint8_t *cert_pub_key)
-{
- MigrateBeginItemInfo info = {
- .port =port,
- .sport = sport,
- .host = host,
- .cert_pub_key_type = cert_pub_key_type,
- .cert_pub_key_len = cert_pub_key_len,
- .cert_pub_key = cert_pub_key,
- };
-
- red_channel_pipes_new_add_push(&main_chan->base,
- main_migrate_begin_item_new, &info);
-}
-
-static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
- MigrateBeginPipeItem *item)
+static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc)
{
SpiceMsgMainMigrationBegin migrate;
-
- migrate.port = item->port;
- migrate.sport = item->sport;
- migrate.host_size = strlen(item->host) + 1;
- migrate.host_data = (uint8_t *)item->host;
- migrate.pub_key_type = item->cert_pub_key_type;
- migrate.pub_key_size = item->cert_pub_key_len;
- migrate.pub_key_data = item->cert_pub_key;
+ MainChannel *main_ch;
+
+ main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+ migrate.port = main_ch->mig_target.port;
+ migrate.sport = main_ch->mig_target.sport;
+ migrate.host_size = strlen(main_ch->mig_target.host) + 1;
+ migrate.host_data = (uint8_t *)main_ch->mig_target.host;
+ if (main_ch->mig_target.cert_subject) {
+ migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
+ migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
+ } else {
+ migrate.cert_subject_size = 0;
+ migrate.cert_subject_data = NULL;
+ }
spice_marshall_msg_main_migrate_begin(m, &migrate);
}
@@ -699,8 +652,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
main_channel_marshall_migrate(m);
break;
case SPICE_MSG_MAIN_MIGRATE_BEGIN:
- main_channel_marshall_migrate_begin(m,
- SPICE_CONTAINEROF(base, MigrateBeginPipeItem, base));
+ main_channel_marshall_migrate_begin(m, rcc);
break;
case SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
main_channel_marshall_multi_media_time(m,
@@ -741,6 +693,26 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
free(base);
}
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int success)
+{
+ red_printf("client %p connected: %d", mcc->base.client, success);
+ if (mcc->mig_wait_connect) {
+ MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
+
+ mcc->mig_wait_connect = FALSE;
+ mcc->mig_connect_ok = success;
+ ASSERT(main_channel->num_clients_mig_wait);
+ if (!--main_channel->num_clients_mig_wait) {
+ reds_on_main_migrate_connected();
+ }
+ } else {
+ if (success) {
+ red_printf("client %p MIGRATE_CANCEL", mcc->base.client);
+ red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+ }
+ }
+}
+
static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
{
MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
@@ -764,12 +736,10 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
main_channel_push_channels(mcc);
break;
case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
- red_printf("connected");
- reds_on_main_migrate_connected();
+ main_channel_client_handle_migrate_connected(mcc, TRUE);
break;
case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
- red_printf("mig connect error");
- reds_on_main_migrate_connect_error();
+ main_channel_client_handle_migrate_connected(mcc, FALSE);
break;
case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
reds_on_main_mouse_mode_request(message, size);
@@ -1001,6 +971,58 @@ MainChannel* main_channel_init(void)
main_channel_handle_parsed,
&channel_cbs);
ASSERT(channel);
-
+ red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
return (MainChannel *)channel;
}
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
+{
+ ASSERT(mcc);
+ return &mcc->base;
+}
+
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target)
+{
+ RingItem *client_link;
+
+ ASSERT(mig_target);
+ free(main_channel->mig_target.host);
+ main_channel->mig_target.host = strdup(mig_target->host);
+ free(main_channel->mig_target.cert_subject);
+ if (mig_target->cert_subject) {
+ main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
+ }
+ main_channel->mig_target.port = mig_target->port;
+ main_channel->mig_target.sport = mig_target->sport;
+
+ main_channel->num_clients_mig_wait = 0;
+
+ RING_FOREACH(client_link, &main_channel->base.clients) {
+ MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
+ if (red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+ red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+ mcc->mig_wait_connect = TRUE;
+ mcc->mig_connect_ok = FALSE;
+ main_channel->num_clients_mig_wait++;
+ }
+ }
+ return main_channel->num_clients_mig_wait;
+}
+
+void main_channel_migrate_cancel_wait(MainChannel *main_chan)
+{
+ RingItem *client_link;
+
+ RING_FOREACH(client_link, &main_chan->base.clients) {
+ MainChannelClient *mcc;
+
+ mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
+ if (mcc->mig_wait_connect) {
+ red_printf("client %p cancel wait connect", mcc->base.client);
+ mcc->mig_wait_connect = FALSE;
+ mcc->mig_connect_ok = FALSE;
+ }
+ }
+ main_chan->num_clients_mig_wait = 0;
+}
diff --git a/server/main_channel.h b/server/main_channel.h
index 2ae05e8..f3702e7 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -57,6 +57,8 @@ struct MainMigrateData {
typedef struct MainChannel {
RedChannel base;
uint8_t recv_buf[RECEIVE_BUF_SIZE];
+ RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?)
+ int num_clients_mig_wait;
} MainChannel;
@@ -80,9 +82,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ
int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
int ram_hint);
void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
-// TODO: consider exporting RedsMigSpice from reds.c
-void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport, char *host,
- uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, uint8_t *cert_pub_key);
void main_channel_push_migrate(MainChannel *main_chan);
void main_channel_push_migrate_switch(MainChannel *main_chan);
void main_channel_push_migrate_cancel(MainChannel *main_chan);
@@ -94,5 +93,12 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc);
int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
int main_channel_is_connected(MainChannel *main_chan);
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
+/* semi seamless migration */
+
+/* returns the number of clients that we are waiting for their connection */
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target);
+void main_channel_migrate_cancel_wait(MainChannel *main_chan);
+void main_channel_migrate_complete(MainChannel *main_chan, int success);
#endif
diff --git a/server/red_channel.h b/server/red_channel.h
index d044253..e30401c 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -450,6 +450,7 @@ struct RedClient {
pthread_t thread_id;
int disconnecting;
+
};
RedClient *red_client_new(void);
@@ -457,7 +458,6 @@ MainChannelClient *red_client_get_main(RedClient *client);
// main should be set once before all the other channels are created
void red_client_set_main(RedClient *client, MainChannelClient *mcc);
-
void red_client_migrate(RedClient *client);
// disconnects all the client's channels (should be called from the client's thread)
void red_client_destroy(RedClient *client);
diff --git a/server/reds.c b/server/reds.c
index 0c201e7..bc93e4a 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -191,8 +191,6 @@ typedef struct RedsStatValue {
#endif
-typedef struct RedsMigSpice RedsMigSpice;
-
typedef struct RedsState {
int listen_socket;
int secure_listen_socket;
@@ -295,6 +293,7 @@ struct ChannelSecurityOptions {
ChannelSecurityOptions *next;
};
+static void migrate_timeout(void *opaque);
static ChannelSecurityOptions *channels_security = NULL;
static int default_channel_security =
@@ -538,6 +537,12 @@ static RedChannel *reds_find_channel(uint32_t type, uint32_t id)
static void reds_mig_cleanup(void)
{
if (reds->mig_inprogress) {
+ if (reds->mig_wait_connect) {
+ SpiceMigrateInterface *sif;
+ ASSERT(migration_interface);
+ sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+ sif->migrate_connect_complete(migration_interface);
+ }
reds->mig_inprogress = FALSE;
reds->mig_wait_connect = FALSE;
reds->mig_wait_disconnect = FALSE;
@@ -1060,13 +1065,6 @@ void reds_on_main_migrate_connected(void)
}
}
-void reds_on_main_migrate_connect_error(void)
-{
- if (reds->mig_wait_connect) {
- reds_mig_cleanup();
- }
-}
-
void reds_on_main_mouse_mode_request(void *message, size_t size)
{
switch (((SpiceMsgcMainMouseModeRequest *)message)->mode) {
@@ -2976,18 +2974,6 @@ static void set_one_channel_security(int id, uint32_t security)
#define REDS_SAVE_VERSION 1
-struct RedsMigSpice {
- char pub_key[SPICE_TICKET_PUBKEY_BYTES];
- uint32_t mig_key;
- char *host;
- char *cert_subject;
- int port;
- int sport;
- uint16_t cert_pub_key_type;
- uint32_t cert_pub_key_len;
- uint8_t* cert_pub_key;
-};
-
typedef struct RedsMigSpiceMessage {
uint32_t connection_id;
} RedsMigSpiceMessage;
@@ -3007,24 +2993,12 @@ void reds_mig_release(void)
}
}
-static void reds_mig_continue(void)
-{
- RedsMigSpice *s = reds->mig_spice;
-
- red_printf("");
- main_channel_push_migrate_begin(reds->main_channel, s->port, s->sport,
- s->host, s->cert_pub_key_type, s->cert_pub_key_len, s->cert_pub_key);
-
- reds_mig_release();
-
- reds->mig_wait_connect = TRUE;
- core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
-}
-
static void reds_mig_started(void)
{
red_printf("");
+ ASSERT(reds->mig_spice);
+ reds->mig_wait_connect = TRUE;
reds->mig_inprogress = TRUE;
if (reds->listen_watch != NULL) {
@@ -3034,24 +3008,7 @@ static void reds_mig_started(void)
if (reds->secure_listen_watch != NULL) {
core->watch_update_mask(reds->secure_listen_watch, 0);
}
-
- if (!reds_main_channel_connected()) {
- red_printf("not connected to peer");
- goto error;
- }
-
- if ((SPICE_VERSION_MAJOR == 1) && (reds->peer_minor_version < 2)) {
- red_printf("minor version mismatch client %u server %u",
- reds->peer_minor_version, SPICE_VERSION_MINOR);
- goto error;
- }
-
- reds_mig_continue();
- return;
-
-error:
- reds_mig_release();
- reds_mig_disconnect();
+ core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
}
static void reds_mig_finished(int completed)
@@ -3127,11 +3084,16 @@ void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate)
}
}
-static void migrate_timout(void *opaque)
+static void migrate_timeout(void *opaque)
{
red_printf("");
ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
- reds_mig_disconnect();
+ if (reds->mig_wait_connect) {
+ main_channel_migrate_cancel_wait(reds->main_channel);
+ reds_mig_cleanup();
+ } else {
+ reds_mig_disconnect();
+ }
}
uint32_t reds_get_mm_time(void)
@@ -3492,7 +3454,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
main_dispatcher_init(core);
ring_init(&reds->channels);
- if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) {
+ if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) {
red_error("migration timer create failed");
}
if (!(reds->vdi_port_write_timer = core->timer_add(vdi_port_write_retry, NULL)))
@@ -3875,19 +3837,55 @@ SPICE_GNUC_VISIBLE int spice_server_set_agent_copypaste(SpiceServer *s, int enab
return 0;
}
+/* returns FALSE if info is invalid */
+static int reds_set_migration_dest_info(const char* dest,
+ int port, int secure_port,
+ const char* cert_subject)
+{
+ RedsMigSpice *spice_migration = NULL;
+
+ reds_mig_release();
+ if ((port == -1 && secure_port == -1) || !dest) {
+ return FALSE;
+ }
+
+ spice_migration = spice_new0(RedsMigSpice, 1);
+ spice_migration->port = port;
+ spice_migration->sport = secure_port;
+ spice_migration->host = strdup(dest);
+ if (cert_subject) {
+ spice_migration->cert_subject = strdup(cert_subject);
+ }
+
+ reds->mig_spice = spice_migration;
+
+ return TRUE;
+}
+
/* semi-seamless client migration */
SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* dest,
int port, int secure_port,
const char* cert_subject)
{
SpiceMigrateInterface *sif;
+
red_printf("");
ASSERT(migration_interface);
ASSERT(reds == s);
- red_printf("not implemented yet");
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
- sif->migrate_connect_complete(migration_interface);
+
+ if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
+ sif->migrate_connect_complete(migration_interface);
+ return -1;
+ }
+
+ if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
+ reds->mig_wait_connect = TRUE;
+ reds_mig_started();
+ } else {
+ sif->migrate_connect_complete(migration_interface);
+ }
return 0;
}
@@ -3896,27 +3894,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
int port, int secure_port,
const char* cert_subject)
{
- RedsMigSpice *spice_migration = NULL;
-
+ ASSERT(!migration_interface);
ASSERT(reds == s);
- if ((port == -1 && secure_port == -1) || !dest)
+ if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
return -1;
-
- spice_migration = spice_new0(RedsMigSpice, 1);
- spice_migration->port = port;
- spice_migration->sport = secure_port;
- spice_migration->host = strdup(dest);
- if (cert_subject) {
- spice_migration->cert_subject = strdup(cert_subject);
}
- reds_mig_release();
- reds->mig_spice = spice_migration;
return 0;
}
-/* interface for seamless migration */
SPICE_GNUC_VISIBLE int spice_server_migrate_start(SpiceServer *s)
{
ASSERT(reds == s);
diff --git a/server/reds.h b/server/reds.h
index 7720b30..13fec42 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -99,6 +99,13 @@ struct SpiceMigrateState {
int dummy;
};
+typedef struct RedsMigSpice {
+ char *host;
+ char *cert_subject;
+ int port;
+ int sport;
+} RedsMigSpice;
+
ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
@@ -134,11 +141,12 @@ int reds_num_of_clients(void);
void reds_update_stat_value(uint32_t value);
#endif
-// callbacks from main channel messages
+/* callbacks from main channel messages */
+
void reds_on_main_agent_start(void);
void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
-void reds_on_main_migrate_connected(void);
-void reds_on_main_migrate_connect_error(void);
+void reds_on_main_migrate_connected(void); //should be called when all the clients
+ // are connected to the target
void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
void reds_on_main_mouse_mode_request(void *message, size_t size);
diff --git a/spice.proto b/spice.proto
index abf3ec3..78c1fad 100644
--- a/spice.proto
+++ b/spice.proto
@@ -167,9 +167,8 @@ channel MainChannel : BaseChannel {
uint16 sport;
uint32 host_size;
uint8 *host_data[host_size] @zero_terminated @marshall @nonnull;
- pubkey_type pub_key_type;
- uint32 pub_key_size;
- uint8 *pub_key_data[pub_key_size] @zero_terminated @marshall @nonnull;
+ uint32 cert_subject_size;
+ uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall;
} @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
Empty migrate_cancel;
commit fc3aa53211a85387df52befcaa8e8f86f7e811f2
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Mon Oct 3 11:53:24 2011 +0200
configure: spice-protocol >= 0.9.1 (semi-seamless migration protocol)
(cherry picked from commit 55ccc022ec9829523ebe36fdf0ec7c593ce76c22 branch 0.8)
Conflicts:
configure.ac
diff --git a/configure.ac b/configure.ac
index 2afc559..203e82f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -132,7 +132,7 @@ AM_CONDITIONAL(SUPPORT_CLIENT, test "x$enable_client" = "xyes")
dnl =========================================================================
dnl Check deps
-PKG_CHECK_MODULES(PROTOCOL, spice-protocol >= 0.9.0)
+PKG_CHECK_MODULES(PROTOCOL, spice-protocol >= 0.9.1)
AC_SUBST(PROTOCOL_CFLAGS)
AC_CHECK_LIBM
commit 38a96b408494a94630232ab574a8c54c05112f07
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Mon Oct 3 11:47:33 2011 +0200
server: handle migration interface addition
(cherry picked from commit 3ac0075cdac8fa42de47a7882022795e96cb1fee branch 0.8)
Conflicts:
server/reds.h
diff --git a/server/reds.c b/server/reds.c
index 7e1b090..0c201e7 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -74,6 +74,7 @@
SpiceCoreInterface *core = NULL;
static SpiceCharDeviceInstance *vdagent = NULL;
+static SpiceMigrateInstance *migration_interface = NULL;
/* Debugging only variable: allow multiple client connections to the spice
* server */
@@ -3365,6 +3366,20 @@ SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *s,
red_printf("unsupported net wire interface");
return -1;
#endif
+ } else if (strcmp(interface->type, SPICE_INTERFACE_MIGRATION) == 0) {
+ red_printf("SPICE_INTERFACE_MIGRATION");
+ if (migration_interface) {
+ red_printf("already have migration");
+ return -1;
+ }
+
+ if (interface->major_version != SPICE_INTERFACE_MIGRATION_MAJOR ||
+ interface->minor_version > SPICE_INTERFACE_MIGRATION_MINOR) {
+ red_printf("unsupported migration interface");
+ return -1;
+ }
+ migration_interface = SPICE_CONTAINEROF(sin, SpiceMigrateInstance, base);
+ migration_interface->st = spice_new0(SpiceMigrateState, 1);
}
return 0;
@@ -3865,7 +3880,15 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
int port, int secure_port,
const char* cert_subject)
{
+ SpiceMigrateInterface *sif;
+ red_printf("");
+ ASSERT(migration_interface);
+ ASSERT(reds == s);
+
red_printf("not implemented yet");
+ sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+ sif->migrate_connect_complete(migration_interface);
+
return 0;
}
@@ -3926,8 +3949,14 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_client_state(SpiceServer *s)
SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
{
+ SpiceMigrateInterface *sif;
+ ASSERT(migration_interface);
ASSERT(reds == s);
reds_mig_finished(completed);
+ sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+ if (sif->migrate_end_complete) {
+ sif->migrate_end_complete(migration_interface);
+ }
return 0;
}
diff --git a/server/reds.h b/server/reds.h
index 188bed5..7720b30 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -95,6 +95,10 @@ struct SpiceNetWireState {
struct TunnelWorker *worker;
};
+struct SpiceMigrateState {
+ int dummy;
+};
+
ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
commit cd402151def11c5748065de28d124747df09447b
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Mon Oct 3 11:21:38 2011 +0200
server/spice.h: semi-seamless migration interface, RHBZ #738266
semi-seamless migration details:
migration source side
---------------------
(1) spice_server_migrate_connect (*): tell client to link
to the target side - send SPICE_MSG_MAIN_MIGRATE_BEGIN.
This should be called upon client_migrate_info cmd.
client_migrate_info is asynchronous.
(2) Complete spice_server_migrate_connect only when the client has been connected
to the target - wait for SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR) or a timeout.
(3) spice_server_migrate_end: tell client migration it can switch to the target - send
SPICE_MSG_MAIN_MIGRATE_END.
(4) client cleans up all data related to the connection to the source and switches to the target.
It sends SPICE_MSGC_MAIN_MIGRATE_END.
migration target side
---------------------
(1) the server identifies itself as a migraiton target since the client is linked with (connection_id != 0)
(2) server doesn't start the channels' logic (channel->link) till it receives SPICE_MSGC_MAIN_MIGRATE_END
from the client.
* After migration starts, the target qemu is blocked and cannot accept new spice client
connections. Thus, we trigger the connection to the target upon client_migrate_info
command.
(cherry picked from commit 6e56bea67c5648b0c81990171d4bc0cf1a402043 branch 0.8)
Conflicts:
server/spice.h
diff --git a/server/reds.c b/server/reds.c
index 31e255f..7e1b090 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3860,6 +3860,15 @@ SPICE_GNUC_VISIBLE int spice_server_set_agent_copypaste(SpiceServer *s, int enab
return 0;
}
+/* semi-seamless client migration */
+SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* dest,
+ int port, int secure_port,
+ const char* cert_subject)
+{
+ red_printf("not implemented yet");
+ return 0;
+}
+
SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* dest,
int port, int secure_port,
const char* cert_subject)
diff --git a/server/spice-experimental.h b/server/spice-experimental.h
index 482ac44..6997aa0 100644
--- a/server/spice-experimental.h
+++ b/server/spice-experimental.h
@@ -29,16 +29,13 @@ void spice_server_net_wire_recv_packet(SpiceNetWireInstance *sin,
const uint8_t *pkt, int len);
/* spice seamless client migration (broken) */
-
enum {
SPICE_MIGRATE_CLIENT_NONE = 1,
SPICE_MIGRATE_CLIENT_WAITING,
SPICE_MIGRATE_CLIENT_READY,
};
-int spice_server_migrate_start(SpiceServer *s);
int spice_server_migrate_client_state(SpiceServer *s);
-int spice_server_migrate_end(SpiceServer *s, int completed);
#endif // __SPICE_EXPERIMENTAL_H__
diff --git a/server/spice.h b/server/spice.h
index 74f9fdb..974975a 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -475,8 +475,26 @@ int spice_server_set_agent_copypaste(SpiceServer *s, int enable);
int spice_server_get_sock_info(SpiceServer *s, struct sockaddr *sa, socklen_t *salen);
int spice_server_get_peer_info(SpiceServer *s, struct sockaddr *sa, socklen_t *salen);
-/* spice switch-host client migration */
+/* migration interface */
+#define SPICE_INTERFACE_MIGRATION "migration"
+#define SPICE_INTERFACE_MIGRATION_MAJOR 1
+#define SPICE_INTERFACE_MIGRATION_MINOR 1
+typedef struct SpiceMigrateInterface SpiceMigrateInterface;
+typedef struct SpiceMigrateInstance SpiceMigrateInstance;
+typedef struct SpiceMigrateState SpiceMigrateState;
+
+struct SpiceMigrateInterface {
+ SpiceBaseInterface base;
+ void (*migrate_connect_complete)(SpiceMigrateInstance *sin);
+ void (*migrate_end_complete)(SpiceMigrateInstance *sin);
+};
+
+struct SpiceMigrateInstance {
+ SpiceBaseInstance base;
+ SpiceMigrateState *st;
+};
+/* spice switch-host client migration */
int spice_server_migrate_info(SpiceServer *s, const char* dest,
int port, int secure_port,
const char* cert_subject);
@@ -485,4 +503,11 @@ int spice_server_migrate_switch(SpiceServer *s);
/* server status */
int spice_server_get_num_clients(SpiceServer *s);
+/* spice (semi-)seamless client migration */
+int spice_server_migrate_connect(SpiceServer *s, const char* dest,
+ int port, int secure_port,
+ const char* cert_subject);
+int spice_server_migrate_start(SpiceServer *s);
+int spice_server_migrate_end(SpiceServer *s, int completed);
+
#endif
commit 0bf518cd3daad526a16468e82c1d6c715535e9ba
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Wed Oct 5 09:05:13 2011 +0200
server: set & test channel capabilities in red_channel
The code for setting and testing channel capabilities was
unnecessarily duplicated. Now it is in red_channel.
RedsChannel was dropped from Reds; It was used only for holding
the channels common capabilities, which are now held in RedChannel.
diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 24fc621..c8b42e3 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -496,7 +496,9 @@ static void inputs_connect(RedChannel *channel, RedClient *client,
icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
channel,
client,
- stream);
+ stream,
+ num_common_caps, common_caps,
+ num_caps, caps);
icc->motion_count = 0;
inputs_pipe_add_init(&icc->base);
}
diff --git a/server/main_channel.c b/server/main_channel.c
index 43c0f3f..49028b3 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -39,17 +39,13 @@
#include "server/demarshallers.h"
#include "common/ring.h"
#include "common/messages.h"
-#include "reds.h"
#include "main_channel.h"
+#include "reds.h"
#include "red_channel.h"
#include "generated_marshallers.h"
#define ZERO_BUF_SIZE 4096
-// approximate max receive message size for main channel
-#define RECEIVE_BUF_SIZE \
- (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
-
#define REDS_MAX_SEND_IOVEC 100
#define NET_TEST_WARMUP_BYTES 0
@@ -143,11 +139,6 @@ struct MainChannelClient {
#endif
};
-struct MainChannel {
- RedChannel base;
- uint8_t recv_buf[RECEIVE_BUF_SIZE];
-};
-
enum NetTestStage {
NET_TEST_STAGE_INVALID,
NET_TEST_STAGE_WARMUP,
@@ -911,12 +902,18 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc)
return mcc->connection_id;
}
-MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
- RedsStream *stream, uint32_t connection_id)
+static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
+ RedsStream *stream, uint32_t connection_id,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps)
{
MainChannelClient *mcc = (MainChannelClient*)red_channel_client_create(sizeof(MainChannelClient),
&main_chan->base,
- client, stream);
+ client, stream,
+ num_common_caps,
+ common_caps,
+ num_caps,
+ caps);
mcc->connection_id = connection_id;
mcc->bitrate_per_sec = ~0;
@@ -942,7 +939,9 @@ MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
// into usage somewhere (not an issue until we return migration to it's
// former glory)
red_printf("add main channel client");
- mcc = main_channel_client_create(channel, client, stream, connection_id);
+ mcc = main_channel_client_create(channel, client, stream, connection_id,
+ num_common_caps, common_caps,
+ num_caps, caps);
return mcc;
}
diff --git a/server/main_channel.h b/server/main_channel.h
index 713bcd0..2ae05e8 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -45,7 +45,20 @@ struct MainMigrateData {
uint32_t write_queue_size;
};
-typedef struct MainChannel MainChannel;
+// TODO: Defines used to calculate receive buffer size, and also by reds.c
+// other options: is to make a reds_main_consts.h, to duplicate defines.
+#define REDS_AGENT_WINDOW_SIZE 10
+#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
+
+// approximate max receive message size for main channel
+#define RECEIVE_BUF_SIZE \
+ (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
+
+typedef struct MainChannel {
+ RedChannel base;
+ uint8_t recv_buf[RECEIVE_BUF_SIZE];
+} MainChannel;
+
MainChannel *main_channel_init(void);
RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
@@ -82,9 +95,4 @@ int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
int main_channel_is_connected(MainChannel *main_chan);
-// TODO: Defines used to calculate receive buffer size, and also by reds.c
-// other options: is to make a reds_main_consts.h, to duplicate defines.
-#define REDS_AGENT_WINDOW_SIZE 10
-#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
-
#endif
diff --git a/server/red_channel.c b/server/red_channel.c
index 8993cc3..51415cb 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -358,11 +358,43 @@ static void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
channel->clients_num++;
}
-RedChannelClient *red_channel_client_create(
- int size,
- RedChannel *channel,
- RedClient *client,
- RedsStream *stream)
+static void red_channel_client_set_remote_caps(RedChannelClient* rcc,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps)
+{
+ rcc->remote_caps.num_common_caps = num_common_caps;
+ rcc->remote_caps.common_caps = spice_memdup(common_caps, num_common_caps * sizeof(uint32_t));
+
+ rcc->remote_caps.num_caps = num_caps;
+ rcc->remote_caps.caps = spice_memdup(caps, num_caps * sizeof(uint32_t));
+}
+
+static void red_channel_client_destroy_remote_caps(RedChannelClient* rcc)
+{
+ rcc->remote_caps.num_common_caps = 0;
+ free(rcc->remote_caps.common_caps);
+ rcc->remote_caps.num_caps = 0;
+ free(rcc->remote_caps.caps);
+}
+
+int red_channel_client_test_remote_common_cap(RedChannelClient *rcc, uint32_t cap)
+{
+ return test_capabilty(rcc->remote_caps.common_caps,
+ rcc->remote_caps.num_common_caps,
+ cap);
+}
+
+int red_channel_client_test_remote_cap(RedChannelClient *rcc, uint32_t cap)
+{
+ return test_capabilty(rcc->remote_caps.caps,
+ rcc->remote_caps.num_caps,
+ cap);
+}
+
+RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
+ RedsStream *stream,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps)
{
RedChannelClient *rcc;
@@ -384,6 +416,9 @@ RedChannelClient *red_channel_client_create(
rcc->outgoing.cb = &channel->outgoing_cb;
rcc->outgoing.pos = 0;
rcc->outgoing.size = 0;
+
+ red_channel_client_set_remote_caps(rcc, num_common_caps, common_caps, num_caps, caps);
+
if (!channel->channel_cbs.config_socket(rcc)) {
goto error;
}
@@ -564,10 +599,36 @@ void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs)
}
}
-void red_channel_set_caps(RedChannel *channel, int num_caps, uint32_t *caps)
+int test_capabilty(uint32_t *caps, int num_caps, uint32_t cap)
{
- channel->num_caps = num_caps;
- channel->caps = caps;
+ uint32_t index = cap / 32;
+ if (num_caps < index + 1) {
+ return FALSE;
+ }
+
+ return (caps[index] & (1 << (cap % 32))) != 0;
+}
+
+static void add_capability(uint32_t **caps, int *num_caps, uint32_t cap)
+{
+ int nbefore, n;
+
+ nbefore = *num_caps;
+ n = cap / 32;
+ *num_caps = MAX(*num_caps, n + 1);
+ *caps = spice_renew(uint32_t, *caps, *num_caps);
+ memset(*caps + nbefore, 0, (*num_caps - nbefore) * sizeof(uint32_t));
+ (*caps)[n] |= (1 << (cap % 32));
+}
+
+void red_channel_set_common_cap(RedChannel *channel, uint32_t cap)
+{
+ add_capability(&channel->local_caps.common_caps, &channel->local_caps.num_common_caps, cap);
+}
+
+void red_channel_set_cap(RedChannel *channel, uint32_t cap)
+{
+ add_capability(&channel->local_caps.caps, &channel->local_caps.num_caps, cap);
}
void red_channel_set_data(RedChannel *channel, void *data)
@@ -585,6 +646,7 @@ void red_channel_client_destroy(RedChannelClient *rcc)
if (rcc->send_data.marshaller) {
spice_marshaller_destroy(rcc->send_data.marshaller);
}
+ red_channel_client_destroy_remote_caps(rcc);
free(rcc);
}
@@ -600,9 +662,15 @@ void red_channel_destroy(RedChannel *channel)
red_channel_client_destroy(
SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
}
- if (channel->caps) {
- free(channel->caps);
+
+ if (channel->local_caps.num_common_caps) {
+ free(channel->local_caps.common_caps);
+ }
+
+ if (channel->local_caps.num_caps) {
+ free(channel->local_caps.caps);
}
+
free(channel);
}
@@ -979,7 +1047,9 @@ void red_channel_disconnect(RedChannel *channel)
RedChannelClient *red_channel_client_create_dummy(int size,
RedChannel *channel,
- RedClient *client)
+ RedClient *client,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps)
{
RedChannelClient *rcc;
@@ -987,6 +1057,7 @@ RedChannelClient *red_channel_client_create_dummy(int size,
rcc = spice_malloc0(size);
rcc->client = client;
rcc->channel = channel;
+ red_channel_client_set_remote_caps(rcc, num_common_caps, common_caps, num_caps, caps);
red_channel_add_client(channel, rcc);
return rcc;
}
@@ -994,6 +1065,7 @@ RedChannelClient *red_channel_client_create_dummy(int size,
void red_channel_client_destroy_dummy(RedChannelClient *rcc)
{
red_channel_remove_client(rcc);
+ red_channel_client_destroy_remote_caps(rcc);
free(rcc);
}
diff --git a/server/red_channel.h b/server/red_channel.h
index 2ebb6b6..d044253 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -143,7 +143,7 @@ typedef uint64_t (*channel_handle_migrate_data_get_serial_proc)(RedChannelClient
typedef void (*channel_client_connect_proc)(RedChannel *channel, RedClient *client, RedsStream *stream,
- int migration, int num_common_caps, uint32_t *common_caps,
+ int migration, int num_common_cap, uint32_t *common_caps,
int num_caps, uint32_t *caps);
typedef void (*channel_client_disconnect_proc)(RedChannelClient *base);
typedef void (*channel_client_migrate_proc)(RedChannelClient *base);
@@ -178,6 +178,15 @@ typedef struct {
channel_client_migrate_proc migrate;
} ClientCbs;
+typedef struct RedChannelCapabilities {
+ int num_common_caps;
+ uint32_t *common_caps;
+ int num_caps;
+ uint32_t *caps;
+} RedChannelCapabilities;
+
+int test_capabilty(uint32_t *caps, int num_caps, uint32_t cap);
+
struct RedChannelClient {
RingItem channel_link;
RingItem client_link;
@@ -206,12 +215,16 @@ struct RedChannelClient {
int id; // debugging purposes
Ring pipe;
uint32_t pipe_size;
+
+ RedChannelCapabilities remote_caps;
};
struct RedChannel {
uint32_t type;
uint32_t id;
+ RingItem link; // channels link for reds
+
SpiceCoreInterface *core;
int migrate;
int handle_acks;
@@ -232,8 +245,7 @@ struct RedChannel {
ChannelCbs channel_cbs;
ClientCbs client_cbs;
- int num_caps;
- uint32_t *caps;
+ RedChannelCapabilities local_caps;
void *data;
@@ -265,19 +277,23 @@ RedChannel *red_channel_create_parser(int size,
void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs);
// caps are freed when the channel is destroyed
-void red_channel_set_caps(RedChannel *channel, int num_caps, uint32_t *caps);
+void red_channel_set_common_cap(RedChannel *channel, uint32_t cap);
+void red_channel_set_cap(RedChannel *channel, uint32_t cap);
void red_channel_set_data(RedChannel *channel, void *data);
RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
- RedsStream *stream);
-
+ RedsStream *stream,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps);
// TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but
// do use the client callbacks. So the channel clients are not connected (the channel doesn't
// have list of them, but they do have a link to the channel, and the client has a list of them)
RedChannel *red_channel_create_dummy(int size, uint32_t type, uint32_t id);
RedChannelClient *red_channel_client_create_dummy(int size,
RedChannel *channel,
- RedClient *client);
+ RedClient *client,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps);
void red_channel_client_destroy_dummy(RedChannelClient *rcc);
@@ -294,6 +310,9 @@ int red_channel_client_is_connected(RedChannelClient *rcc);
void red_channel_client_destroy(RedChannelClient *rcc);
void red_channel_destroy(RedChannel *channel);
+int red_channel_client_test_remote_common_cap(RedChannelClient *rcc, uint32_t cap);
+int red_channel_client_test_remote_cap(RedChannelClient *rcc, uint32_t cap);
+
/* shutdown is the only safe thing to do out of the client/channel
* thread. It will not touch the rings, just shutdown the socket.
* It should be followed by some way to gurantee a disconnection. */
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index 6c36fec..1e8267e 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -3445,7 +3445,8 @@ static void handle_tunnel_channel_link(RedChannel *channel, RedClient *client,
}
tcc = (TunnelChannelClient*)red_channel_client_create(sizeof(TunnelChannelClient),
- channel, client, stream);
+ channel, client, stream,
+ 0, NULL, 0, NULL);
tcc->worker = worker;
tcc->worker->channel_client = tcc;
diff --git a/server/red_worker.c b/server/red_worker.c
index 6756af9..4337d16 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -9449,7 +9449,8 @@ static CommonChannelClient *common_channel_client_create(int size,
{
MainChannelClient *mcc = red_client_get_main(client);
RedChannelClient *rcc =
- red_channel_client_create(size, &common->base, client, stream);
+ red_channel_client_create(size, &common->base, client, stream,
+ 0, NULL, 0, NULL);
CommonChannelClient *common_cc = (CommonChannelClient*)rcc;
common_cc->worker = common->worker;
diff --git a/server/reds.c b/server/reds.c
index 65c98ae..31e255f 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -192,14 +192,6 @@ typedef struct RedsStatValue {
typedef struct RedsMigSpice RedsMigSpice;
-typedef struct RedsChannel {
- struct RedsChannel *next;
- RedChannel *base;
-
- int num_common_caps;
- uint32_t *common_caps;
-} RedsChannel;
-
typedef struct RedsState {
int listen_socket;
int secure_listen_socket;
@@ -217,7 +209,7 @@ typedef struct RedsState {
int mig_target;
RedsMigSpice *mig_spice;
int num_of_channels;
- RedsChannel *channels;
+ Ring channels;
int mouse_mode;
int is_client_mouse_allowed;
int dispatcher_allows_client_mouse;
@@ -302,7 +294,6 @@ struct ChannelSecurityOptions {
ChannelSecurityOptions *next;
};
-static void reds_dispose_channel(RedsChannel *channel);
static ChannelSecurityOptions *channels_security = NULL;
static int default_channel_security =
@@ -515,42 +506,32 @@ void reds_update_stat_value(uint32_t value)
void reds_register_channel(RedChannel *channel)
{
- RedsChannel *reds_channel;
-
ASSERT(reds);
- // TODO: should channels be released upon some destructor?
- reds_channel = spice_malloc0(sizeof(RedsChannel));
- reds_channel->base = channel;
- reds_channel->next = reds->channels;
- reds->channels = reds_channel;
+ ring_add(&reds->channels, &channel->link);
reds->num_of_channels++;
}
void reds_unregister_channel(RedChannel *channel)
{
- RedsChannel **now = &reds->channels;
-
- while (*now) {
- if ((*now)->base == channel) {
- RedsChannel *free_channel = *now;
- *now = free_channel->next;
- reds_dispose_channel(free_channel);
- free(free_channel);
- reds->num_of_channels--;
- return;
- }
- now = &(*now)->next;
+ if (ring_item_is_linked(&channel->link)) {
+ ring_remove(&channel->link);
+ reds->num_of_channels--;
+ } else {
+ red_printf("not found");
}
- red_printf("not found");
}
-static RedsChannel *reds_find_channel(uint32_t type, uint32_t id)
+static RedChannel *reds_find_channel(uint32_t type, uint32_t id)
{
- RedsChannel *channel = reds->channels;
- while (channel && !(channel->base->type == type && channel->base->id == id)) {
- channel = channel->next;
+ RingItem *now;
+
+ RING_FOREACH(now, &reds->channels) {
+ RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link);
+ if (channel->type == type && channel->id == id) {
+ return channel;
+ }
}
- return channel;
+ return NULL;
}
static void reds_mig_cleanup(void)
@@ -987,11 +968,11 @@ SPICE_GNUC_VISIBLE int spice_server_get_num_clients(SpiceServer *s)
static int secondary_channels[] = {
SPICE_CHANNEL_MAIN, SPICE_CHANNEL_DISPLAY, SPICE_CHANNEL_CURSOR, SPICE_CHANNEL_INPUTS};
-static int channel_is_secondary(RedsChannel *channel)
+static int channel_is_secondary(RedChannel *channel)
{
int i;
for (i = 0 ; i < sizeof(secondary_channels)/sizeof(secondary_channels[0]); ++i) {
- if (channel->base->type == secondary_channels[i]) {
+ if (channel->type == secondary_channels[i]) {
return TRUE;
}
}
@@ -1000,21 +981,20 @@ static int channel_is_secondary(RedsChannel *channel)
void reds_fill_channels(SpiceMsgChannels *channels_info)
{
- RedsChannel *channel;
- int i;
+ RingItem *now;
int used_channels = 0;
channels_info->num_of_channels = reds->num_of_channels;
- channel = reds->channels;
- for (i = 0; i < reds->num_of_channels; i++, channel = channel->next) {
- ASSERT(channel);
+ RING_FOREACH(now, &reds->channels) {
+ RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link);
if (reds->num_clients > 1 && !channel_is_secondary(channel)) {
continue;
}
- channels_info->channels[used_channels].type = channel->base->type;
- channels_info->channels[used_channels].id = channel->base->id;
+ channels_info->channels[used_channels].type = channel->type;
+ channels_info->channels[used_channels].id = channel->id;
used_channels++;
}
+
channels_info->num_of_channels = used_channels;
if (used_channels != reds->num_of_channels) {
red_printf("sent %d out of %d", used_channels, reds->num_of_channels);
@@ -1388,46 +1368,22 @@ static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
return TRUE;
}
-static void reds_channel_set_common_caps(RedsChannel *channel, int cap, int active)
-{
- int nbefore, n;
-
- nbefore = channel->num_common_caps;
- n = cap / 32;
- channel->num_common_caps = MAX(channel->num_common_caps, n + 1);
- channel->common_caps = spice_renew(uint32_t, channel->common_caps, channel->num_common_caps);
- memset(channel->common_caps + nbefore, 0,
- (channel->num_common_caps - nbefore) * sizeof(uint32_t));
- if (active) {
- channel->common_caps[n] |= (1 << cap);
- } else {
- channel->common_caps[n] &= ~(1 << cap);
- }
-}
-
-static void reds_channel_init_auth_caps(RedsChannel *channel)
+static void reds_channel_init_auth_caps(RedChannel *channel)
{
if (sasl_enabled) {
- reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SASL, TRUE);
+ red_channel_set_common_cap(channel, SPICE_COMMON_CAP_AUTH_SASL);
} else {
- reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SPICE, TRUE);
+ red_channel_set_common_cap(channel, SPICE_COMMON_CAP_AUTH_SPICE);
}
- reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION, TRUE);
-}
-
-static void reds_dispose_channel(RedsChannel *channel)
-{
- free(channel->common_caps);
- channel->common_caps = NULL;
- channel->num_common_caps = 0;
+ red_channel_set_common_cap(channel, SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION);
}
static int reds_send_link_ack(RedLinkInfo *link)
{
SpiceLinkHeader header;
SpiceLinkReply ack;
- RedsChannel common_caps = { 0, };
- RedsChannel *channel;
+ RedChannel *channel;
+ RedChannelCapabilities *channel_caps;
BUF_MEM *bmBuf;
BIO *bio;
int ret = FALSE;
@@ -1441,13 +1397,16 @@ static int reds_send_link_ack(RedLinkInfo *link)
channel = reds_find_channel(link->link_mess->channel_type, 0);
if (!channel) {
- channel = &common_caps;
+ ASSERT(link->link_mess->channel_type == SPICE_CHANNEL_MAIN);
+ ASSERT(reds->main_channel);
+ channel = &reds->main_channel->base;
}
reds_channel_init_auth_caps(channel); /* make sure common caps are set */
- ack.num_common_caps = channel->num_common_caps;
- ack.num_channel_caps = channel->base ? channel->base->num_caps : 0;
+ channel_caps = &channel->local_caps;
+ ack.num_common_caps = channel_caps->num_common_caps;
+ ack.num_channel_caps = channel_caps->num_caps;
header.size += (ack.num_common_caps + ack.num_channel_caps) * sizeof(uint32_t);
ack.caps_offset = sizeof(SpiceLinkReply);
@@ -1473,17 +1432,14 @@ static int reds_send_link_ack(RedLinkInfo *link)
goto end;
if (!sync_write(link->stream, &ack, sizeof(ack)))
goto end;
- if (!sync_write(link->stream, channel->common_caps, channel->num_common_caps * sizeof(uint32_t)))
+ if (!sync_write(link->stream, channel_caps->common_caps, channel_caps->num_common_caps * sizeof(uint32_t)))
+ goto end;
+ if (!sync_write(link->stream, channel_caps->caps, channel_caps->num_caps * sizeof(uint32_t)))
goto end;
- if (channel->base) {
- if (!sync_write(link->stream, channel->base->caps, channel->base->num_caps * sizeof(uint32_t)))
- goto end;
- }
ret = TRUE;
end:
- reds_dispose_channel(&common_caps);
BIO_free(bio);
return ret;
}
@@ -1542,6 +1498,8 @@ static void reds_handle_main_link(RedLinkInfo *link)
MainChannelClient *mcc;
red_printf("");
+ ASSERT(reds->main_channel);
+
link_mess = link->link_mess;
if (!reds->allow_multiple_clients) {
reds_disconnect();
@@ -1576,10 +1534,6 @@ static void reds_handle_main_link(RedLinkInfo *link)
link->link_mess = NULL;
reds_link_free(link);
caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
- if (!reds->main_channel) {
- reds->main_channel = main_channel_init();
- ASSERT(reds->main_channel);
- }
client = red_client_new();
ring_add(&reds->clients, &client->link);
reds->num_clients++;
@@ -1645,7 +1599,7 @@ static void openssl_init(RedLinkInfo *link)
static void reds_handle_other_links(RedLinkInfo *link)
{
- RedsChannel *channel;
+ RedChannel *channel;
RedClient *client = NULL;
RedsStream *stream;
SpiceLinkMess *link_mess;
@@ -1687,12 +1641,12 @@ static void reds_handle_other_links(RedLinkInfo *link)
link->link_mess = NULL;
reds_link_free(link);
caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
- channel->base->client_cbs.connect(channel->base, client, stream, reds->mig_target,
- link_mess->num_common_caps,
- link_mess->num_common_caps ? caps : NULL,
- link_mess->num_channel_caps,
- link_mess->num_channel_caps ?
- caps + link_mess->num_common_caps : NULL);
+ channel->client_cbs.connect(channel, client, stream, reds->mig_target,
+ link_mess->num_common_caps,
+ link_mess->num_common_caps ? caps : NULL,
+ link_mess->num_channel_caps,
+ link_mess->num_channel_caps ?
+ caps + link_mess->num_common_caps : NULL);
free(link_mess);
}
@@ -2459,8 +2413,8 @@ static void reds_handle_read_link_done(void *opaque)
return;
}
- auth_selection = link_mess->num_common_caps > 0 &&
- (caps[0] & (1 << SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION));;
+ auth_selection = test_capabilty(caps, link_mess->num_common_caps,
+ SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION);
if (!reds_security_check(link)) {
if (link->stream->ssl) {
@@ -3521,6 +3475,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
ring_init(&reds->clients);
reds->num_clients = 0;
main_dispatcher_init(core);
+ ring_init(&reds->channels);
if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) {
red_error("migration timer create failed");
@@ -3577,7 +3532,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
}
#endif
- reds->main_channel = NULL;
+ reds->main_channel = main_channel_init();
inputs_init();
reds->mouse_mode = SPICE_MOUSE_MODE_SERVER;
diff --git a/server/smartcard.c b/server/smartcard.c
index 056caa6..f9cafdf 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -493,7 +493,9 @@ static void smartcard_connect(RedChannel *channel, RedClient *client,
{
RedChannelClient *rcc;
- rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
+ rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream,
+ num_common_caps, common_caps,
+ num_caps, caps);
red_channel_client_ack_zero_messages_window(rcc);
}
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 98f3cd1..048da34 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -118,8 +118,6 @@ struct SndChannel {
snd_channel_handle_message_proc handle_message;
snd_channel_on_message_done_proc on_message_done;
snd_channel_cleanup_channel_proc cleanup;
- int num_caps;
- uint32_t *caps;
};
typedef struct AudioFrame AudioFrame;
@@ -196,18 +194,6 @@ static uint32_t playback_compression = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
static void snd_receive(void* data);
-static int check_cap(uint32_t *caps, int num_caps, uint32_t cap)
-{
- uint32_t i = cap / 32;
-
- cap = 1 << (cap % 32);
- if (i >= num_caps) {
- return FALSE;
- }
-
- return caps[i] & cap;
-}
-
static SndChannel *snd_channel_get(SndChannel *channel)
{
channel->refs++;
@@ -239,7 +225,6 @@ static void snd_disconnect_channel(SndChannel *channel)
channel->stream->watch = NULL;
reds_stream_free(channel->stream);
spice_marshaller_destroy(channel->send_data.marshaller);
- free(channel->caps);
snd_channel_put(channel);
}
@@ -579,8 +564,10 @@ static int snd_playback_send_volume(PlaybackChannel *playback_channel)
SndChannel *channel = &playback_channel->base;
SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
- if (!check_cap(channel->caps, channel->num_caps, SPICE_PLAYBACK_CAP_VOLUME))
+ if (!red_channel_client_test_remote_cap(channel->channel_client,
+ SPICE_PLAYBACK_CAP_VOLUME)) {
return TRUE;
+ }
return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME);
}
@@ -603,8 +590,10 @@ static int snd_playback_send_mute(PlaybackChannel *playback_channel)
SndChannel *channel = &playback_channel->base;
SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
- if (!check_cap(channel->caps, channel->num_caps, SPICE_PLAYBACK_CAP_VOLUME))
+ if (!red_channel_client_test_remote_cap(channel->channel_client,
+ SPICE_PLAYBACK_CAP_VOLUME)) {
return TRUE;
+ }
return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE);
}
@@ -695,8 +684,10 @@ static int snd_record_send_volume(RecordChannel *record_channel)
SndChannel *channel = &record_channel->base;
SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
- if (!check_cap(channel->caps, channel->num_caps, SPICE_RECORD_CAP_VOLUME))
+ if (!red_channel_client_test_remote_cap(channel->channel_client,
+ SPICE_RECORD_CAP_VOLUME)) {
return TRUE;
+ }
return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME);
}
@@ -706,8 +697,10 @@ static int snd_record_send_mute(RecordChannel *record_channel)
SndChannel *channel = &record_channel->base;
SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
- if (!check_cap(channel->caps, channel->num_caps, SPICE_RECORD_CAP_VOLUME))
+ if (!red_channel_client_test_remote_cap(channel->channel_client,
+ SPICE_RECORD_CAP_VOLUME)) {
return TRUE;
+ }
return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE);
}
@@ -941,12 +934,12 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i
channel->handle_message = handle_message;
channel->on_message_done = on_message_done;
channel->cleanup = cleanup;
- channel->num_caps = num_caps;
- channel->caps = spice_memdup(caps, num_caps * sizeof(uint32_t));
channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient),
worker->base_channel,
- client);
+ client,
+ 0, NULL,
+ num_caps, caps);
return channel;
error2:
@@ -1140,6 +1133,7 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
CELTEncoder *celt_encoder;
CELTMode *celt_mode;
int celt_error;
+ RedChannelClient *rcc;
snd_disconnect_channel(worker->connection);
@@ -1169,13 +1163,15 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
goto error_2;
}
worker->connection = &playback_channel->base;
+ rcc = playback_channel->base.channel_client;
snd_playback_free_frame(playback_channel, &playback_channel->frames[0]);
snd_playback_free_frame(playback_channel, &playback_channel->frames[1]);
snd_playback_free_frame(playback_channel, &playback_channel->frames[2]);
playback_channel->celt_mode = celt_mode;
playback_channel->celt_encoder = celt_encoder;
- playback_channel->mode = check_cap(caps, num_caps, SPICE_PLAYBACK_CAP_CELT_0_5_1) ?
+ playback_channel->mode = red_channel_client_test_remote_cap(rcc,
+ SPICE_PLAYBACK_CAP_CELT_0_5_1) ?
playback_compression : SPICE_AUDIO_DATA_MODE_RAW;
on_new_playback_channel(worker);
@@ -1431,8 +1427,6 @@ static void remove_worker(SndWorker *worker)
void snd_attach_playback(SpicePlaybackInstance *sin)
{
SndWorker *playback_worker;
- int num_caps;
- uint32_t *caps;
RedChannel *channel;
ClientCbs client_cbs = {0,};
@@ -1449,12 +1443,8 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
client_cbs.migrate = snd_playback_migrate_channel_client;
red_channel_register_client_cbs(channel, &client_cbs);
red_channel_set_data(channel, playback_worker);
-
- num_caps = 1;
- caps = spice_new(uint32_t, 1);
- caps[0] = (1 << SPICE_PLAYBACK_CAP_CELT_0_5_1) |
- (1 << SPICE_PLAYBACK_CAP_VOLUME);
- red_channel_set_caps(channel, num_caps, caps);
+ red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
+ red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
playback_worker->base_channel = channel;
add_worker(playback_worker);
@@ -1464,8 +1454,6 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
void snd_attach_record(SpiceRecordInstance *sin)
{
SndWorker *record_worker;
- int num_caps;
- uint32_t *caps;
RedChannel *channel;
ClientCbs client_cbs = {0,};
@@ -1482,12 +1470,8 @@ void snd_attach_record(SpiceRecordInstance *sin)
client_cbs.migrate = snd_record_migrate_channel_client;
red_channel_register_client_cbs(channel, &client_cbs);
red_channel_set_data(channel, record_worker);
-
- num_caps = 1;
- caps = spice_new(uint32_t, 1);
- caps[0] = (1 << SPICE_RECORD_CAP_CELT_0_5_1) |
- (1 << SPICE_RECORD_CAP_VOLUME);
- red_channel_set_caps(channel, num_caps, caps);
+ red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
+ red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
record_worker->base_channel = channel;
add_worker(record_worker);
@@ -1538,8 +1522,8 @@ void snd_set_playback_compression(int on)
if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
SndChannel* sndchannel = now->connection;
PlaybackChannel* playback = (PlaybackChannel*)now->connection;
- if (!check_cap(sndchannel->caps, sndchannel->num_caps,
- SPICE_PLAYBACK_CAP_CELT_0_5_1)) {
+ if (!red_channel_client_test_remote_cap(sndchannel->channel_client,
+ SPICE_PLAYBACK_CAP_CELT_0_5_1)) {
ASSERT(playback->mode == SPICE_AUDIO_DATA_MODE_RAW);
continue;
}
diff --git a/server/spicevmc.c b/server/spicevmc.c
index 9ccc0d1..8580984 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -206,7 +206,9 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client,
return;
}
- rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
+ rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream,
+ num_common_caps, common_caps,
+ num_caps, caps);
if (!rcc) {
return;
}
More information about the Spice-commits
mailing list