[Spice-commits] Branch '0.8' - 23 commits - client/application.cpp 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_peer.cpp client/red_peer.h client/red_sw_canvas.cpp client/red_sw_canvas.h common/messages.h configure.ac NEWS server/reds.c server/reds.h server/spice-experimental.h server/spice.h server/spice-server.syms spice.proto
Yonit Halperin
yhalperi at kemper.freedesktop.org
Mon Sep 26 02:56:27 PDT 2011
NEWS | 8
client/application.cpp | 57 ----
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 | 154 +++++++++++-
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_peer.cpp | 51 ++++
client/red_peer.h | 2
client/red_sw_canvas.cpp | 6
client/red_sw_canvas.h | 2
common/messages.h | 2
configure.ac | 4
server/reds.c | 546 +++++++++++++++++++++++++++++++++-----------
server/reds.h | 4
server/spice-experimental.h | 3
server/spice-server.syms | 4
server/spice.h | 29 ++
spice.proto | 9
29 files changed, 1104 insertions(+), 502 deletions(-)
New commits:
commit 4a26fa01cdd1c341d5f0cca08655c9c08064b987
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Wed Sep 21 17:31:00 2011 +0300
Release 0.8.3
diff --git a/NEWS b/NEWS
index ee6ceec..e9ed92e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,11 @@
+Major changes in 0.8.3:
+=======================
+* server Bug fixes (RHBZ): 718713
+* client Bug fixes (RHBZ): 726441, 727969, 728252
+* server: semi-seamless migration support (RHBZ 738266)
+* client: semi-seamless migration support (RHBZ 725009, 738270)
+* require spice-protocol >= 0.8.2
+
Major changes in 0.8.2:
=======================
* server: sasl support (fdo bz 34795)
diff --git a/configure.ac b/configure.ac
index e169f36..7fb636f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ AC_PREREQ([2.57])
m4_define([SPICE_MAJOR], 0)
m4_define([SPICE_MINOR], 8)
-m4_define([SPICE_MICRO], 2)
+m4_define([SPICE_MICRO], 3)
AC_INIT(spice, [SPICE_MAJOR.SPICE_MINOR.SPICE_MICRO], [], spice)
diff --git a/server/spice-server.syms b/server/spice-server.syms
index 826c562..62cdc18 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -81,3 +81,7 @@ global:
spice_qxl_destroy_surface_async;
spice_qxl_flush_surfaces_async;
} SPICE_SERVER_0.8.1;
+
+SPICE_SERVER_0.8.3 {
+ spice_server_migrate_connect;
+} SPICE_SERVER_0.8.2;
diff --git a/server/spice.h b/server/spice.h
index 42ddbc6..ce2d149 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -22,7 +22,7 @@
#include <sys/socket.h>
#include <spice/qxl_dev.h>
-#define SPICE_SERVER_VERSION 0x000802 /* release 0.8.2 */
+#define SPICE_SERVER_VERSION 0x000803 /* release 0.8.3 */
/* interface base type */
commit 4b2bf4d88c253502003aa5e4b93a045742eec9b4
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.
diff --git a/client/red_channel.cpp b/client/red_channel.cpp
index 939c13b..ba057a9 100644
--- a/client/red_channel.cpp
+++ b/client/red_channel.cpp
@@ -295,6 +295,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)
{
@@ -490,6 +504,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 c47d143..0eedc4d 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 3dde7b2..8bf2fe2 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -133,6 +133,7 @@ Migrate::Migrate(RedClient& client)
, _connected (false)
, _thread (NULL)
, _pending_con (0)
+ , _protocol (0)
{
}
@@ -189,7 +190,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;
@@ -212,6 +213,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()
@@ -232,7 +240,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);
}
@@ -440,11 +448,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)
@@ -679,10 +692,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 f47f1cb..b059f32 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 010b22cd771b7e81363b4b6521e4265b093fcd25
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.
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index e6b1af5..3f449a5 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -1042,9 +1042,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);
@@ -1053,7 +1051,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();
@@ -1109,7 +1106,7 @@ void DisplayChannel::destroy_off_screen_surfaces()
}
}
-void DisplayChannel::on_disconnect_mig_src()
+void DisplayChannel::clear(bool destroy_primary)
{
_palette_cache.clear();
destroy_streams();
@@ -1119,7 +1116,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 10e1731..4b3a660 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 cad3c585444f940f60c12789f4174f2d32bec70f
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 21:50:06 2011 +0300
client: display channel migration
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index 6127a96..e6b1af5 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -84,6 +84,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,
@@ -544,6 +566,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)
@@ -571,6 +608,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());
@@ -618,11 +656,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++) {
@@ -1021,6 +1059,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 {
@@ -1362,26 +1469,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_OGL
- 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 cdad5ff..10e1731 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 d3ed9d5e9d52ddcadcb3c8c77dd827b50071d813
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 21:42:18 2011 +0300
client: playback/record channels: implement on_disconnect
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 1e902a9..4a10cf7 100644
--- a/client/playback_channel.cpp
+++ b/client/playback_channel.cpp
@@ -169,19 +169,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 084866c..a655866 100644
--- a/client/record_channel.cpp
+++ b/client/record_channel.cpp
@@ -98,14 +98,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)
@@ -125,6 +118,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);
@@ -174,6 +172,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());
@@ -183,13 +198,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 f91d202eb3bf631cf5e70277d1aabffec7da9393
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.
diff --git a/client/red_client.cpp b/client/red_client.cpp
index f2412de..3dde7b2 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -493,6 +493,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 1d81468..f47f1cb 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 510a4ff7c4f188fe6d0fb12198b8f9fdb74b9a2d
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
diff --git a/client/red_channel.cpp b/client/red_channel.cpp
index fafb2e1..939c13b 100644
--- a/client/red_channel.cpp
+++ b/client/red_channel.cpp
@@ -27,6 +27,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)
@@ -437,6 +446,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 a326680..c47d143 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 *recive();
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_recived();
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 f09e3c9..f2412de 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -23,6 +23,7 @@
#include "utils.h"
#include "debug.h"
#include "marshallers.h"
+#include <algorithm>
#ifndef INFINITY
#define INFINITY HUGE
@@ -120,6 +121,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)
@@ -390,6 +396,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());
@@ -408,6 +415,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);
@@ -419,6 +427,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();
}
@@ -486,6 +496,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();
@@ -613,7 +624,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);
@@ -621,9 +632,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();
@@ -968,12 +1029,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;
@@ -984,20 +1070,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 7fdba44..1d81468 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 31ed2519a752b7332ed40d0d7ab02e938c0e65cb
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 21:21:00 2011 +0300
client: handle SpiceMsgMainMigrationBegin for 0.8.2
RHBZ 725009, 738270
diff --git a/client/red_client.cpp b/client/red_client.cpp
index d7a3e6a..f09e3c9 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -257,9 +257,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;
@@ -271,8 +277,17 @@ 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 = RedPeer::HostAuthOptions::HOST_AUTH_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 = RedPeer::HostAuthOptions::HOST_AUTH_OP_PUBKEY;
+ _auth_options.host_pubkey.assign(migrate->pub_key_data, migrate->pub_key_data +
+ migrate->pub_key_size);
+ } else {
+ _auth_options.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_SUBJECT;
+ _auth_options.CA_file = _client.get_host_auth_options().CA_file;
+ if (migrate->cert_subject_size != 0) {
+ _auth_options.set_cert_subject((char *)migrate->cert_subject_data);
+ }
+ }
}
_con_ciphers = _client.get_connection_ciphers();
commit 59e55605cc0e0cef924a57a14325c3ca9fe2e110
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 15:17:33 2011 +0300
client: RedPeer::HostAuthOptions::set_cert_subject
diff --git a/client/application.cpp b/client/application.cpp
index b3a73bd..634fcdd 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -2015,56 +2015,13 @@ bool Application::set_ca_file(const char* ca_file, const char* arg0)
bool Application::set_host_cert_subject(const char* subject, const char* arg0)
{
- std::string subject_str(subject);
- std::string::const_iterator iter = subject_str.begin();
- std::string entry;
- _host_auth_opt.type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_SUBJECT;
- _host_auth_opt.host_subject.clear();
-
- while (true) {
- if ((iter == subject_str.end()) || (*iter == ',')) {
- RedPeer::HostAuthOptions::CertFieldValuePair entry_pair;
- int value_pos = entry.find_first_of('=');
- if ((value_pos == std::string::npos) || (value_pos == (entry.length() - 1))) {
- Platform::term_printf("%s: host_subject bad format: assignment for %s is missing\n",
- arg0, entry.c_str());
- _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
- return false;
- }
- size_t start_pos = entry.find_first_not_of(' ');
- if ((start_pos == std::string::npos) || (start_pos == value_pos)) {
- Platform::term_printf("%s: host_subject bad format: first part of assignment must be non empty in %s\n",
- arg0, entry.c_str());
- _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
- return false;
- }
- entry_pair.first = entry.substr(start_pos, value_pos - start_pos);
- entry_pair.second = entry.substr(value_pos + 1);
- _host_auth_opt.host_subject.push_back(entry_pair);
- DBG(0, "subject entry: %s=%s", entry_pair.first.c_str(), entry_pair.second.c_str());
- if (iter == subject_str.end()) {
- break;
- }
- entry.clear();
- } else if (*iter == '\\') {
- iter++;
- if (iter == subject_str.end()) {
- LOG_WARN("single \\ in host subject");
- entry.append(1, '\\');
- continue;
- } else if ((*iter == '\\') || (*iter == ',')) {
- entry.append(1, *iter);
- } else {
- LOG_WARN("single \\ in host subject");
- entry.append(1, '\\');
- continue;
- }
- } else {
- entry.append(1, *iter);
- }
- iter++;
- }
- return true;
+ if (!_host_auth_opt.set_cert_subject(subject)) {
+ Platform::term_printf("%s: bad cert subject %s", arg0, subject);
+ _exit_code = SPICEC_ERROR_CODE_INVALID_ARG;
+ return false;
+ }
+
+ return true;
}
bool Application::set_canvas_option(CmdLineParser& parser, char *val, const char* arg0)
diff --git a/client/red_peer.cpp b/client/red_peer.cpp
index 61120b9..0965ac3 100644
--- a/client/red_peer.cpp
+++ b/client/red_peer.cpp
@@ -39,6 +39,57 @@ static void ssl_error()
THROW_ERR(SPICEC_ERROR_CODE_SSL_ERROR, "SSL Error:", ERR_error_string(last_error, NULL));
}
+bool RedPeer::HostAuthOptions::set_cert_subject(const char* subject)
+{
+ std::string subject_str(subject);
+ std::string::const_iterator iter = subject_str.begin();
+ std::string entry;
+ this->type_flags = RedPeer::HostAuthOptions::HOST_AUTH_OP_SUBJECT;
+ this->host_subject.clear();
+
+ while (true) {
+ if ((iter == subject_str.end()) || (*iter == ',')) {
+ RedPeer::HostAuthOptions::CertFieldValuePair entry_pair;
+ int value_pos = entry.find_first_of('=');
+ if ((value_pos == std::string::npos) || (value_pos == (entry.length() - 1))) {
+ LOG_ERROR("host_subject bad format: assignment for %s is missing\n", entry.c_str());
+ return false;
+ }
+ size_t start_pos = entry.find_first_not_of(' ');
+ if ((start_pos == std::string::npos) || (start_pos == value_pos)) {
+ LOG_ERROR("host_subject bad format: first part of assignment"
+ " must be non empty in %s\n", entry.c_str());
+ return false;
+ }
+ entry_pair.first = entry.substr(start_pos, value_pos - start_pos);
+ entry_pair.second = entry.substr(value_pos + 1);
+ this->host_subject.push_back(entry_pair);
+ DBG(0, "subject entry: %s=%s", entry_pair.first.c_str(), entry_pair.second.c_str());
+ if (iter == subject_str.end()) {
+ break;
+ }
+ entry.clear();
+ } else if (*iter == '\\') {
+ iter++;
+ if (iter == subject_str.end()) {
+ LOG_WARN("single \\ in host subject");
+ entry.append(1, '\\');
+ continue;
+ } else if ((*iter == '\\') || (*iter == ',')) {
+ entry.append(1, *iter);
+ } else {
+ LOG_WARN("single \\ in host subject");
+ entry.append(1, '\\');
+ continue;
+ }
+ } else {
+ entry.append(1, *iter);
+ }
+ iter++;
+ }
+ return true;
+}
+
RedPeer::RedPeer()
: _peer (INVALID_SOCKET)
, _shut (false)
diff --git a/client/red_peer.h b/client/red_peer.h
index 53fd3c9..c260935 100644
--- a/client/red_peer.h
+++ b/client/red_peer.h
@@ -52,7 +52,7 @@ public:
typedef std::list<CertFieldValuePair> CertFieldValueList;
HostAuthOptions() : type_flags(0) {}
-
+ bool set_cert_subject(const char* subject);
public:
int type_flags;
commit fcb3b4ce5231218bcf949da4270bd85a2cfb3535
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.
diff --git a/client/canvas.cpp b/client/canvas.cpp
index 284a814..6a50a32 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -21,14 +21,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 4844c31..5deafda 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -31,6 +31,7 @@
#include "glz_decoder.h"
#include "jpeg_decoder.h"
#include "zlib_decoder.h"
+#include <map>
enum CanvasType {
CANVAS_TYPE_INVALID,
@@ -39,102 +40,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)
@@ -201,44 +106,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:
@@ -400,10 +267,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,
@@ -443,7 +320,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;}
@@ -454,7 +331,6 @@ private:
protected:
SpiceCanvas* _canvas;
- CSurfaces _surfaces;
private:
PixmapCache& _pixmap_cache;
@@ -467,7 +343,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 ea0623c..6127a96 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -544,42 +544,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)
@@ -716,11 +680,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);
}
@@ -729,8 +693,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;
@@ -740,13 +704,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();
@@ -886,12 +850,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_OGL
- canvas = surfaces_mngr.get_canvas(0);
+ canvas = _surfaces_cache[0];
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->pre_gl_copy();
}
@@ -915,7 +879,7 @@ void DisplayChannel::pre_migrate()
void DisplayChannel::post_migrate()
{
#ifdef USE_OGL
- 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
@@ -926,15 +890,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 {
@@ -1040,11 +1000,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()) {
@@ -1070,9 +1027,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());
}
@@ -1094,12 +1050,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());
@@ -1118,9 +1073,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());
}
@@ -1136,11 +1090,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_OGL
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
@@ -1148,9 +1104,6 @@ void DisplayChannel::destroy_canvas(int surface_id)
}
#endif
- surfaces_mngr.del_canvas(surface_id);
- surfaces_mngr.del_surface(surface_id);
-
delete canvas;
}
@@ -1168,10 +1121,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)) {
@@ -1205,7 +1161,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,
@@ -1241,10 +1197,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();
@@ -1323,10 +1277,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,
@@ -1429,7 +1387,7 @@ void DisplayChannel::create_primary_surface(int width, int height, uint32_t form
_format = format;
#ifdef USE_OGL
- canvas = surfaces_mngr.get_canvas(0);
+ canvas = _surfaces_cache[0];
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->touch_context();
@@ -1452,7 +1410,7 @@ void DisplayChannel::create_surface(int surface_id, int width, int height, uint3
#ifdef USE_OGL
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();
@@ -1464,11 +1422,8 @@ void DisplayChannel::destroy_primary_surface()
{
if (screen()) {
#ifdef USE_OGL
- 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();
}
@@ -1535,7 +1490,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) {
@@ -1547,7 +1502,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);
}
@@ -1555,7 +1510,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);
}
@@ -1563,7 +1518,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);
}
@@ -1571,7 +1526,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);
}
@@ -1579,7 +1534,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);
}
@@ -1587,7 +1542,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);
}
@@ -1595,7 +1550,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);
}
@@ -1603,7 +1558,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);
}
@@ -1611,7 +1566,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);
}
@@ -1619,7 +1574,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);
}
@@ -1627,7 +1582,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);
}
@@ -1635,7 +1590,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 e6cd227..cdad5ff 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 72b31df..0c38832 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -25,7 +25,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)
{
@@ -36,7 +36,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 643f3c6..b88c298 100644
--- a/client/red_gdi_canvas.h
+++ b/client/red_gdi_canvas.h
@@ -29,7 +29,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 d7841b9..e2bff7f 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -27,7 +27,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)
@@ -39,7 +39,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 0260958..8c19882 100644
--- a/client/red_gl_canvas.h
+++ b/client/red_gl_canvas.h
@@ -31,7 +31,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 b580e61..08d4e09 100644
--- a/client/red_sw_canvas.cpp
+++ b/client/red_sw_canvas.cpp
@@ -28,7 +28,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)
{
@@ -41,7 +41,7 @@ SCanvas::SCanvas(bool onscreen,
_pixmap->get_stride(),
&pixmap_cache.base,
&palette_cache.base,
- &csurfaces.base,
+ &csurfaces,
&glz_decoder(),
&jpeg_decoder(),
&zlib_decoder());
@@ -49,7 +49,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 ebac710..cf97d1d 100644
--- a/client/red_sw_canvas.h
+++ b/client/red_sw_canvas.h
@@ -29,7 +29,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 f29dc9b6201f4a575ebb1f2ea61775ab46f4ad1f
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Mon Sep 26 10:16:51 2011 +0300
server: fix not calling migrate_connect completion callback
When the server is a migration target and spice_server_migrate_connect
is called before SPICE_MSGC_MIGRATE_END has been received, we start
the mig_timer. We handle the migrate_connect only when receiving SPICE_MSGC_MIGRATE_END.
If the mig_timer expires before that, we dismiss the request, and should call the
migrate_connect completion callback. Since reds->mig_inprogress
wasn't set appropriately, it wasn't called.
diff --git a/server/reds.c b/server/reds.c
index 10d2ffc..8e83b99 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -4205,22 +4205,20 @@ static void reds_mig_started(void)
reds->expect_migrate = TRUE;
if (reds->client_semi_mig_cap) {
+ reds->mig_inprogress = TRUE;
if (reds->mig_target) {
red_printf("previous spice migration hasn't completed yet. Waiting for client");
reds->mig_wait_prev_complete = TRUE;
core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
- return;
+ } else {
+ reds_mig_connect();
}
- } else if (sif) {
- // switch host msg will be sent after migration completes
- sif->migrate_connect_complete(migration_interface);
- return;
+ } else {
+ if (sif) {
+ // switch host msg will be sent after migration completes
+ sif->migrate_connect_complete(migration_interface);
+ }
}
-
- reds->mig_inprogress = TRUE;
-
- reds_mig_connect();
- return;
}
static void reds_mig_finished(int completed)
commit 524fcb3aa460b200ed038fbbab9cecf5c736cde2
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Tue Sep 20 21:00:02 2011 +0300
server: fall back to switch host scheme in case semi-seamless connection to target fails
diff --git a/server/reds.c b/server/reds.c
index 8ee002f..10d2ffc 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -283,6 +283,7 @@ typedef struct RedsState {
int mig_wait_disconnect;
int mig_wait_prev_complete;
int mig_inprogress;
+ int mig_connect_ok;
int expect_migrate;
int mig_target;
RedsMigSpice *mig_spice;
@@ -1736,13 +1737,14 @@ static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, v
case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
red_printf("client connected to migration target");
if (reds->mig_wait_connect) {
+ reds->mig_connect_ok = TRUE;
reds_mig_cleanup();
}
break;
case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
- // TODO: fall into switch host in case of connect error or timeout
red_printf("mig connect error");
if (reds->mig_wait_connect) {
+ reds->mig_connect_ok = FALSE;
reds_mig_cleanup();
}
break;
@@ -2172,6 +2174,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
reds->mig_inprogress = FALSE;
reds->mig_wait_connect = FALSE;
reds->mig_wait_disconnect = FALSE;
+ reds->mig_connect_ok = FALSE;
reds->stream = link->stream;
reds->in_handler.shut = FALSE;
@@ -4150,8 +4153,6 @@ static void reds_mig_connect(void)
reds_push_pipe_item(item);
- reds_mig_release();
-
reds->mig_wait_connect = TRUE;
core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
}
@@ -4193,6 +4194,7 @@ static void reds_mig_started(void)
reds_listen_stop();
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+ reds->mig_connect_ok = FALSE;
if (reds->stream == NULL) {
red_printf("not connected to stream");
@@ -4226,7 +4228,7 @@ static void reds_mig_finished(int completed)
RedsOutItem *item;
red_printf("");
-
+ reds_mig_release();
if (reds->stream == NULL) {
red_printf("no stream connected");
return;
@@ -4297,7 +4299,12 @@ static void migrate_timeout(void *opaque)
{
red_printf("");
ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect || reds->mig_wait_prev_complete);
- reds_mig_disconnect();
+ if (reds->mig_wait_connect) {
+ reds->mig_connect_ok = FALSE;
+ reds_mig_cleanup();
+ } else {
+ reds_mig_disconnect();
+ }
}
static void key_modifiers_sender(void *opaque)
@@ -5168,7 +5175,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
SpiceMigrateInterface *sif;
int ret = 0;
- red_printf("");
+ red_printf("completed=%d", completed);
ASSERT(migration_interface);
ASSERT(reds == s);
@@ -5187,11 +5194,12 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
goto complete;
}
- if (reds->client_semi_mig_cap) {
+ if (reds->client_semi_mig_cap && reds->mig_connect_ok) {
reds_mig_finished(completed);
} else {
- ret = spice_server_migrate_switch(s);
- goto complete;
+ if (completed) {
+ ret = spice_server_migrate_switch(s);
+ }
}
ret = 0;
complete:
commit b8213167717979e6f2fb52646e43eb458634e6a1
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.
diff --git a/server/reds.c b/server/reds.c
index e61751d..8ee002f 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -5142,16 +5142,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 ddf1188b30aed7f66aefdaac61835ee5371e9747
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Tue Sep 20 13:27:37 2011 +0300
server: call migrate_connect_complete callback when no client is connected
diff --git a/server/reds.c b/server/reds.c
index 0f67a95..e61751d 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -761,6 +761,7 @@ static void reds_disconnect()
reds->net_test_id = 0;
reds->net_test_stage = NET_TEST_STAGE_INVALID;
reds->in_handler.end_pos = 0;
+ reds->expect_migrate = FALSE;
bitrate_per_sec = ~0;
latency = 0;
@@ -1739,6 +1740,7 @@ static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, v
}
break;
case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
+ // TODO: fall into switch host in case of connect error or timeout
red_printf("mig connect error");
if (reds->mig_wait_connect) {
reds_mig_cleanup();
@@ -4123,7 +4125,7 @@ static void reds_mig_release(void)
}
}
-static void reds_mig_continue(void)
+static void reds_mig_connect(void)
{
RedsMigSpice *s = reds->mig_spice;
SpiceMsgMainMigrationBegin migrate;
@@ -4180,22 +4182,43 @@ static void reds_listen_stop(void)
static void reds_mig_started(void)
{
+ SpiceMigrateInterface *sif = NULL;
+
red_printf("");
ASSERT(reds->mig_spice);
- reds->mig_inprogress = TRUE;
+ if (!migration_interface) {
+ return;
+ }
+
+ reds_listen_stop();
+ sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
if (reds->stream == NULL) {
red_printf("not connected to stream");
- goto error;
+ reds_mig_release();
+ sif->migrate_connect_complete(migration_interface);
+ return;
}
- reds_mig_continue();
- return;
+ reds->expect_migrate = TRUE;
+ if (reds->client_semi_mig_cap) {
+ if (reds->mig_target) {
+ red_printf("previous spice migration hasn't completed yet. Waiting for client");
+ reds->mig_wait_prev_complete = TRUE;
+ core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
+ return;
+ }
+ } else if (sif) {
+ // switch host msg will be sent after migration completes
+ sif->migrate_connect_complete(migration_interface);
+ return;
+ }
-error:
- reds_mig_release();
- reds_mig_disconnect();
+ reds->mig_inprogress = TRUE;
+
+ reds_mig_connect();
+ return;
}
static void reds_mig_finished(int completed)
@@ -5059,6 +5082,7 @@ static int reds_set_migration_dest_info(const char* dest,
RedsMigSpice *spice_migration = NULL;
reds_mig_release();
+
if ((port == -1 && secure_port == -1) || !dest) {
return FALSE;
}
@@ -5081,7 +5105,6 @@ 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);
@@ -5091,27 +5114,14 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
reds_mig_finished(FALSE);
}
- sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
-
if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
+ SpiceMigrateInterface *sif;
+ sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
sif->migrate_connect_complete(migration_interface);
return -1;
}
+ reds_mig_started();
- reds->expect_migrate = TRUE;
- reds_listen_stop();
-
- if (reds->client_semi_mig_cap) {
- if (!reds->mig_target) {
- reds_mig_started();
- } else {
- red_printf("previous spice migration hasn't completed yet. Waiting for client");
- reds->mig_wait_prev_complete = TRUE;
- core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
- }
- } else {
- sif->migrate_connect_complete(migration_interface);
- }
return 0;
}
@@ -5165,20 +5175,24 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
int ret = 0;
red_printf("");
-
ASSERT(migration_interface);
ASSERT(reds == s);
reds_listen_start();
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
- if (!reds->expect_migrate && reds->stream) {
- red_printf("spice_server_migrate_info was not called, disconnecting client");
+
+ if (!reds->stream) {
+ ret = 0;
+ goto complete;
+ }
+
+ if (!reds->expect_migrate) {
+ red_printf("spice_server_migrate_info failed or was not called, disconnecting client");
reds_disconnect("");
ret = -1;
goto complete;
}
- reds->expect_migrate = FALSE;
if (reds->client_semi_mig_cap) {
reds_mig_finished(completed);
} else {
commit 4568f2dbbe9a5c8d31ac83d516442ba3b90f51e1
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 14:16:09 2011 +0300
server: handling semi-seamless migration in the target side
(1) not sending anything to the client till we recieve SPICE_MSGC_MIGRATE_END
(2) start a new migration (handle client_migrate_info) only after SPICE_MSGC_MIGRATE_END
from the previous migration has been received
(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 client.
diff --git a/server/reds.c b/server/reds.c
index 6e7b891..0f67a95 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -258,6 +258,13 @@ typedef struct RedsStatValue {
#endif
typedef struct RedsMigSpice RedsMigSpice;
+typedef struct RedsMigPendingLink RedsMigPendingLink;
+
+struct RedsMigPendingLink {
+ RedsMigPendingLink *next;
+ SpiceLinkMess *link_msg;
+ RedsStream *stream;
+};
typedef struct RedsState {
int listen_socket;
@@ -274,10 +281,13 @@ typedef struct RedsState {
int client_semi_mig_cap;
int mig_wait_connect;
int mig_wait_disconnect;
+ int mig_wait_prev_complete;
int mig_inprogress;
int expect_migrate;
int mig_target;
RedsMigSpice *mig_spice;
+ RedsMigPendingLink *mig_pending_links;
+ int num_mig_links;
int num_of_channels;
IncomingHandler in_handler;
RedsOutgoingData outgoing;
@@ -293,7 +303,6 @@ typedef struct RedsState {
SpiceTimer *vdi_port_write_timer;
int vdi_port_write_timer_started;
- TicketAuthentication taTicket;
SSL_CTX *ctx;
#ifdef RED_STATISTICS
@@ -382,6 +391,9 @@ static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
static void reds_push();
static void reds_out_item_free(RedsOutItem *item);
static void migrate_timeout(void *opaque);
+static void reds_mig_pending_links_free(void);
+static void reds_handle_client_migrate_complete(void);
+static void reds_mig_started(void);
static ChannelSecurityOptions *channels_security = NULL;
static int default_channel_security =
@@ -648,7 +660,7 @@ static void reds_shatdown_channels()
static void reds_mig_cleanup()
{
if (reds->mig_inprogress) {
- if (reds->mig_wait_connect) {
+ if (reds->mig_wait_connect || reds->mig_wait_prev_complete) {
SpiceMigrateInterface *sif;
ASSERT(migration_interface);
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
@@ -657,8 +669,13 @@ static void reds_mig_cleanup()
reds->mig_inprogress = FALSE;
reds->mig_wait_connect = FALSE;
reds->mig_wait_disconnect = FALSE;
+ reds->mig_wait_prev_complete = FALSE;
core->timer_cancel(reds->mig_timer);
}
+ if (reds->num_mig_links) {
+ ASSERT(reds->mig_target);
+ reds_mig_pending_links_free();
+ }
}
static void reds_reset_vdp()
@@ -925,6 +942,11 @@ static void reds_send_channels()
Channel *channel;
int i;
+ if (reds->mig_target) {
+ red_printf("warning: unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS during migration");
+ return;
+ }
+
item = new_out_item(SPICE_MSG_MAIN_CHANNELS_LIST);
channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels) + reds->num_of_channels * sizeof(SpiceChannelId));
channels_info->num_of_channels = reds->num_of_channels;
@@ -1008,7 +1030,7 @@ static void reds_send_mouse_mode()
SpiceMsgMainMouseMode mouse_mode;
RedsOutItem *item;
- if (!reds->stream) {
+ if (!reds->stream || reds->mig_target) {
return;
}
@@ -1077,6 +1099,7 @@ static void reds_agent_remove()
SpiceCharDeviceInstance *sin = vdagent;
SpiceCharDeviceInterface *sif;
+ // TODO: is this cond needed
if (!reds->mig_target) {
reds_reset_vdp();
}
@@ -1103,7 +1126,7 @@ static void reds_send_tokens()
SpiceMsgMainAgentTokens tokens;
RedsOutItem *item;
- if (!reds->stream) {
+ if (!reds->stream || reds->mig_target) {
return;
}
@@ -1798,6 +1821,18 @@ static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, v
break;
case SPICE_MSGC_DISCONNECTING:
break;
+ case SPICE_MSGC_MAIN_MIGRATE_END:
+ if (!reds->mig_target) {
+ red_printf("unexpected SPICE_MSGC_MIGRATE_END, not target");
+ return;
+ }
+ if (!reds->client_semi_mig_cap) {
+ red_printf("unexpected SPICE_MSGC_MIGRATE_END"
+ ",client does not support semi-seamless migration");
+ return;
+ }
+ reds_handle_client_migrate_complete();
+ break;
default:
red_printf("unexpected type %d", type);
}
@@ -2122,14 +2157,10 @@ 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;
} else {
- if (link_mess->connection_id != reds->link_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;
@@ -2158,6 +2189,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
link->stream = NULL;
link->link_mess = NULL;
reds_link_free(link);
+ // TODO: should this be moved to be done only after mig completed (reds_main_channel_init)?
if (vdagent) {
SpiceCharDeviceInterface *sif;
sif = SPICE_CONTAINEROF(vdagent->base.sif, SpiceCharDeviceInterface, base);
@@ -2612,6 +2644,32 @@ static void inputs_init()
reds_register_channel(channel);
}
+static void reds_mig_pending_link_add(SpiceLinkMess *link_msg, RedsStream *stream)
+{
+ RedsMigPendingLink *mig_link;
+
+ ASSERT(reds);
+ mig_link = spice_malloc0(sizeof(RedsMigPendingLink));
+ mig_link->link_msg = link_msg;
+ mig_link->stream = stream;
+
+ mig_link->next = reds->mig_pending_links;
+ reds->mig_pending_links = mig_link;
+ reds->num_mig_links++;
+}
+
+static void reds_mig_pending_links_free(void)
+{
+ red_printf("");
+ while(reds->mig_pending_links) {
+ RedsMigPendingLink *tmp = reds->mig_pending_links;
+ reds->mig_pending_links = tmp->next;
+ free(tmp->link_msg);
+ free(tmp);
+ }
+
+ reds->num_mig_links = 0;
+}
static void reds_send_input_channel_insecure_warn()
{
RedsOutItem *item;
@@ -2650,6 +2708,35 @@ static void reds_channel_do_link(Channel *channel, SpiceLinkMess *link_msg, Reds
link_msg->num_channel_caps ? caps + link_msg->num_common_caps : NULL);
}
+static void reds_handle_client_migrate_complete(void)
+{
+ RedsMigPendingLink *cur_link;
+
+ red_printf("");
+ // TODO: not doing net test. consider doing it on client_migrate_info
+ reds_main_channel_init(FALSE);
+
+ for (cur_link = reds->mig_pending_links; cur_link; cur_link = cur_link->next) {
+ Channel *channel = reds_find_channel(cur_link->link_msg->channel_type,
+ cur_link->link_msg->channel_id);
+ if (!channel) {
+ red_printf("warning: channel (%d, %d) (type,id) wasn't found",
+ cur_link->link_msg->channel_type, cur_link->link_msg->channel_id);
+ continue;
+ }
+ reds_channel_do_link(channel, cur_link->link_msg, cur_link->stream);
+ }
+
+ reds_mig_pending_links_free();
+ reds->mig_target = FALSE;
+ if (reds->mig_wait_prev_complete) {
+ reds->mig_wait_prev_complete = FALSE;
+ core->timer_cancel(reds->mig_timer);
+ // starting a pending migrate info command
+ reds_mig_started();
+ }
+}
+
static void reds_handle_other_links(RedLinkInfo *link)
{
Channel *channel;
@@ -2674,8 +2761,12 @@ static void reds_handle_other_links(RedLinkInfo *link)
reds_show_new_channel(link, reds->link_id);
reds_stream_remove_watch(link->stream);
- reds_channel_do_link(channel, link_mess, link->stream);
- free(link_mess);
+ if (reds->mig_target) {
+ reds_mig_pending_link_add(link_mess, link->stream);
+ } else {
+ reds_channel_do_link(channel, link_mess, link->stream);
+ free(link_mess);
+ }
link->stream = NULL;
link->link_mess = NULL;
@@ -2704,10 +2795,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");
@@ -2715,7 +2805,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;
@@ -4183,7 +4273,7 @@ static void reds_mig_switch(void)
static void migrate_timeout(void *opaque)
{
red_printf("");
- ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
+ ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect || reds->mig_wait_prev_complete);
reds_mig_disconnect();
}
@@ -4210,7 +4300,7 @@ void reds_enable_mm_timer(void)
RedsOutItem *item;
core->timer_start(reds->mm_timer, MM_TIMER_GRANULARITY_MS);
- if (!reds->stream) {
+ if (!reds->stream || reds->mig_target) {
return;
}
@@ -5012,7 +5102,13 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
reds_listen_stop();
if (reds->client_semi_mig_cap) {
- reds_mig_started();
+ if (!reds->mig_target) {
+ reds_mig_started();
+ } else {
+ red_printf("previous spice migration hasn't completed yet. Waiting for client");
+ reds->mig_wait_prev_complete = TRUE;
+ core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
+ }
} else {
sif->migrate_connect_complete(migration_interface);
}
commit 0e57df1dd16def2ff295288e83623e2ef991b9f0
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 14:05:00 2011 +0300
server: move the linking of channels to a separate routine
diff --git a/server/reds.c b/server/reds.c
index 8ed84a8..6e7b891 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -2612,12 +2612,48 @@ static void inputs_init()
reds_register_channel(channel);
}
+static void reds_send_input_channel_insecure_warn()
+{
+ RedsOutItem *item;
+ SpiceMsgNotify notify;
+ char *mess = "keyboard channel is insecure";
+ const int mess_len = strlen(mess);
+
+ item = new_out_item(SPICE_MSG_NOTIFY);
+
+ notify.time_stamp = get_time_stamp();
+ notify.severity = SPICE_NOTIFY_SEVERITY_WARN;
+ notify.visibilty = SPICE_NOTIFY_VISIBILITY_HIGH;
+ notify.what = SPICE_WARN_GENERAL;
+ notify.message_len = mess_len;
+
+ spice_marshall_msg_notify(item->m, ¬ify);
+ spice_marshaller_add(item->m, (uint8_t *)mess, mess_len + 1);
+
+ reds_push_pipe_item(item);
+}
+
+static void reds_channel_do_link(Channel *channel, 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) {
+ reds_send_input_channel_insecure_warn();
+ }
+ caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset);
+ channel->link(channel, stream, FALSE, 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)
{
Channel *channel;
- RedsStream *stream;
SpiceLinkMess *link_mess;
- uint32_t *caps;
link_mess = link->link_mess;
@@ -2636,35 +2672,14 @@ static void reds_handle_other_links(RedLinkInfo *link)
reds_send_link_result(link, SPICE_LINK_ERR_OK);
reds_show_new_channel(link, reds->link_id);
- if (link_mess->channel_type == SPICE_CHANNEL_INPUTS && !link->stream->ssl) {
- RedsOutItem *item;
- SpiceMsgNotify notify;
- char *mess = "keyboard channel is insecure";
- const int mess_len = strlen(mess);
-
- item = new_out_item(SPICE_MSG_NOTIFY);
-
- notify.time_stamp = get_time_stamp();
- notify.severity = SPICE_NOTIFY_SEVERITY_WARN;
- notify.visibilty = SPICE_NOTIFY_VISIBILITY_HIGH;
- notify.what = SPICE_WARN_GENERAL;
- notify.message_len = mess_len;
+ reds_stream_remove_watch(link->stream);
- spice_marshall_msg_notify(item->m, ¬ify);
- spice_marshaller_add(item->m, (uint8_t *)mess, mess_len + 1);
+ reds_channel_do_link(channel, link_mess, link->stream);
+ free(link_mess);
- reds_push_pipe_item(item);
- }
- stream = link->stream;
- reds_stream_remove_watch(stream);
link->stream = NULL;
link->link_mess = NULL;
reds_link_free(link);
- caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
- channel->link(channel, 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 1081d8ccf0ec677e43f387f74f27dee3fe187cf0
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 13:37:09 2011 +0300
server: move SPICE_MSG_MAIN_INIT sending code to a separate routine
diff --git a/server/reds.c b/server/reds.c
index 4ffdfb2..8ed84a8 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -2078,6 +2078,35 @@ static int test_capability(uint32_t *caps, uint32_t num_caps, uint32_t cap)
return (caps[index] & (1 << (cap % 32))) != 0;
}
+static void reds_main_channel_init(int do_net_test)
+{
+ RedsOutItem *item;
+ SpiceMsgMainInit init;
+
+ item = new_out_item(SPICE_MSG_MAIN_INIT);
+ init.session_id = reds->link_id;
+ init.display_channels_hint = red_dispatcher_count();
+ init.current_mouse_mode = reds->mouse_mode;
+ init.supported_mouse_modes = SPICE_MOUSE_MODE_SERVER;
+ if (reds->is_client_mouse_allowed) {
+ init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT;
+ }
+ init.agent_connected = !!vdagent;
+ init.agent_tokens = REDS_AGENT_WINDOW_SIZE;
+ reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
+ init.multi_media_time = reds_get_mm_time() - MM_TIME_DELTA;
+ init.ram_hint = red_dispatcher_qxl_ram_size();
+
+ spice_marshall_msg_main_init(item->m, &init);
+
+ reds_push_pipe_item(item);
+ if (do_net_test) {
+ reds_start_net_test();
+ }
+ /* Now that we have a client, forward any pending agent data */
+ while (read_from_vdi_port());
+}
+
static void reds_handle_main_link(RedLinkInfo *link)
{
SpiceLinkMess *link_mess = link->link_mess;
@@ -2144,29 +2173,10 @@ static void reds_handle_main_link(RedLinkInfo *link)
reds_main_event, NULL);
if (!reds->mig_target) {
- RedsOutItem *item;
- SpiceMsgMainInit init;
-
- item = new_out_item(SPICE_MSG_MAIN_INIT);
- init.session_id = connection_id;
- init.display_channels_hint = red_dispatcher_count();
- init.current_mouse_mode = reds->mouse_mode;
- init.supported_mouse_modes = SPICE_MOUSE_MODE_SERVER;
- if (reds->is_client_mouse_allowed) {
- init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT;
- }
- init.agent_connected = !!vdagent;
- init.agent_tokens = REDS_AGENT_WINDOW_SIZE;
- reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
- init.multi_media_time = reds_get_mm_time() - MM_TIME_DELTA;
- init.ram_hint = red_dispatcher_qxl_ram_size();
-
- spice_marshall_msg_main_init(item->m, &init);
-
- reds_push_pipe_item(item);
- reds_start_net_test();
- /* Now that we have a client, forward any pending agent data */
- while (read_from_vdi_port());
+ reds_main_channel_init(TRUE);
+ }
+ else {
+ ASSERT(reds->client_semi_mig_cap);
}
}
commit 4b82580fc36228af13db4ac3c403753d6b5c40b5
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 13:09:52 2011 +0300
server: send SPICE_MSG_MAIN_MIGRATE_END on spice_server_migrate_end
diff --git a/server/reds.c b/server/reds.c
index 845b0ee..4ffdfb2 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -275,6 +275,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;
@@ -4038,13 +4039,21 @@ static void reds_mig_continue(void)
core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
}
-static void reds_mig_started(void)
+static void reds_listen_start(void)
{
- red_printf("");
- ASSERT(reds->mig_spice);
+ ASSERT(reds);
+ if (reds->listen_watch != NULL) {
+ core->watch_update_mask(reds->listen_watch, SPICE_WATCH_EVENT_READ);
+ }
- reds->mig_inprogress = TRUE;
+ if (reds->secure_listen_watch != NULL) {
+ core->watch_update_mask(reds->secure_listen_watch, SPICE_WATCH_EVENT_READ);
+ }
+}
+static void reds_listen_stop(void)
+{
+ ASSERT(reds);
if (reds->listen_watch != NULL) {
core->watch_update_mask(reds->listen_watch, 0);
}
@@ -4052,6 +4061,14 @@ static void reds_mig_started(void)
if (reds->secure_listen_watch != NULL) {
core->watch_update_mask(reds->secure_listen_watch, 0);
}
+}
+
+static void reds_mig_started(void)
+{
+ red_printf("");
+ ASSERT(reds->mig_spice);
+
+ reds->mig_inprogress = TRUE;
if (reds->stream == NULL) {
red_printf("not connected to stream");
@@ -4071,13 +4088,6 @@ static void reds_mig_finished(int completed)
RedsOutItem *item;
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->stream == NULL) {
red_printf("no stream connected");
@@ -4086,22 +4096,25 @@ static void reds_mig_finished(int completed)
reds->mig_inprogress = TRUE;
if (completed) {
+#ifdef SPICE_SEAMLESS_MIGRATION
Channel *channel;
SpiceMsgMigrate migrate;
- reds->mig_wait_disconnect = TRUE;
- core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
-
item = new_out_item(SPICE_MSG_MIGRATE);
migrate.flags = SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER;
spice_marshall_msg_migrate(item->m, &migrate);
-
reds_push_pipe_item(item);
channel = reds->channels;
while (channel) {
channel->migrate(channel);
channel = channel->next;
}
+#else
+ item = new_out_item(SPICE_MSG_MAIN_MIGRATE_END);
+ reds_push_pipe_item(item);
+#endif
+ reds->mig_wait_disconnect = TRUE;
+ core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
} else {
item = new_out_item(SPICE_MSG_MAIN_MIGRATE_CANCEL);
reds_push_pipe_item(item);
@@ -4116,6 +4129,8 @@ static void reds_mig_switch(void)
RedsOutItem *item;
if (s == NULL) {
+ red_printf("warning: migration target was not set. disconnecting client");
+ reds_disconnect();
return;
}
@@ -4956,6 +4971,11 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
ASSERT(migration_interface);
ASSERT(reds == s);
+ if (reds->expect_migrate && reds->client_semi_mig_cap) {
+ red_printf("warning: consecutive calls without migration. Canceling previous call");
+ reds_mig_finished(FALSE);
+ }
+
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
@@ -4963,6 +4983,9 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
return -1;
}
+ reds->expect_migrate = TRUE;
+ reds_listen_stop();
+
if (reds->client_semi_mig_cap) {
reds_mig_started();
} else {
@@ -4975,13 +4998,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;
}
@@ -5018,20 +5041,46 @@ 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);
+
+ reds_listen_start();
sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+ if (!reds->expect_migrate && reds->stream) {
+ red_printf("spice_server_migrate_info was not called, disconnecting client");
+ reds_disconnect("");
+ ret = -1;
+ goto complete;
+ }
+
+ reds->expect_migrate = FALSE;
+ if (reds->client_semi_mig_cap) {
+ reds_mig_finished(completed);
+ } else {
+ ret = spice_server_migrate_switch(s);
+ goto complete;
+ }
+ 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->stream) {
+ return 0;
+ }
+ reds->expect_migrate = FALSE;
reds_mig_switch();
return 0;
}
commit cfbd07710562e522179ae5a7085a789489a821bb
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
diff --git a/spice.proto b/spice.proto
index d5b954e..235ec95 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 5560c56ef05c74da5e0e0825dc1f134019593cad
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 10:31:38 2011 +0300
server,proto: tell the client to connect to the migration target before migraton starts
(1) send SPICE_MSG_MAIN_MIGRATE_BEGIN upon spice_server_migrate_connect
(2) wait for SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR), or a timeout, in order
to complete client_migrate_info monitor command
diff --git a/common/messages.h b/common/messages.h
index 6fcd8be..16ae05b 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/reds.c b/server/reds.c
index 99d52f9..845b0ee 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -271,6 +271,7 @@ typedef struct RedsState {
VDIPortState agent_state;
InputsState *inputs_state;
+ int client_semi_mig_cap;
int mig_wait_connect;
int mig_wait_disconnect;
int mig_inprogress;
@@ -280,6 +281,7 @@ typedef struct RedsState {
IncomingHandler in_handler;
RedsOutgoingData outgoing;
Channel *channels;
+ Channel main_channel;
int mouse_mode;
int is_client_mouse_allowed;
int dispatcher_allows_client_mouse;
@@ -378,6 +380,7 @@ static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
static void reds_push();
static void reds_out_item_free(RedsOutItem *item);
+static void migrate_timeout(void *opaque);
static ChannelSecurityOptions *channels_security = NULL;
static int default_channel_security =
@@ -644,6 +647,12 @@ static void reds_shatdown_channels()
static void reds_mig_cleanup()
{
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;
@@ -1700,7 +1709,7 @@ static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, v
reds_send_channels();
break;
case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
- red_printf("connected");
+ red_printf("client connected to migration target");
if (reds->mig_wait_connect) {
reds_mig_cleanup();
}
@@ -1891,6 +1900,23 @@ static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
return TRUE;
}
+static void reds_channel_set_caps(Channel *channel, int cap, int active)
+{
+ int nbefore, n;
+
+ nbefore = channel->num_caps;
+ n = cap / 32;
+ channel->num_caps = MAX(channel->num_caps, n + 1);
+ channel->caps = spice_renew(uint32_t, channel->caps, channel->num_caps);
+ memset(channel->caps + nbefore, 0,
+ (channel->num_caps - nbefore) * sizeof(uint32_t));
+ if (active) {
+ channel->caps[n] |= (1 << cap);
+ } else {
+ channel->caps[n] &= ~(1 << cap);
+ }
+}
+
static void reds_channel_set_common_caps(Channel *channel, int cap, int active)
{
int nbefore, n;
@@ -1933,7 +1959,6 @@ static int reds_send_link_ack(RedLinkInfo *link)
{
SpiceLinkHeader header;
SpiceLinkReply ack;
- Channel caps = { 0, };
Channel *channel;
BUF_MEM *bmBuf;
BIO *bio;
@@ -1948,7 +1973,8 @@ static int reds_send_link_ack(RedLinkInfo *link)
channel = reds_find_channel(link->link_mess->channel_type, 0);
if (!channel) {
- channel = ∩︀
+ ASSERT(link->link_mess->channel_type == SPICE_CHANNEL_MAIN);
+ channel = &reds->main_channel;
}
reds_channel_init_auth_caps(channel); /* make sure common caps are set */
@@ -1989,7 +2015,6 @@ static int reds_send_link_ack(RedLinkInfo *link)
ret = TRUE;
end:
- reds_channel_dispose(&caps);
BIO_free(bio);
return ret;
}
@@ -2042,28 +2067,41 @@ static void reds_start_net_test()
}
}
+static int test_capability(uint32_t *caps, uint32_t num_caps, uint32_t cap)
+{
+ uint32_t index = cap / 32;
+ if (num_caps < index + 1) {
+ return FALSE;
+ }
+
+ return (caps[index] & (1 << (cap % 32))) != 0;
+}
+
static void reds_handle_main_link(RedLinkInfo *link)
{
+ SpiceLinkMess *link_mess = link->link_mess;
uint32_t connection_id;
+ uint32_t *channel_caps;
+ uint32_t num_channel_caps;
red_printf("");
reds_disconnect();
- if (!link->link_mess->connection_id) {
+ if (!link_mess->connection_id) {
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;
} else {
- if (link->link_mess->connection_id != reds->link_id) {
+ if (link_mess->connection_id != reds->link_id) {
reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
reds_link_free(link);
return;
}
reds_send_link_result(link, SPICE_LINK_ERR_OK);
- connection_id = link->link_mess->connection_id;
+ connection_id = link_mess->connection_id;
reds->mig_target = TRUE;
}
@@ -2074,6 +2112,17 @@ static void reds_handle_main_link(RedLinkInfo *link)
reds->stream = link->stream;
reds->in_handler.shut = FALSE;
+ num_channel_caps = link_mess->num_channel_caps;
+ channel_caps = num_channel_caps ? (uint32_t *)((uint8_t *)link_mess +
+ link_mess->caps_offset) + link_mess->num_common_caps :
+ NULL;
+ reds->client_semi_mig_cap = test_capability(channel_caps, num_channel_caps, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
+ if (reds->client_semi_mig_cap && (SPICE_VERSION_MAJOR == 2) && (reds->peer_minor_version < 1)) {
+ red_printf("warning: client claims to support semi seamless migration,"
+ "but its version is incompatible");
+ reds->client_semi_mig_cap = FALSE;
+ }
+
reds_show_new_channel(link, connection_id);
reds_stream_remove_watch(link->stream);
link->stream = NULL;
@@ -2534,6 +2583,12 @@ static void openssl_init(RedLinkInfo *link)
BN_set_word(link->tiTicketing.bn, f4);
}
+static void main_init()
+{
+ reds->main_channel.type = SPICE_CHANNEL_MAIN;
+ reds_channel_set_caps(&reds->main_channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE, TRUE);
+}
+
static void inputs_init()
{
Channel *channel;
@@ -3926,15 +3981,11 @@ 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 {
@@ -3963,15 +4014,20 @@ static void reds_mig_continue(void)
RedsOutItem *item;
red_printf("");
+ ASSERT(reds->client_semi_mig_cap);
item = new_out_item(SPICE_MSG_MAIN_MIGRATE_BEGIN);
migrate.port = s->port;
migrate.sport = s->sport;
migrate.host_size = strlen(s->host) + 1;
migrate.host_data = (uint8_t *)s->host;
- migrate.pub_key_type = s->cert_pub_key_type;
- migrate.pub_key_size = s->cert_pub_key_len;
- migrate.pub_key_data = s->cert_pub_key;
+ 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;
+ }
spice_marshall_msg_main_migrate_begin(item->m, &migrate);
reds_push_pipe_item(item);
@@ -3985,6 +4041,7 @@ static void reds_mig_continue(void)
static void reds_mig_started(void)
{
red_printf("");
+ ASSERT(reds->mig_spice);
reds->mig_inprogress = TRUE;
@@ -4001,12 +4058,6 @@ static void reds_mig_started(void)
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;
@@ -4089,7 +4140,7 @@ static void reds_mig_switch(void)
reds_mig_release();
}
-static void migrate_timout(void *opaque)
+static void migrate_timeout(void *opaque)
{
red_printf("");
ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
@@ -4477,7 +4528,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
init_vd_agent_resources();
- 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->key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
@@ -4540,6 +4591,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
}
#endif
+ main_init();
inputs_init();
reds->mouse_mode = SPICE_MOUSE_MODE_SERVER;
@@ -4869,6 +4921,31 @@ 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,
@@ -4879,10 +4956,18 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
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 (reds->client_semi_mig_cap) {
+ reds_mig_started();
+ } else {
+ sif->migrate_connect_complete(migration_interface);
+ }
return 0;
}
@@ -4890,27 +4975,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/spice.proto b/spice.proto
index 6160de1..d5b954e 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 55ccc022ec9829523ebe36fdf0ec7c593ce76c22
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 13:16:45 2011 +0300
configure: spice-protocol >= 0.8.2 (semi-seamless migration protocol)
diff --git a/configure.ac b/configure.ac
index 3a86515..e169f36 100644
--- a/configure.ac
+++ b/configure.ac
@@ -126,7 +126,7 @@ fi
dnl =========================================================================
dnl Check deps
-PKG_CHECK_MODULES(PROTOCOL, spice-protocol >= 0.8.1)
+PKG_CHECK_MODULES(PROTOCOL, spice-protocol >= 0.8.2)
AC_SUBST(PROTOCOL_CFLAGS)
AC_CHECK_LIBM
commit 3ac0075cdac8fa42de47a7882022795e96cb1fee
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Sun Sep 18 09:10:24 2011 +0300
server: handle migration interface addition
diff --git a/server/reds.c b/server/reds.c
index 9a983f8..99d52f9 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -73,6 +73,7 @@ static SpiceKbdInstance *keyboard = NULL;
static SpiceMouseInstance *mouse = NULL;
static SpiceTabletInstance *tablet = NULL;
static SpiceCharDeviceInstance *vdagent = NULL;
+static SpiceMigrateInstance *migration_interface = NULL;
#define MIGRATION_NOTIFY_SPICE_KEY "spice_mig_ext"
@@ -4345,6 +4346,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;
@@ -4859,7 +4874,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;
}
@@ -4920,8 +4943,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 463c94f..b60681a 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -119,6 +119,10 @@ struct SpiceNetWireState {
struct TunnelWorker *worker;
};
+struct SpiceMigrateState {
+ int dummy;
+};
+
void reds_channel_dispose(Channel *channel);
ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
commit 6e56bea67c5648b0c81990171d4bc0cf1a402043
Author: Yonit Halperin <yhalperi at redhat.com>
Date: Wed Sep 14 14:21:49 2011 +0300
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.
diff --git a/server/reds.c b/server/reds.c
index f082c53..9a983f8 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -4854,6 +4854,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 ac5a41e..42ddbc6 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -469,11 +469,36 @@ 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);
int spice_server_migrate_switch(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
More information about the Spice-commits
mailing list