1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-06-23 06:25:24 +00:00

sync work to kh33

svn path=/icecast/branches/kh/icecast/; revision=14653
This commit is contained in:
Karl Heyes 2008-04-03 15:15:10 +00:00
parent 1cac5ce6d9
commit 4f1c9156bf
18 changed files with 236 additions and 101 deletions

20
NEWS
View File

@ -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 <mount> 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 <master> tag block instead of <master-*>
tags allowed are <username> <password> <server> <port> <redirect> <bind>
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

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -26,6 +26,7 @@
#include <sys/stat.h>
#include "auth.h"
#include "auth_htpasswd.h"
#include "source.h"
#include "client.h"
#include "cfgfile.h"

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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.

View File

@ -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);

View File

@ -1,11 +1,11 @@
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
<xsl:output method="text" media-type="text/plain" indent="yes" encoding="UTF-8" />
<xsl:template match = "/icestats" >
<pre>
MountPoint,Connections,Stream Name,Current Listeners,Description,Currently Playing,Stream URL
Global,Client:<xsl:value-of select="connections" /> Source: <xsl:value-of select="source_connections" />,,<xsl:value-of select="listeners" />,,
Global,Clients:<xsl:value-of select="connections" />,Sources:<xsl:value-of select="source_client_connections" />,,<xsl:value-of select="listeners" />,,
MountPoint,Connections,Stream Name,Current Listeners,Description,Currently Playing,Stream URL
<xsl:for-each select="source">
<xsl:value-of select="@mount" />,,<xsl:value-of select="name" />,<xsl:value-of select="listeners" />,<xsl:value-of select="description" />,<xsl:value-of select="artist" /> - <xsl:value-of select="title" />,<xsl:value-of select="url" />
<xsl:value-of select="@mount" />,<xsl:value-of select="listener_connections" />,<xsl:value-of select="server_name" />,<xsl:value-of select="listeners" />,<xsl:value-of select="server_description" />,<xsl:value-of select="artist" /> <xsl:value-of select="title" />,<xsl:value-of select="listenurl" />
</xsl:for-each>
</pre>
</xsl:template>