[Spice-devel] [PATCH migration 05/19] server, proto: tell the clients to connect to the migration target before migraton starts

Yonit Halperin yhalperi at redhat.com
Wed Oct 12 03:38:55 PDT 2011


(1) send SPICE_MSG_MAIN_MIGRATE_BEGIN upon spice_server_migrate_connect
    (to all the clients that support it)
(2) wait for SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR) from all the relevant clients,
    or a timeout, in order to complete client_migrate_info monitor command
(cherry picked from commit 5560c56ef05c74da5e0e0825dc1f134019593cad branch 0.8;
 Was modified to support the separation of main channel from reds, and multiple clients)

Conflicts:

	server/reds.c

Signed-off-by: Yonit Halperin <yhalperi at redhat.com>
---
 common/messages.h     |    2 +
 server/main_channel.c |  164 ++++++++++++++++++++++++++++---------------------
 server/main_channel.h |   12 +++-
 server/red_channel.h  |    2 +-
 server/reds.c         |  129 +++++++++++++++++---------------------
 server/reds.h         |   14 +++-
 spice.proto           |    5 +-
 7 files changed, 176 insertions(+), 152 deletions(-)

diff --git a/common/messages.h b/common/messages.h
index 8151dc0..a54190f 100644
--- a/common/messages.h
+++ b/common/messages.h
@@ -66,6 +66,8 @@ typedef struct SpiceMsgMainMigrationBegin {
     uint16_t pub_key_type;
     uint32_t pub_key_size;
     uint8_t *pub_key_data;
+    uint32_t cert_subject_size;
+    uint8_t *cert_subject_data;
 } SpiceMsgMainMigrationBegin;
 
 typedef struct SpiceMsgMainMigrationSwitchHost {
diff --git a/server/main_channel.c b/server/main_channel.c
index 49028b3..5b16b30 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -110,16 +110,6 @@ typedef struct NotifyPipeItem {
     int mess_len;
 } NotifyPipeItem;
 
-typedef struct MigrateBeginPipeItem {
-    PipeItem base;
-    int port;
-    int sport;
-    char *host;
-    uint16_t cert_pub_key_type;
-    uint32_t cert_pub_key_len;
-    uint8_t *cert_pub_key;
-} MigrateBeginPipeItem;
-
 typedef struct MultiMediaTimePipeItem {
     PipeItem base;
     int time;
@@ -137,6 +127,8 @@ struct MainChannelClient {
     SpiceTimer *ping_timer;
     int ping_interval;
 #endif
+    int mig_wait_connect;
+    int mig_connect_ok;
 };
 
 enum NetTestStage {
@@ -283,33 +275,6 @@ static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num
     return &item->base;
 }
 
-typedef struct MigrateBeginItemInfo {
-    int port;
-    int sport;
-    char *host;
-    uint16_t cert_pub_key_type;
-    uint32_t cert_pub_key_len;
-    uint8_t *cert_pub_key;
-} MigrateBeginItemInfo;
-
-// TODO: MC: migration is not tested at all with multiclient.
-static PipeItem *main_migrate_begin_item_new(
-    RedChannelClient *rcc, void *data, int num)
-{
-    MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem));
-    MigrateBeginItemInfo *info = data;
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               SPICE_MSG_MAIN_MIGRATE_BEGIN);
-    item->port = info->port;
-    item->sport = info->sport;
-    item->host = info->host;
-    item->cert_pub_key_type = info->cert_pub_key_type;
-    item->cert_pub_key_len = info->cert_pub_key_len;
-    item->cert_pub_key = info->cert_pub_key;
-    return &item->base;
-}
-
 static PipeItem *main_multi_media_time_item_new(
     RedChannelClient *rcc, void *data, int num)
 {
@@ -550,35 +515,23 @@ static void main_channel_marshall_notify(SpiceMarshaller *m, NotifyPipeItem *ite
     spice_marshaller_add(m, item->mess, item->mess_len + 1);
 }
 
-void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport,
-    char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
-    uint8_t *cert_pub_key)
-{
-    MigrateBeginItemInfo info = {
-        .port =port,
-        .sport = sport,
-        .host = host,
-        .cert_pub_key_type = cert_pub_key_type,
-        .cert_pub_key_len = cert_pub_key_len,
-        .cert_pub_key = cert_pub_key,
-    };
-
-    red_channel_pipes_new_add_push(&main_chan->base,
-        main_migrate_begin_item_new, &info);
-}
-
-static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
-    MigrateBeginPipeItem *item)
+static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc)
 {
     SpiceMsgMainMigrationBegin migrate;
-
-    migrate.port = item->port;
-    migrate.sport = item->sport;
-    migrate.host_size = strlen(item->host) + 1;
-    migrate.host_data = (uint8_t *)item->host;
-    migrate.pub_key_type = item->cert_pub_key_type;
-    migrate.pub_key_size = item->cert_pub_key_len;
-    migrate.pub_key_data = item->cert_pub_key;
+    MainChannel *main_ch;
+
+    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    migrate.port = main_ch->mig_target.port;
+    migrate.sport = main_ch->mig_target.sport;
+    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
+    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
+    if (main_ch->mig_target.cert_subject) {
+        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
+        migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
+    } else {
+        migrate.cert_subject_size = 0;
+        migrate.cert_subject_data = NULL;
+    }
     spice_marshall_msg_main_migrate_begin(m, &migrate);
 }
 
@@ -699,8 +652,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
             main_channel_marshall_migrate(m);
             break;
         case SPICE_MSG_MAIN_MIGRATE_BEGIN:
-            main_channel_marshall_migrate_begin(m,
-                SPICE_CONTAINEROF(base, MigrateBeginPipeItem, base));
+            main_channel_marshall_migrate_begin(m, rcc);
             break;
         case SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
             main_channel_marshall_multi_media_time(m,
@@ -741,6 +693,26 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
     free(base);
 }
 
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int success)
+{
+    red_printf("client %p connected: %d", mcc->base.client, success);
+    if (mcc->mig_wait_connect) {
+        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
+
+        mcc->mig_wait_connect = FALSE;
+        mcc->mig_connect_ok = success;
+        ASSERT(main_channel->num_clients_mig_wait);
+        if (!--main_channel->num_clients_mig_wait) {
+            reds_on_main_migrate_connected();
+        }
+    } else {
+        if (success) {
+            red_printf("client %p MIGRATE_CANCEL", mcc->base.client);
+            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+        }
+    }
+}
+
 static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
 {
     MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
@@ -764,12 +736,10 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
         main_channel_push_channels(mcc);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
-        red_printf("connected");
-        reds_on_main_migrate_connected();
+        main_channel_client_handle_migrate_connected(mcc, TRUE);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
-        red_printf("mig connect error");
-        reds_on_main_migrate_connect_error();
+        main_channel_client_handle_migrate_connected(mcc, FALSE);
         break;
     case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
         reds_on_main_mouse_mode_request(message, size);
@@ -1001,6 +971,58 @@ MainChannel* main_channel_init(void)
                                         main_channel_handle_parsed,
                                         &channel_cbs);
     ASSERT(channel);
-
+    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
     return (MainChannel *)channel;
 }
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
+{
+    ASSERT(mcc);
+    return &mcc->base;
+}
+
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target)
+{
+    RingItem *client_link;
+
+    ASSERT(mig_target);
+    free(main_channel->mig_target.host);
+    main_channel->mig_target.host = strdup(mig_target->host);
+    free(main_channel->mig_target.cert_subject);
+    if (mig_target->cert_subject) {
+        main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
+    }
+    main_channel->mig_target.port = mig_target->port;
+    main_channel->mig_target.sport = mig_target->sport;
+
+    main_channel->num_clients_mig_wait = 0;
+
+    RING_FOREACH(client_link, &main_channel->base.clients) {
+        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
+        if (red_channel_client_test_remote_cap(&mcc->base,
+                                               SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+            mcc->mig_wait_connect = TRUE;
+            mcc->mig_connect_ok = FALSE;
+            main_channel->num_clients_mig_wait++;
+        }
+    }
+    return main_channel->num_clients_mig_wait;
+}
+
+void main_channel_migrate_cancel_wait(MainChannel *main_chan)
+{
+    RingItem *client_link;
+
+    RING_FOREACH(client_link, &main_chan->base.clients) {
+        MainChannelClient *mcc;
+
+        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
+        if (mcc->mig_wait_connect) {
+            red_printf("client %p cancel wait connect", mcc->base.client);
+            mcc->mig_wait_connect = FALSE;
+            mcc->mig_connect_ok = FALSE;
+        }
+    }
+    main_chan->num_clients_mig_wait = 0;
+}
diff --git a/server/main_channel.h b/server/main_channel.h
index 2ae05e8..f3702e7 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -57,6 +57,8 @@ struct MainMigrateData {
 typedef struct MainChannel {
     RedChannel base;
     uint8_t recv_buf[RECEIVE_BUF_SIZE];
+    RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?)
+    int num_clients_mig_wait;
 } MainChannel;
 
 
@@ -80,9 +82,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ
     int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
     int ram_hint);
 void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
-// TODO: consider exporting RedsMigSpice from reds.c
-void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport, char *host,
-    uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, uint8_t *cert_pub_key);
 void main_channel_push_migrate(MainChannel *main_chan);
 void main_channel_push_migrate_switch(MainChannel *main_chan);
 void main_channel_push_migrate_cancel(MainChannel *main_chan);
@@ -94,5 +93,12 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc);
 int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
 uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
 int main_channel_is_connected(MainChannel *main_chan);
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
 
+/* semi seamless migration */
+
+/* returns the number of clients that we are waiting for their connection */
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target);
+void main_channel_migrate_cancel_wait(MainChannel *main_chan);
+void main_channel_migrate_complete(MainChannel *main_chan, int success);
 #endif
diff --git a/server/red_channel.h b/server/red_channel.h
index d044253..e30401c 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -450,6 +450,7 @@ struct RedClient {
     pthread_t thread_id;
 
     int disconnecting;
+
 };
 
 RedClient *red_client_new(void);
@@ -457,7 +458,6 @@ MainChannelClient *red_client_get_main(RedClient *client);
 // main should be set once before all the other channels are created
 void red_client_set_main(RedClient *client, MainChannelClient *mcc);
 
-
 void red_client_migrate(RedClient *client);
 // disconnects all the client's channels (should be called from the client's thread)
 void red_client_destroy(RedClient *client);
diff --git a/server/reds.c b/server/reds.c
index 40b54bb..009ad21 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -190,8 +190,6 @@ typedef struct RedsStatValue {
 
 #endif
 
-typedef struct RedsMigSpice RedsMigSpice;
-
 typedef struct RedsState {
     int listen_socket;
     int secure_listen_socket;
@@ -294,6 +292,7 @@ struct ChannelSecurityOptions {
     ChannelSecurityOptions *next;
 };
 
+static void migrate_timeout(void *opaque);
 
 static ChannelSecurityOptions *channels_security = NULL;
 static int default_channel_security =
@@ -537,6 +536,12 @@ static RedChannel *reds_find_channel(uint32_t type, uint32_t id)
 static void reds_mig_cleanup(void)
 {
     if (reds->mig_inprogress) {
+        if (reds->mig_wait_connect) {
+            SpiceMigrateInterface *sif;
+            ASSERT(migration_interface);
+            sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+            sif->migrate_connect_complete(migration_interface);
+        }
         reds->mig_inprogress = FALSE;
         reds->mig_wait_connect = FALSE;
         reds->mig_wait_disconnect = FALSE;
@@ -1059,13 +1064,6 @@ void reds_on_main_migrate_connected(void)
     }
 }
 
-void reds_on_main_migrate_connect_error(void)
-{
-    if (reds->mig_wait_connect) {
-        reds_mig_cleanup();
-    }
-}
-
 void reds_on_main_mouse_mode_request(void *message, size_t size)
 {
     switch (((SpiceMsgcMainMouseModeRequest *)message)->mode) {
@@ -2975,18 +2973,6 @@ static void set_one_channel_security(int id, uint32_t security)
 
 #define REDS_SAVE_VERSION 1
 
-struct RedsMigSpice {
-    char pub_key[SPICE_TICKET_PUBKEY_BYTES];
-    uint32_t mig_key;
-    char *host;
-    char *cert_subject;
-    int port;
-    int sport;
-    uint16_t cert_pub_key_type;
-    uint32_t cert_pub_key_len;
-    uint8_t* cert_pub_key;
-};
-
 typedef struct RedsMigSpiceMessage {
     uint32_t connection_id;
 } RedsMigSpiceMessage;
@@ -3006,24 +2992,12 @@ void reds_mig_release(void)
     }
 }
 
-static void reds_mig_continue(void)
-{
-    RedsMigSpice *s = reds->mig_spice;
-
-    red_printf("");
-    main_channel_push_migrate_begin(reds->main_channel, s->port, s->sport,
-        s->host, s->cert_pub_key_type, s->cert_pub_key_len, s->cert_pub_key);
-
-    reds_mig_release();
-
-    reds->mig_wait_connect = TRUE;
-    core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
-}
-
 static void reds_mig_started(void)
 {
     red_printf("");
+    ASSERT(reds->mig_spice);
 
+    reds->mig_wait_connect = TRUE;
     reds->mig_inprogress = TRUE;
 
     if (reds->listen_watch != NULL) {
@@ -3033,24 +3007,7 @@ static void reds_mig_started(void)
     if (reds->secure_listen_watch != NULL) {
         core->watch_update_mask(reds->secure_listen_watch, 0);
     }
-
-    if (!reds_main_channel_connected()) {
-        red_printf("not connected to peer");
-        goto error;
-    }
-
-    if ((SPICE_VERSION_MAJOR == 1) && (reds->peer_minor_version < 2)) {
-        red_printf("minor version mismatch client %u server %u",
-                   reds->peer_minor_version, SPICE_VERSION_MINOR);
-        goto error;
-    }
-
-    reds_mig_continue();
-    return;
-
-error:
-    reds_mig_release();
-    reds_mig_disconnect();
+    core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
 }
 
 static void reds_mig_finished(int completed)
@@ -3126,11 +3083,16 @@ void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate)
     }
 }
 
-static void migrate_timout(void *opaque)
+static void migrate_timeout(void *opaque)
 {
     red_printf("");
     ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
-    reds_mig_disconnect();
+    if (reds->mig_wait_connect) {
+        main_channel_migrate_cancel_wait(reds->main_channel);
+        reds_mig_cleanup();
+    } else {
+        reds_mig_disconnect();
+    }
 }
 
 uint32_t reds_get_mm_time(void)
@@ -3490,7 +3452,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     reds->num_clients = 0;
     ring_init(&reds->channels);
 
-    if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) {
+    if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) {
         red_error("migration timer create failed");
     }
     if (!(reds->vdi_port_write_timer = core->timer_add(vdi_port_write_retry, NULL)))
@@ -3873,19 +3835,55 @@ SPICE_GNUC_VISIBLE int spice_server_set_agent_copypaste(SpiceServer *s, int enab
     return 0;
 }
 
+/* returns FALSE if info is invalid */
+static int reds_set_migration_dest_info(const char* dest,
+                                        int port, int secure_port,
+                                        const char* cert_subject)
+{
+    RedsMigSpice *spice_migration = NULL;
+
+    reds_mig_release();
+    if ((port == -1 && secure_port == -1) || !dest) {
+        return FALSE;
+    }
+
+    spice_migration = spice_new0(RedsMigSpice, 1);
+    spice_migration->port = port;
+    spice_migration->sport = secure_port;
+    spice_migration->host = strdup(dest);
+    if (cert_subject) {
+        spice_migration->cert_subject = strdup(cert_subject);
+    }
+
+    reds->mig_spice = spice_migration;
+
+    return TRUE;
+}
+
 /* semi-seamless client migration */
 SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* dest,
                                                     int port, int secure_port,
                                                     const char* cert_subject)
 {
     SpiceMigrateInterface *sif;
+
     red_printf("");
     ASSERT(migration_interface);
     ASSERT(reds == s);
 
-    red_printf("not implemented yet");
     sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
-    sif->migrate_connect_complete(migration_interface);
+
+    if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
+        sif->migrate_connect_complete(migration_interface);
+        return -1;
+    }
+
+    if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
+        reds->mig_wait_connect = TRUE;
+        reds_mig_started();
+    } else {
+        sif->migrate_connect_complete(migration_interface);
+    }
 
     return 0;
 }
@@ -3894,27 +3892,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
                                           int port, int secure_port,
                                           const char* cert_subject)
 {
-    RedsMigSpice *spice_migration = NULL;
-
+    ASSERT(!migration_interface);
     ASSERT(reds == s);
 
-    if ((port == -1 && secure_port == -1) || !dest)
+    if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
         return -1;
-
-    spice_migration = spice_new0(RedsMigSpice, 1);
-    spice_migration->port = port;
-    spice_migration->sport = secure_port;
-    spice_migration->host = strdup(dest);
-    if (cert_subject) {
-        spice_migration->cert_subject = strdup(cert_subject);
     }
 
-    reds_mig_release();
-    reds->mig_spice = spice_migration;
     return 0;
 }
 
-/* interface for seamless migration */
 SPICE_GNUC_VISIBLE int spice_server_migrate_start(SpiceServer *s)
 {
     ASSERT(reds == s);
diff --git a/server/reds.h b/server/reds.h
index 7720b30..13fec42 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -99,6 +99,13 @@ struct SpiceMigrateState {
     int dummy;
 };
 
+typedef struct RedsMigSpice {
+    char *host;
+    char *cert_subject;
+    int port;
+    int sport;
+} RedsMigSpice;
+
 ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
 ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
 ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
@@ -134,11 +141,12 @@ int reds_num_of_clients(void);
 void reds_update_stat_value(uint32_t value);
 #endif
 
-// callbacks from main channel messages
+/* callbacks from main channel messages */
+
 void reds_on_main_agent_start(void);
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
-void reds_on_main_migrate_connected(void);
-void reds_on_main_migrate_connect_error(void);
+void reds_on_main_migrate_connected(void); //should be called when all the clients
+                                           // are connected to the target
 void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
 void reds_on_main_mouse_mode_request(void *message, size_t size);
 
diff --git a/spice.proto b/spice.proto
index abf3ec3..78c1fad 100644
--- a/spice.proto
+++ b/spice.proto
@@ -167,9 +167,8 @@ channel MainChannel : BaseChannel {
 	uint16 sport;
 	uint32 host_size;
 	uint8 *host_data[host_size] @zero_terminated @marshall @nonnull;
-	pubkey_type pub_key_type;
-	uint32 pub_key_size;
-	uint8 *pub_key_data[pub_key_size] @zero_terminated  @marshall @nonnull;
+	uint32 cert_subject_size;
+	uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall;
     } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
 
     Empty migrate_cancel;
-- 
1.7.6.4



More information about the Spice-devel mailing list