diff --git a/NEWS b/NEWS index 04d007c7..26841d38 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,26 @@ Feature differences from SVN trunk any extra tags are show in the conf/icecast.xml.dist file +2.3-kh33 +. master/slave update. + slave mode only issues a streamlist.txt request if the /admin/slaves request + fails. /admin/slaves also acts as an mountpoint for the slave relays by + passing a mount= arg and auth. + You can now define a for /admin/slaves to define how slave + authentication is done eg url (listener_add) or htpasswd. + Once authenticated, the slave can bypass any limits like max listeners etc. + The slave also allows for defining a tag block instead of + tags allowed are + auth url can take an "icecast-slave: 1" header, which makes client act as a + slave and bypass mount limits. +. build fixups for OpenBSD and cases where IPV6_V6ONLY is missing +. move 'clients' limit check to after admin request check, means admin commands + can work even if the limit is reached. removes a lock taken as well. +. drop connections to known slave relays if we find that the destination source + is a fallback to file, as this would cause high bandwidth usage. +. stats fix for inactive on-demand relay stats without a mount definition. +. added some minor consistency checks. + 2.3-kh32 . crash fix for a failing on-demand relay. . incorrect logfile settings for timestamp setting, for playlist and access.log diff --git a/configure.in b/configure.in index 9ad6e711..69139a32 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -AC_INIT([Icecast], [2.3-kh32], [karl@xiph.org]) +AC_INIT([Icecast], [2.3-kh33], [karl@xiph.org]) AC_PREREQ(2.59) AC_CONFIG_SRCDIR(src/main.c) diff --git a/src/admin.c b/src/admin.c index 104dc8d1..bc74996c 100644 --- a/src/admin.c +++ b/src/admin.c @@ -52,7 +52,6 @@ static void command_move_clients(client_t *client, source_t *source, int response); static void command_stats(client_t *client, const char *filename); static void command_stats_mount (client_t *client, source_t *source, int response); -static void command_list_mounts(client_t *client, int response); static void command_kill_client(client_t *client, source_t *source, int response); static void command_manageauth(client_t *client, source_t *source, @@ -333,6 +332,19 @@ int admin_handle_request (client_t *client, const char *uri) if (connection_check_admin_pass (client->parser)) client->authenticated = 1; + /* special case for slaves requesting a streamlist for authenticated relaying */ + if (strcmp (uri, "streams") == 0) + { + client->is_slave = 1; + auth_add_listener ("/admin/streams", client); + return 0; + } + if (strcmp (uri, "streamlist.txt") == 0) + { + if (connection_check_relay_pass (client->parser)) + client->authenticated = 1; + } + if (mount) { /* no auth/stream required for this */ @@ -362,13 +374,6 @@ int admin_handle_request (client_t *client, const char *uri) return 0; } - /* no auth/stream required for this */ - if (strcmp (uri, "streamlist.txt") == 0) - { - if (connection_check_relay_pass (client->parser)) - client->authenticated = 1; - } - admin_handle_general_request (client, uri); return 0; } @@ -1090,7 +1095,7 @@ static void command_list_log (client_t *client, int response) } -static void command_list_mounts(client_t *client, int response) +void command_list_mounts(client_t *client, int response) { DEBUG0("List mounts request"); @@ -1099,6 +1104,7 @@ static void command_list_mounts(client_t *client, int response) char *buf; int remaining = PER_CLIENT_REFBUF_SIZE; int ret; + redirector_update (client); buf = client->refbuf->data; @@ -1106,7 +1112,10 @@ static void command_list_mounts(client_t *client, int response) "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); client->respcode = 200; - stats_get_streamlist (buf+ret, remaining-ret); + if (strcmp (httpp_getvar (client->parser, HTTPP_VAR_URI), "/admin/streams") == 0) + client->refbuf->next = stats_get_streams (); + else + stats_get_streamlist (buf+ret, remaining-ret); client->refbuf->len = strlen (client->refbuf->data); fserve_add_client (client, NULL); diff --git a/src/admin.h b/src/admin.h index e96f723b..994d2a73 100644 --- a/src/admin.h +++ b/src/admin.h @@ -26,6 +26,7 @@ typedef enum { TEXT } admin_response_type; +void command_list_mounts(client_t *client, int response); int admin_handle_request (client_t *client, const char *uri); void admin_mount_request (client_t *client, const char *uri); void admin_source_listeners (source_t *source, xmlNodePtr node); diff --git a/src/auth.c b/src/auth.c index 9a992c88..2ac76079 100644 --- a/src/auth.c +++ b/src/auth.c @@ -382,7 +382,7 @@ static int check_duplicate_logins (source_t *source, client_t *client, auth_t *a /* Add client to source if it finds one. If a 0 is returned then the client should not be - * touched, if the return value is -1 then the it failed to add and should not be touched. + * touched, if the return value is -1 then it failed to add and should not be touched. * If it's a -2 value then the client is still around for any further processing. */ static int add_listener_to_source (const char *mount, mount_proxy *mountinfo, client_t *client) @@ -424,6 +424,11 @@ static int add_listener_to_source (const char *mount, mount_proxy *mountinfo, cl /* ok, we found a source and it is locked */ if (client->is_slave) { + if (source->client == NULL && source->on_demand == 0) + { + client_send_403 (client, "Slave relay reading from time unregulated stream"); + return -1; + } INFO0 ("client is from a slave, bypassing limits"); break; } @@ -532,6 +537,22 @@ static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo { int ret = 0; + /* check whether we are processing a streamlist request for slaves */ + if (strcmp (mount, "/admin/streams") == 0) + { + if (client->authenticated == 0) + { + client_send_401 (client, NULL); + return 0; + } + mount = httpp_get_query_param (client->parser, "mount"); + if (mount == NULL) + { + command_list_mounts (client, TEXT); + return 0; + } + mountinfo = config_find_mount (config_get_config_unlocked(), mount); + } client->authenticated = 1; /* Here we are parsing the URI request to see if the extension is .xsl, if @@ -630,9 +651,10 @@ void auth_add_listener (const char *mount, client_t *client) /* we don't need any more data from the listener, just setup for writing */ client->refbuf->len = PER_CLIENT_REFBUF_SIZE; - if (connection_check_relay_pass(client->parser)) + if (connection_check_relay_pass (client->parser)) { client->is_slave = 1; + client->authenticated = 1; INFO0 ("client connected as slave"); } config = config_get_config(); @@ -643,7 +665,7 @@ void auth_add_listener (const char *mount, client_t *client) client_send_403 (client, "mountpoint unavailable"); return; } - if (mountinfo && mountinfo->auth && mountinfo->auth->authenticate) + if (client->authenticated == 0 && mountinfo && mountinfo->auth && mountinfo->auth->authenticate) { auth_client *auth_user; diff --git a/src/auth_htpasswd.c b/src/auth_htpasswd.c index 46526053..9bf9f0b4 100644 --- a/src/auth_htpasswd.c +++ b/src/auth_htpasswd.c @@ -26,6 +26,7 @@ #include #include "auth.h" +#include "auth_htpasswd.h" #include "source.h" #include "client.h" #include "cfgfile.h" diff --git a/src/auth_url.c b/src/auth_url.c index 4231754d..59ce5473 100644 --- a/src/auth_url.c +++ b/src/auth_url.c @@ -30,6 +30,12 @@ * * icecast-auth-timelimit: 900 * + * A listening client may be a slave relay and as such you may want it to avoid + * certain checks like max listeners. Send this header back if to wish icecast + * to treat the client as a slave relay. + * + * icecast-slave: 1 + * * On client disconnection another request can be sent to a URL with the POST * information of * @@ -170,6 +176,9 @@ static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *s sscanf ((char *)ptr+url->timelimit_header_len, "%u\r\n", &limit); client->con->discon_time = global.time + limit; } + if (strncasecmp (ptr, "icecast-slave: 1", 16) == 0) + client->is_slave =1; + if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0) { char *eol; diff --git a/src/cfgfile.c b/src/cfgfile.c index 90ee97ef..8c4c8bf6 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -869,12 +869,13 @@ static int _parse_master (xmlNodePtr node, void *arg) { "username", config_get_str, &config->master_username }, { "password", config_get_str, &config->master_password }, { "bind", config_get_str, &config->master_bind }, - { "update-interval", config_get_int, &config->master_update_interval }, + { "interval", config_get_int, &config->master_update_interval }, { "relay-auth", config_get_bool, &config->master_relay_auth }, { "redirect", config_get_bool, &config->master_redirect }, { NULL, NULL, NULL }, }; + config->master_relay_auth = 1; if (parse_xml_tags (node, icecast_tags)) return -1; diff --git a/src/client.c b/src/client.c index a7f2b150..8f5f8c8b 100644 --- a/src/client.c +++ b/src/client.c @@ -45,14 +45,11 @@ #define CATMODULE "client" /* create a client_t with the provided connection and parser details. Return - * 0 on success, -1 if server limit has been reached. In either case a - * client_t is returned just in case a message needs to be returned. Should - * be called with global lock held. + * client_t ready for use. Should be called with global lock held. */ -int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser) +client_t *client_create (connection_t *con, http_parser_t *parser) { client_t *client = (client_t *)calloc(1, sizeof(client_t)); - int ret = 0; if (client == NULL) abort(); @@ -71,20 +68,6 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser) } } } - - /* don't do client limit check if on an SSL socket, as that will be an admin request */ - if (not_ssl_connection (con)) - { - ice_config_t *config = config_get_config (); - - if (config->client_limit < global.clients) - { - WARN2 ("server client limit reached (%d/%d)", config->client_limit, global.clients); - ret = -1; - } - config_release_config (); - } - stats_event_args (NULL, "clients", "%d", global.clients); client->con = con; client->parser = parser; @@ -92,8 +75,7 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser) client->refbuf->len = 0; /* force reader code to ignore buffer contents */ client->pos = 0; client->write_to_client = format_generic_write_to_client; - *c_ptr = client; - return ret; + return client; } diff --git a/src/client.h b/src/client.h index 8984cf94..b8236af6 100644 --- a/src/client.h +++ b/src/client.h @@ -87,7 +87,7 @@ struct _client_tag struct _client_tag *next; }; -int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser); +client_t *client_create (connection_t *con, http_parser_t *parser); void client_destroy(client_t *client); void client_send_504(client_t *client, char *message); void client_send_416(client_t *client); @@ -97,8 +97,8 @@ void client_send_403(client_t *client, const char *reason); void client_send_403redirect (client_t *client, const char *mount, const char *reason); void client_send_400(client_t *client, char *message); void client_send_302(client_t *client, const char *location); -int client_send_bytes (client_t *client, const void *buf, unsigned len); -int client_read_bytes (client_t *client, void *buf, unsigned len); +int client_send_bytes (client_t *client, const void *buf, unsigned len); +int client_read_bytes (client_t *client, void *buf, unsigned len); void client_set_queue (client_t *client, refbuf_t *refbuf); #endif /* __CLIENT_H__ */ diff --git a/src/connection.c b/src/connection.c index f91f6e5d..eaea45cf 100644 --- a/src/connection.c +++ b/src/connection.c @@ -112,7 +112,9 @@ static SSL_CTX *ssl_ctx; #endif /* filtering client connection based on IP */ -cache_file_contents banned_ip, allowed_ip, useragents; +cache_file_contents banned_ip, allowed_ip; +/* filtering listener connection based on useragent */ +cache_file_contents useragents; int connection_running = 0; rwlock_t _source_shutdown_rwlock; @@ -732,14 +734,7 @@ void *connection_thread (void *arg) client_t *client = NULL; global_lock(); - if (client_create (&client, con, NULL) < 0) - { - global_unlock(); - client_send_403 (client, "Icecast connection limit reached"); - /* don't be too eager as this is an imposed hard limit */ - thread_sleep (400000); - continue; - } + client = client_create (con, NULL); global_unlock(); if (client->server_conn->ssl && ssl_ok) @@ -778,8 +773,11 @@ void *connection_thread (void *arg) void connection_thread_startup () { - if (conn_tid == NULL) - conn_tid = thread_create ("connection", connection_thread, NULL, THREAD_ATTACHED); + connection_running = 0; + while (conn_tid) + thread_sleep (100001); + + conn_tid = thread_create ("connection", connection_thread, NULL, THREAD_ATTACHED); } void connection_thread_shutdown () @@ -1171,6 +1169,7 @@ static void _handle_get_request (client_t *client, char *passed_uri) aliases *alias; ice_config_t *config; char *uri = passed_uri; + int client_limit_reached = 0; DEBUG1 ("start with %s", passed_uri); config = config_get_config(); @@ -1202,6 +1201,11 @@ static void _handle_get_request (client_t *client, char *passed_uri) } alias = alias->next; } + if (global.clients > config->client_limit) + { + client_limit_reached = 1; + WARN2 ("server client limit reached (%d/%d)", config->client_limit, global.clients); + } config_release_config(); stats_event_inc(NULL, "client_connections"); @@ -1212,7 +1216,11 @@ static void _handle_get_request (client_t *client, char *passed_uri) if (uri != passed_uri) free (uri); return; } - auth_add_listener (uri, client); + /* drop non-admin GET requests here if clients limit reached */ + if (client_limit_reached) + client_send_403 (client, "Too many clients connected"); + else + auth_add_listener (uri, client); if (uri != passed_uri) free (uri); } diff --git a/src/global.h b/src/global.h index 625b9f3e..809dc612 100644 --- a/src/global.h +++ b/src/global.h @@ -50,7 +50,6 @@ typedef struct ice_global_tag /* redirection to slaves */ unsigned int redirect_count; - struct _redirect_host *redirectors; spin_t spinlock; struct rate_calc *out_bitrate; diff --git a/src/slave.c b/src/slave.c index c12c1b69..6004c5fa 100644 --- a/src/slave.c +++ b/src/slave.c @@ -62,6 +62,15 @@ #define CATMODULE "slave" +typedef struct _redirect_host +{ + struct _redirect_host *next; + time_t next_update; + char *server; + int port; +} redirect_host; + + static void *_slave_thread(void *arg); static void redirector_add (const char *server, int port, int interval); static redirect_host *find_slave_host (const char *server, int port); @@ -74,6 +83,8 @@ static volatile int restart_connection_thread = 0; static time_t streamlist_check = 0; static rwlock_t slaves_lock; +redirect_host *redirectors; + relay_server *relay_free (relay_server *relay) { relay_server *next = relay->next; @@ -175,6 +186,7 @@ void slave_initialize(void) update_settings = 0; update_all_mounts = 0; restart_connection_thread = 0; + redirectors = NULL; #ifndef HAVE_CURL ERROR0 ("streamlist request disabled, rebuild with libcurl if required"); #endif @@ -204,8 +216,8 @@ int redirect_client (const char *mountpoint, client_t *client) return 0; } which=(int) (((float)global.redirect_count)*rand()/(RAND_MAX+1.0)) + 1; - checking = global.redirectors; - trail = &global.redirectors; + checking = redirectors; + trail = &redirectors; DEBUG2 ("random selection %d (out of %d)", which, global.redirect_count); while (checking) @@ -231,7 +243,7 @@ int redirect_client (const char *mountpoint, client_t *client) /* add enough for "http://" the port ':' and nul */ int len = strlen (mountpoint) + strlen (checking->server) + 13; - INFO2 ("redirecting client to slave server " + INFO2 ("redirecting listener to slave server " "at %s:%d", checking->server, checking->port); location = malloc (len); snprintf (location, len, "http://%s:%d%s", checking->server, @@ -320,7 +332,7 @@ static client_t *open_relay_connection (relay_server *relay, relay_server_master "\r\n", mount, server_id, - relay->mp3metadata?"Icy-MetaData: 1\r\n":"", + relay->mp3metadata ? "Icy-MetaData: 1\r\n" : "", auth_header); memset (header, 0, sizeof(header)); if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0) @@ -377,15 +389,7 @@ static client_t *open_relay_connection (relay_server *relay, relay_server_master break; } global_lock (); - if (client_create (&client, con, parser) < 0) - { - global_unlock (); - /* make sure only the client_destory frees these */ - con = NULL; - parser = NULL; - client_destroy (client); - break; - } + client = client_create (con, parser); global_unlock (); sock_set_blocking (streamsock, SOCK_NONBLOCK); client_set_queue (client, NULL); @@ -524,7 +528,10 @@ static void check_relay_stream (relay_server *relay) { DEBUG1("Adding relay source at mountpoint \"%s\"", relay->localmount); if (relay->on_demand) + { + relay->start = global.time; slave_update_all_mounts(); + } } else WARN1 ("new relay but source \"%s\" already exists", relay->localmount); @@ -548,17 +555,30 @@ static void check_relay_stream (relay_server *relay) source->on_demand = relay->on_demand; - if (mountinfo && mountinfo->fallback_mount && mountinfo->fallback_override) + if (mountinfo) { - source_t *fallback; - avl_tree_rlock (global.source_tree); - fallback = source_find_mount (mountinfo->fallback_mount); - if (fallback && fallback->running && fallback->listeners) + if (mountinfo->fallback_mount && mountinfo->fallback_override) { - DEBUG1 ("fallback running with %lu listeners", fallback->listeners); - source->on_demand_req = 1; + source_t *fallback; + avl_tree_rlock (global.source_tree); + fallback = source_find_mount (mountinfo->fallback_mount); + if (fallback && fallback->running && fallback->listeners) + { + DEBUG1 ("fallback running with %lu listeners", fallback->listeners); + source->on_demand_req = 1; + } + avl_tree_unlock (global.source_tree); + } + } + else + { + if (relay->start) + { + thread_mutex_lock (&relay->source->lock); + source_update_settings (config, relay->source, mountinfo); + thread_mutex_unlock (&relay->source->lock); + relay->start = 0; } - avl_tree_unlock (global.source_tree); } config_release_config(); if (source->on_demand_req == 0) @@ -598,6 +618,7 @@ static void check_relay_stream (relay_server *relay) thread_mutex_unlock (&relay->source->lock); config_release_config (); stats_event (relay->localmount, "listeners", "0"); + relay->start = global.time; } } } @@ -827,13 +848,17 @@ static size_t streamlist_data (void *ptr, size_t size, size_t nmemb, void *strea { relay_server *r = calloc (1, sizeof (relay_server)); relay_server_master *m = calloc (1, sizeof (relay_server_master)); + m->ip = (char *)xmlStrdup (XMLSTR(master->server)); m->port = master->port; if (master->bind) m->bind = (char *)xmlStrdup (XMLSTR(master->bind)); m->mount = (char *)xmlStrdup (XMLSTR(buf)); r->masters = m; - r->localmount = (char *)xmlStrdup (XMLSTR(buf)); + if (strncmp (buf, "/admin/streams?mount=/", 22) == 0) + r->localmount = (char *)xmlStrdup (XMLSTR(buf+21)); + else + r->localmount = (char *)xmlStrdup (XMLSTR(buf)); r->mp3metadata = 1; r->on_demand = master->on_demand; r->interval = master->max_interval; @@ -874,7 +899,7 @@ static void *streamlist_thread (void *arg) port = master->ssl_port; } snprintf (auth, sizeof (auth), "%s:%s", master->username, master->password); - snprintf (url, sizeof (url), "%s://%s:%d/admin/streamlist.txt%s", + snprintf (url, sizeof (url), "%s://%s:%d/admin/streams%s", protocol, master->server, port, master->args); handle = curl_easy_init (); curl_easy_setopt (handle, CURLOPT_USERAGENT, master->server_id); @@ -892,7 +917,14 @@ static void *streamlist_thread (void *arg) curl_easy_setopt (handle, CURLOPT_INTERFACE, master->bind); if (curl_easy_perform (handle) != 0) - WARN2 ("Failed URL access \"%s\" (%s)", url, error); + { + /* fall back to traditional request */ + snprintf (url, sizeof (url), "%s://%s:%d/admin/streamlist.txt%s", + protocol, master->server, port, master->args); + curl_easy_setopt (handle, CURLOPT_URL, url); + if (curl_easy_perform (handle) != 0) + WARN2 ("Failed URL access \"%s\" (%s)", url, error); + } if (master->ok) { /* process retrieved relays */ @@ -1094,10 +1126,10 @@ relay_server *slave_find_relay (relay_server *relays, const char *mount) static void redirector_clearall (void) { thread_rwlock_wlock (&slaves_lock); - while (global.redirectors) + while (redirectors) { - redirect_host *current = global.redirectors; - global.redirectors = current->next; + redirect_host *current = redirectors; + redirectors = current->next; INFO2 ("removing %s:%d", current->server, current->port); free (current->server); free (current); @@ -1113,7 +1145,7 @@ void redirector_update (client_t *client) redirect_host *redirect; const char *rserver = httpp_get_query_param (client->parser, "rserver"); const char *value; - int rport, interval; + int rport = 0, interval = 0; if (rserver==NULL) return; value = httpp_get_query_param (client->parser, "rport"); @@ -1125,7 +1157,6 @@ void redirector_update (client_t *client) interval = atoi (value); if (interval < 5) return; - thread_rwlock_wlock (&slaves_lock); redirect = find_slave_host (rserver, rport); if (redirect == NULL) @@ -1147,7 +1178,7 @@ void redirector_update (client_t *client) */ static redirect_host *find_slave_host (const char *server, int port) { - redirect_host *redirect = global.redirectors; + redirect_host *redirect = redirectors; while (redirect) { if (strcmp (redirect->server, server) == 0 && redirect->port == port) @@ -1180,8 +1211,8 @@ static void redirector_add (const char *server, int port, int interval) redirect->next_update = (time_t)0; else redirect->next_update = global.time + interval; - redirect->next = global.redirectors; - global.redirectors = redirect; + redirect->next = redirectors; + redirectors = redirect; global.redirect_count++; INFO3 ("slave (%d) at %s:%d added", global.redirect_count, redirect->server, redirect->port); diff --git a/src/slave.h b/src/slave.h index 27720052..18e59646 100644 --- a/src/slave.h +++ b/src/slave.h @@ -42,13 +42,6 @@ typedef struct _relay_server { struct _relay_server *next; } relay_server; -typedef struct _redirect_host -{ - char *server; - int port; - time_t next_update; - struct _redirect_host *next; -} redirect_host; void slave_initialize(void); void slave_shutdown(void); diff --git a/src/source.c b/src/source.c index 68d36a58..8c11ff9f 100644 --- a/src/source.c +++ b/src/source.c @@ -359,6 +359,8 @@ void source_move_clients (source_t *source, source_t *dest) do { + client_t *leave_list = NULL; + thread_mutex_lock (&source->lock); if (source->on_demand == 0 && source->format == NULL) @@ -381,6 +383,13 @@ void source_move_clients (source_t *source, source_t *dest) client_t *client = source->active_clients; source->active_clients = client->next; + /* don't move known slave relays to streams which are not timed (fallback file) */ + if (dest->client == NULL && client->is_slave) + { + client->next = leave_list; + leave_list = client; + continue; + } /* when switching a client to a different queue, be wary of the * refbuf it's referring to, if it's http headers then we need * to write them so don't release it. @@ -397,14 +406,12 @@ void source_move_clients (source_t *source, source_t *dest) dest->active_clients = client; count++; } - if (count != source->listeners) - WARN2 ("count %lu, listeners %lu", count, source->listeners); - + source->active_clients = leave_list; INFO2 ("passing %lu listeners to \"%s\"", count, dest->mount); dest->listeners += count; - source->listeners = 0; - stats_event (source->mount, "listeners", "0"); + source->listeners -= count; + stats_event_args (source->mount, "listeners", "%lu", source->listeners); } while (0); @@ -700,6 +707,7 @@ static void process_listeners (source_t *source, int fast_clients_only, int dele { client_t *client, **client_p; client_t *fast_clients = NULL, **fast_client_tail = &fast_clients; + unsigned long listener_count = 0; /* where do we start from */ if (fast_clients_only) @@ -731,6 +739,7 @@ static void process_listeners (source_t *source, int fast_clients_only, int dele DEBUG0("Client removed"); continue; } + listener_count++; if (fast_client && client->check_buffer != format_check_file_buffer) { client_t *to_move = client; @@ -751,6 +760,10 @@ static void process_listeners (source_t *source, int fast_clients_only, int dele if (fast_clients) *client_p = fast_clients; + /* consistency check, these should match */ + if (fast_clients_only == 0 && listener_count != source->listeners) + ERROR3 ("mount %s has %lu, %lu", source->mount, listener_count, source->listeners); + /* has the listener count changed */ if (source->listeners != source->prev_listeners) { diff --git a/src/stats.c b/src/stats.c index 7b5c514b..50b04101 100644 --- a/src/stats.c +++ b/src/stats.c @@ -1118,7 +1118,7 @@ void stats_get_streamlist (char *buffer, size_t remaining) if (source->hidden == 0) { - if (remaining <= strlen (source->source)+2) + if (remaining <= strlen (source->source)+3) { WARN0 ("streamlist was truncated"); break; @@ -1136,6 +1136,51 @@ void stats_get_streamlist (char *buffer, size_t remaining) thread_mutex_unlock (&_stats_mutex); } + + +/* get a list of refbufs which contain urls for slaves to use for relaying */ +refbuf_t *stats_get_streams (void) +{ + avl_node *node; + int remaining = 4096; + refbuf_t *start = refbuf_new (remaining), *cur = start; + const char *pre = "/admin/streams?mount="; + char *buffer = cur->data; + + /* now the stats for each source */ + thread_mutex_lock (&_stats_mutex); + node = avl_get_first(_stats.source_tree); + while (node) + { + int ret; + stats_source_t *source = (stats_source_t *)node->key; + + if (source->hidden == 0) + { + if (remaining <= strlen (source->source) + strlen (pre) + 3) + { + cur->len = 4096 - remaining; + cur->next = refbuf_new (4096); + remaining = 4096; + cur = cur->next; + buffer = cur->data; + } + ret = snprintf (buffer, remaining, "%s%s\r\n", pre, source->source); + if (ret > 0) + { + buffer += ret; + remaining -= ret; + } + } + node = avl_get_next(node); + } + thread_mutex_unlock (&_stats_mutex); + cur->len = 4096 - remaining; + return start; +} + + + /* This removes any source stats from virtual mountpoints, ie mountpoints * where no source_t exists. This function requires the global sources lock * to be held before calling. diff --git a/src/stats.h b/src/stats.h index ab699864..b6417ad2 100644 --- a/src/stats.h +++ b/src/stats.h @@ -77,6 +77,7 @@ void stats_shutdown(void); void stats_global(ice_config_t *config); stats_t *stats_get_stats(void); void stats_get_streamlist (char *buffer, size_t remaining); +refbuf_t *stats_get_streams (void); void stats_clear_virtual_mounts (void); void stats_event(const char *source, const char *name, const char *value); diff --git a/web/status2.xsl b/web/status2.xsl index 3f44d3bf..6f11e171 100755 --- a/web/status2.xsl +++ b/web/status2.xsl @@ -1,11 +1,11 @@ - +
-MountPoint,Connections,Stream Name,Current Listeners,Description,Currently Playing,Stream URL 
-Global,Client: Source: ,,,,
+Global,Clients:,Sources:,,,,
+MountPoint,Connections,Stream Name,Current Listeners,Description,Currently Playing,Stream URL
 
-,,,,, - ,
+,,,,, ,