mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-06-30 22:18:19 -04:00
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
This commit is contained in:
parent
5262073213
commit
bd9c2383c5
44
src/admin.c
44
src/admin.c
@ -25,6 +25,7 @@
|
|||||||
#define COMMAND_FALLBACK 1
|
#define COMMAND_FALLBACK 1
|
||||||
#define COMMAND_METADATA_UPDATE 2
|
#define COMMAND_METADATA_UPDATE 2
|
||||||
#define COMMAND_SHOW_LISTENERS 3
|
#define COMMAND_SHOW_LISTENERS 3
|
||||||
|
#define COMMAND_MOVE_CLIENTS 4
|
||||||
|
|
||||||
/* Global commands */
|
/* Global commands */
|
||||||
#define COMMAND_LIST_MOUNTS 101
|
#define COMMAND_LIST_MOUNTS 101
|
||||||
@ -47,6 +48,8 @@ int admin_get_command(char *command)
|
|||||||
return COMMAND_LIST_MOUNTS;
|
return COMMAND_LIST_MOUNTS;
|
||||||
else if(!strcmp(command, "streamlist"))
|
else if(!strcmp(command, "streamlist"))
|
||||||
return COMMAND_RAW_LISTSTREAM;
|
return COMMAND_RAW_LISTSTREAM;
|
||||||
|
else if(!strcmp(command, "moveclients"))
|
||||||
|
return COMMAND_MOVE_CLIENTS;
|
||||||
else
|
else
|
||||||
return COMMAND_ERROR;
|
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_fallback(client_t *client, source_t *source);
|
||||||
static void command_metadata(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_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_raw_stats(client_t *client);
|
||||||
static void command_list_mounts(client_t *client, int formatted);
|
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:
|
case COMMAND_SHOW_LISTENERS:
|
||||||
command_show_listeners(client, source);
|
command_show_listeners(client, source);
|
||||||
break;
|
break;
|
||||||
|
case COMMAND_MOVE_CLIENTS:
|
||||||
|
command_move_clients(client, source);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
WARN0("Mount request not recognised");
|
WARN0("Mount request not recognised");
|
||||||
client_send_400(client, "Mount request unknown");
|
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;
|
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)
|
static void command_show_listeners(client_t *client, source_t *source)
|
||||||
{
|
{
|
||||||
avl_node *client_node;
|
avl_node *client_node;
|
||||||
|
30
src/source.c
30
src/source.c
@ -49,7 +49,6 @@
|
|||||||
|
|
||||||
/* avl tree helper */
|
/* avl tree helper */
|
||||||
static int _compare_clients(void *compare_arg, void *a, void *b);
|
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 _free_client(void *key);
|
||||||
static int _parse_audio_info(source_t *source, char *s);
|
static int _parse_audio_info(source_t *source, char *s);
|
||||||
static void _add_yp_info(source_t *source, char *stat_name,
|
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;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int source_remove_source(void *key)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* you must already have a read lock on the global source tree
|
/* you must already have a read lock on the global source tree
|
||||||
** to call this function
|
** to call this function
|
||||||
*/
|
*/
|
||||||
@ -136,7 +140,7 @@ int source_compare_sources(void *arg, void *a, void *b)
|
|||||||
|
|
||||||
int source_free_source(void *key)
|
int source_free_source(void *key)
|
||||||
{
|
{
|
||||||
source_t *source = (source_t *)key;
|
source_t *source = key;
|
||||||
int i=0;
|
int i=0;
|
||||||
|
|
||||||
free(source->mount);
|
free(source->mount);
|
||||||
@ -552,7 +556,7 @@ void *source_main(void *arg)
|
|||||||
|
|
||||||
/** clear pending tree **/
|
/** clear pending tree **/
|
||||||
while (avl_get_first(source->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 */
|
/* release write lock on pending_tree */
|
||||||
@ -573,13 +577,21 @@ done:
|
|||||||
fallback_source = source_find_mount(source->fallback_mount);
|
fallback_source = source_find_mount(source->fallback_mount);
|
||||||
avl_tree_unlock(global.source_tree);
|
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 */
|
/* we need to empty the client and pending trees */
|
||||||
avl_tree_wlock(source->pending_tree);
|
avl_tree_wlock(source->pending_tree);
|
||||||
while (avl_get_first(source->pending_tree)) {
|
while (avl_get_first(source->pending_tree)) {
|
||||||
client_t *client = (client_t *)avl_get_first(
|
client_t *client = (client_t *)avl_get_first(
|
||||||
source->pending_tree)->key;
|
source->pending_tree)->key;
|
||||||
if(fallback_source) {
|
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? */
|
/* TODO: reset client local format data? */
|
||||||
avl_tree_wlock(fallback_source->pending_tree);
|
avl_tree_wlock(fallback_source->pending_tree);
|
||||||
@ -597,7 +609,7 @@ done:
|
|||||||
client_t *client = (client_t *)avl_get_first(source->client_tree)->key;
|
client_t *client = (client_t *)avl_get_first(source->client_tree)->key;
|
||||||
|
|
||||||
if(fallback_source) {
|
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? */
|
/* TODO: reset client local format data? */
|
||||||
avl_tree_wlock(fallback_source->pending_tree);
|
avl_tree_wlock(fallback_source->pending_tree);
|
||||||
@ -621,13 +633,11 @@ done:
|
|||||||
if(source->dumpfile)
|
if(source->dumpfile)
|
||||||
fclose(source->dumpfile);
|
fclose(source->dumpfile);
|
||||||
|
|
||||||
|
source_free_source(source);
|
||||||
|
|
||||||
/* release our hold on the lock so the main thread can continue cleaning up */
|
/* release our hold on the lock so the main thread can continue cleaning up */
|
||||||
thread_rwlock_unlock(source->shutdown_rwlock);
|
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);
|
thread_exit(0);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -644,7 +654,7 @@ static int _compare_clients(void *compare_arg, void *a, void *b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _remove_client(void *key)
|
int source_remove_client(void *key)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ source_t *source_create(client_t *client, connection_t *con,
|
|||||||
source_t *source_find_mount(const char *mount);
|
source_t *source_find_mount(const char *mount);
|
||||||
int source_compare_sources(void *arg, void *a, void *b);
|
int source_compare_sources(void *arg, void *a, void *b);
|
||||||
int source_free_source(void *key);
|
int source_free_source(void *key);
|
||||||
|
int source_remove_client(void *key);
|
||||||
void *source_main(void *arg);
|
void *source_main(void *arg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user