From bd9c2383c58fc137c44e323686f6cd986f363d2d Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 14 Mar 2003 07:59:58 +0000 Subject: [PATCH] Bugfix: source shutdown did things in the wrong order, could lead to clients ending up connecting to a source which didn't exist. Add 'moveclients' admin command to move all clients from one source to another (without shutting the old source down) svn path=/trunk/icecast/; revision=4489 --- src/admin.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/source.c | 30 ++++++++++++++++++++---------- src/source.h | 1 + 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/admin.c b/src/admin.c index c197e3e7..4b17f0b8 100644 --- a/src/admin.c +++ b/src/admin.c @@ -25,6 +25,7 @@ #define COMMAND_FALLBACK 1 #define COMMAND_METADATA_UPDATE 2 #define COMMAND_SHOW_LISTENERS 3 +#define COMMAND_MOVE_CLIENTS 4 /* Global commands */ #define COMMAND_LIST_MOUNTS 101 @@ -47,6 +48,8 @@ int admin_get_command(char *command) return COMMAND_LIST_MOUNTS; else if(!strcmp(command, "streamlist")) return COMMAND_RAW_LISTSTREAM; + else if(!strcmp(command, "moveclients")) + return COMMAND_MOVE_CLIENTS; else return COMMAND_ERROR; } @@ -54,6 +57,7 @@ int admin_get_command(char *command) static void command_fallback(client_t *client, source_t *source); static void command_metadata(client_t *client, source_t *source); static void command_show_listeners(client_t *client, source_t *source); +static void command_move_clients(client_t *client, source_t *source); static void command_raw_stats(client_t *client); static void command_list_mounts(client_t *client, int formatted); @@ -160,6 +164,9 @@ static void admin_handle_mount_request(client_t *client, source_t *source, case COMMAND_SHOW_LISTENERS: command_show_listeners(client, source); break; + case COMMAND_MOVE_CLIENTS: + command_move_clients(client, source); + break; default: WARN0("Mount request not recognised"); client_send_400(client, "Mount request unknown"); @@ -214,6 +221,43 @@ static void html_write(client_t *client, char *fmt, ...) if(bytes > 0) client->con->sent_bytes = bytes; } +static void command_move_clients(client_t *client, source_t *source) +{ + char *dest_source; + source_t *dest; + avl_node *client_node; + client_t *current; + + COMMAND_REQUIRE(client, "destination", dest_source); + + avl_tree_rlock(global.source_tree); + dest = source_find_mount(dest_source); + avl_tree_unlock(global.source_tree); + + if(dest == NULL) { + client_send_400(client, "No such source"); + return; + } + + avl_tree_wlock(source->client_tree); + client_node = avl_get_first(source->client_tree); + while(client_node) { + current = (client_t *)client_node->key; + + avl_tree_wlock(dest->pending_tree); + avl_insert(dest->pending_tree, current); + avl_tree_unlock(dest->pending_tree); + + client_node = avl_get_next(client_node); + + avl_delete(source->client_tree, current, source_remove_client); + } + + avl_tree_unlock(source->client_tree); + + html_success(client, "Clients moved"); +} + static void command_show_listeners(client_t *client, source_t *source) { avl_node *client_node; diff --git a/src/source.c b/src/source.c index bb76812c..b3dab158 100644 --- a/src/source.c +++ b/src/source.c @@ -49,7 +49,6 @@ /* avl tree helper */ static int _compare_clients(void *compare_arg, void *a, void *b); -static int _remove_client(void *key); static int _free_client(void *key); static int _parse_audio_info(source_t *source, char *s); static void _add_yp_info(source_t *source, char *stat_name, @@ -96,6 +95,11 @@ source_t *source_create(client_t *client, connection_t *con, return src; } +static int source_remove_source(void *key) +{ + return 1; +} + /* you must already have a read lock on the global source tree ** to call this function */ @@ -136,7 +140,7 @@ int source_compare_sources(void *arg, void *a, void *b) int source_free_source(void *key) { - source_t *source = (source_t *)key; + source_t *source = key; int i=0; free(source->mount); @@ -552,7 +556,7 @@ void *source_main(void *arg) /** clear pending tree **/ while (avl_get_first(source->pending_tree)) { - avl_delete(source->pending_tree, avl_get_first(source->pending_tree)->key, _remove_client); + avl_delete(source->pending_tree, avl_get_first(source->pending_tree)->key, source_remove_client); } /* release write lock on pending_tree */ @@ -573,13 +577,21 @@ done: fallback_source = source_find_mount(source->fallback_mount); avl_tree_unlock(global.source_tree); + /* Now, we must remove this source from the source tree before + * removing the clients, otherwise new clients can sneak into the pending + * tree after we've cleared it + */ + avl_tree_wlock(global.source_tree); + avl_delete(global.source_tree, source, source_remove_source); + avl_tree_unlock(global.source_tree); + /* we need to empty the client and pending trees */ avl_tree_wlock(source->pending_tree); while (avl_get_first(source->pending_tree)) { client_t *client = (client_t *)avl_get_first( source->pending_tree)->key; if(fallback_source) { - avl_delete(source->pending_tree, client, _remove_client); + avl_delete(source->pending_tree, client, source_remove_client); /* TODO: reset client local format data? */ avl_tree_wlock(fallback_source->pending_tree); @@ -597,7 +609,7 @@ done: client_t *client = (client_t *)avl_get_first(source->client_tree)->key; if(fallback_source) { - avl_delete(source->client_tree, client, _remove_client); + avl_delete(source->client_tree, client, source_remove_client); /* TODO: reset client local format data? */ avl_tree_wlock(fallback_source->pending_tree); @@ -621,13 +633,11 @@ done: if(source->dumpfile) fclose(source->dumpfile); + source_free_source(source); + /* release our hold on the lock so the main thread can continue cleaning up */ thread_rwlock_unlock(source->shutdown_rwlock); - avl_tree_wlock(global.source_tree); - avl_delete(global.source_tree, source, source_free_source); - avl_tree_unlock(global.source_tree); - thread_exit(0); return NULL; @@ -644,7 +654,7 @@ static int _compare_clients(void *compare_arg, void *a, void *b) return 0; } -static int _remove_client(void *key) +int source_remove_client(void *key) { return 1; } diff --git a/src/source.h b/src/source.h index 842005b2..338665a0 100644 --- a/src/source.h +++ b/src/source.h @@ -47,6 +47,7 @@ source_t *source_create(client_t *client, connection_t *con, source_t *source_find_mount(const char *mount); int source_compare_sources(void *arg, void *a, void *b); int source_free_source(void *key); +int source_remove_client(void *key); void *source_main(void *arg); #endif