[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, &notify);
+    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, &notify);
-        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 = &caps;
+        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