From e62521ca51dae8c6d943c345e9ff2124727eacaa Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Sun, 18 Sep 2011 13:09:52 +0300 Subject: server: handle spice_server_migrate_end If the migration has completed successfully: (1) send MSG_MAIN_MIGRATE_END to the clients that are connected to the target (2) send MSG_MAIN_SWITCH_HOST to all the other clients If the migration failed, send MSG_MAIN_MIGRATE_CANCEL to clients that are connected to the target. (cherry picked from commit 4b82580fc36228af13db4ac3c403753d6b5c40b5 branch 0.8; Was modified to support multiple clients, and the separation of main_channel from reds) Conflicts: server/reds.c --- server/main_channel.c | 111 +++++++++++++++++++++++++++++++------------------- server/main_channel.h | 8 ++-- server/reds.c | 104 +++++++++++++++++++--------------------------- server/reds.h | 2 - 4 files changed, 117 insertions(+), 108 deletions(-) diff --git a/server/main_channel.c b/server/main_channel.c index 5b16b307..ffc593d2 100644 --- a/server/main_channel.c +++ b/server/main_channel.c @@ -548,11 +548,6 @@ static void main_channel_marshall_migrate(SpiceMarshaller *m) spice_marshall_msg_migrate(m, &migrate); } -void main_channel_push_migrate_cancel(MainChannel *main_chan) -{ - red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); -} - void main_channel_push_multi_media_time(MainChannel *main_chan, int time) { MultiMediaTimePipeItem info = { @@ -563,33 +558,43 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time) main_multi_media_time_item_new, &info); } -static PipeItem *main_migrate_switch_item_new(RedChannelClient *rcc, - void *data, int num) +static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target) { - RefsPipeItem *item = spice_malloc(sizeof(*item)); - - item->refs = data; - red_channel_pipe_item_init(rcc->channel, &item->base, - SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST); - return &item->base; + ASSERT(mig_target); + free(main_channel->mig_target.host); + main_channel->mig_target.host = strdup(mig_target->host); + free(main_channel->mig_target.cert_subject); + if (mig_target->cert_subject) { + main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject); + } + main_channel->mig_target.port = mig_target->port; + main_channel->mig_target.sport = mig_target->sport; } -void main_channel_push_migrate_switch(MainChannel *main_chan) +void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target) { - int *refs = spice_malloc0(sizeof(int)); - - *refs = main_chan->base.clients_num; - red_channel_pipes_new_add_push(&main_chan->base, - main_migrate_switch_item_new, (void*)refs); + main_channel_fill_mig_target(main_chan, mig_target); + red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST); } -static void main_channel_marshall_migrate_switch(SpiceMarshaller *m) +static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc) { SpiceMsgMainMigrationSwitchHost migrate; + MainChannel *main_ch; red_printf(""); - - reds_fill_mig_switch(&migrate); + main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); + migrate.port = main_ch->mig_target.port; + migrate.sport = main_ch->mig_target.sport; + migrate.host_size = strlen(main_ch->mig_target.host) + 1; + migrate.host_data = (uint8_t *)main_ch->mig_target.host; + if (main_ch->mig_target.cert_subject) { + migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1; + migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject; + } else { + migrate.cert_subject_size = 0; + migrate.cert_subject_data = NULL; + } spice_marshall_msg_main_migrate_switch_host(m, &migrate); } @@ -659,7 +664,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base) SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base)); break; case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST: - main_channel_marshall_migrate_switch(m); + main_channel_marshall_migrate_switch(m, rcc); break; }; red_channel_client_begin_send_message(rcc); @@ -679,14 +684,6 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc, } break; } - case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST: { - RefsPipeItem *data = (RefsPipeItem*)base; - if (!--*(data->refs)) { - free(data->refs); - reds_mig_release(); - } - break; - } default: break; } @@ -985,16 +982,7 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta { RingItem *client_link; - ASSERT(mig_target); - free(main_channel->mig_target.host); - main_channel->mig_target.host = strdup(mig_target->host); - free(main_channel->mig_target.cert_subject); - if (mig_target->cert_subject) { - main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject); - } - main_channel->mig_target.port = mig_target->port; - main_channel->mig_target.sport = mig_target->sport; - + main_channel_fill_mig_target(main_channel, mig_target); main_channel->num_clients_mig_wait = 0; RING_FOREACH(client_link, &main_channel->base.clients) { @@ -1026,3 +1014,44 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan) } main_chan->num_clients_mig_wait = 0; } + +int main_channel_migrate_complete(MainChannel *main_chan, int success) +{ + RingItem *client_link; + int semi_seamless_count = 0; + + red_printf(""); + + if (ring_is_empty(&main_chan->base.clients)) { + red_printf("no peer connected"); + return 0; + } + + RING_FOREACH(client_link, &main_chan->base.clients) { + MainChannelClient *mcc; + int semi_seamless_support; + + mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link); + semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base, + SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); + if (semi_seamless_support && mcc->mig_connect_ok) { + if (success) { + red_printf("client %p MIGRATE_END", mcc->base.client); + red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END); + semi_seamless_count++; + } else { + red_printf("client %p MIGRATE_CANCEL", mcc->base.client); + red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); + } + } else { + if (success) { + red_printf("client %p SWITCH_HOST", mcc->base.client); + red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST); + } + } + mcc->mig_connect_ok = FALSE; + mcc->mig_wait_connect = FALSE; + } + return semi_seamless_count; +} + diff --git a/server/main_channel.h b/server/main_channel.h index f3702e76..d97857db 100644 --- a/server/main_channel.h +++ b/server/main_channel.h @@ -83,8 +83,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ int ram_hint); void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len); void main_channel_push_migrate(MainChannel *main_chan); -void main_channel_push_migrate_switch(MainChannel *main_chan); -void main_channel_push_migrate_cancel(MainChannel *main_chan); void main_channel_push_multi_media_time(MainChannel *main_chan, int time); int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen); int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen); @@ -95,10 +93,14 @@ uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc); int main_channel_is_connected(MainChannel *main_chan); RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc); +/* switch host migration */ +void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target); + /* semi seamless migration */ /* returns the number of clients that we are waiting for their connection */ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target); void main_channel_migrate_cancel_wait(MainChannel *main_chan); -void main_channel_migrate_complete(MainChannel *main_chan, int success); +/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/ +int main_channel_migrate_complete(MainChannel *main_chan, int success); #endif diff --git a/server/reds.c b/server/reds.c index bc93e4ac..8799e00c 100644 --- a/server/reds.c +++ b/server/reds.c @@ -205,6 +205,7 @@ typedef struct RedsState { int mig_wait_connect; int mig_wait_disconnect; int mig_inprogress; + int expect_migrate; int mig_target; RedsMigSpice *mig_spice; int num_of_channels; @@ -2983,7 +2984,7 @@ typedef struct RedsMigCertPubKeyInfo { uint32_t len; } RedsMigCertPubKeyInfo; -void reds_mig_release(void) +static void reds_mig_release(void) { if (reds->mig_spice) { free(reds->mig_spice->cert_subject); @@ -2998,29 +2999,14 @@ static void reds_mig_started(void) red_printf(""); ASSERT(reds->mig_spice); - reds->mig_wait_connect = TRUE; reds->mig_inprogress = TRUE; - - if (reds->listen_watch != NULL) { - core->watch_update_mask(reds->listen_watch, 0); - } - - if (reds->secure_listen_watch != NULL) { - core->watch_update_mask(reds->secure_listen_watch, 0); - } + reds->mig_wait_connect = TRUE; core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT); } static void reds_mig_finished(int completed) { red_printf(""); - if (reds->listen_watch != NULL) { - core->watch_update_mask(reds->listen_watch, SPICE_WATCH_EVENT_READ); - } - - if (reds->secure_listen_watch != NULL) { - core->watch_update_mask(reds->secure_listen_watch, SPICE_WATCH_EVENT_READ); - } if (!reds_main_channel_connected()) { red_printf("no peer connected"); @@ -3028,28 +3014,13 @@ static void reds_mig_finished(int completed) } reds->mig_inprogress = TRUE; - if (completed) { - RingItem *link, *next; - + if (main_channel_migrate_complete(reds->main_channel, completed)) { reds->mig_wait_disconnect = TRUE; core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT); - - // TODO: so now that main channel is separate, how exactly does migration of it work? - // - it can have an empty migrate - that seems ok - // - I can try to fill it's migrate, then move stuff from reds.c there, but a lot of data - // is in reds state right now. - // currently the migrate callback of main_channel does nothing - main_channel_push_migrate(reds->main_channel); - - RING_FOREACH_SAFE(link, next, &reds->clients) { - red_client_migrate(SPICE_CONTAINEROF(link, RedClient, link)); - } } else { - main_channel_push_migrate_cancel(reds->main_channel); - // TODO: all the seemless migration is broken. Before MC we waited for disconection of one client, - // no we need to wait to all the clients (see mig_timer)? reds_mig_cleanup(); } + reds_mig_release(); } static void reds_mig_switch(void) @@ -3058,30 +3029,8 @@ static void reds_mig_switch(void) red_printf("warning: reds_mig_switch called without migrate_info set"); return; } - main_channel_push_migrate_switch(reds->main_channel); -} - -void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate) -{ - RedsMigSpice *s = reds->mig_spice; - - if (s == NULL) { - red_printf( - "error: reds_fill_mig_switch called without migrate info set"); - bzero(migrate, sizeof(*migrate)); - return; - } - migrate->port = s->port; - migrate->sport = s->sport; - migrate->host_size = strlen(s->host) + 1; - migrate->host_data = (uint8_t *)s->host; - if (s->cert_subject) { - migrate->cert_subject_size = strlen(s->cert_subject) + 1; - migrate->cert_subject_data = (uint8_t *)s->cert_subject; - } else { - migrate->cert_subject_size = 0; - migrate->cert_subject_data = NULL; - } + main_channel_migrate_switch(reds->main_channel, reds->mig_spice); + reds_mig_release(); } static void migrate_timeout(void *opaque) @@ -3873,6 +3822,11 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* ASSERT(migration_interface); ASSERT(reds == s); + if (reds->expect_migrate) { + red_printf("warning: consecutive calls without migration. Canceling previous call"); + main_channel_migrate_complete(reds->main_channel, FALSE); + } + sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base); if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) { @@ -3880,10 +3834,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* return -1; } + reds->expect_migrate = TRUE; + + if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) { - reds->mig_wait_connect = TRUE; reds_mig_started(); } else { + if (reds->num_clients == 0) { + reds_mig_release(); + red_printf("no client connected"); + } sif->migrate_connect_complete(migration_interface); } @@ -3894,13 +3854,13 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des int port, int secure_port, const char* cert_subject) { + red_printf(""); ASSERT(!migration_interface); ASSERT(reds == s); if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) { return -1; } - return 0; } @@ -3937,20 +3897,40 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_client_state(SpiceServer *s) SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed) { SpiceMigrateInterface *sif; + int ret = 0; + + red_printf(""); + ASSERT(migration_interface); ASSERT(reds == s); - reds_mig_finished(completed); + sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base); + if (!reds->expect_migrate && reds->num_clients) { + red_printf("spice_server_migrate_info was not called, disconnecting clients"); + reds_disconnect(); + ret = -1; + goto complete; + } + + reds->expect_migrate = FALSE; + reds_mig_finished(completed); + ret = 0; +complete: if (sif->migrate_end_complete) { sif->migrate_end_complete(migration_interface); } - return 0; + return ret; } /* interface for switch-host migration */ SPICE_GNUC_VISIBLE int spice_server_migrate_switch(SpiceServer *s) { ASSERT(reds == s); + red_printf(""); + if (!reds->num_clients) { + return 0; + } + reds->expect_migrate = FALSE; reds_mig_switch(); return 0; } diff --git a/server/reds.h b/server/reds.h index 13fec421..9feb9ab3 100644 --- a/server/reds.h +++ b/server/reds.h @@ -133,8 +133,6 @@ void reds_client_disconnect(RedClient *client); typedef struct MainMigrateData MainMigrateData; void reds_marshall_migrate_data_item(SpiceMarshaller *m, MainMigrateData *data); void reds_fill_channels(SpiceMsgChannels *channels_info); -void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate); -void reds_mig_release(void); int reds_num_of_channels(void); int reds_num_of_clients(void); #ifdef RED_STATISTICS -- cgit