Commit 79adab01 authored by Karl Heyes's avatar Karl Heyes

allow for more updating over HUP. Made the YP engine only read the stats instead

of updating them, so source header parsing is done in the apply mount. Per-mount
stream settings also allow for overriding the incoming settings.

svn path=/icecast/trunk/icecast/; revision=9325
parent d57a19ac
...@@ -347,7 +347,14 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al ...@@ -347,7 +347,14 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al
<dump-file>/tmp/dump-example1.ogg</dump-file> <dump-file>/tmp/dump-example1.ogg</dump-file>
<fallback-mount>/example2.ogg</fallback-mount> <fallback-mount>/example2.ogg</fallback-mount>
<fallback-override>1</fallback-override> <fallback-override>1</fallback-override>
<no-yp>1</no-yp> <public>1</public>
<stream-name>My audio stream</stream-name>
<stream-description>My audio description</stream-description>
<stream-url>http://some.place.com</stream-url>
<genre>classical</genre>
<bitrate>64</bitrate>
<type>application/ogg</type>
<subtype>vorbis</subtype>
<hidden>1</hidden> <hidden>1</hidden>
<burst-size>65536</burst-size> <burst-size>65536</burst-size>
<mp3-metadata-interval>4096</mp3-metadata-interval> <mp3-metadata-interval>4096</mp3-metadata-interval>
...@@ -358,7 +365,16 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al ...@@ -358,7 +365,16 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al
</mount> </mount>
</pre> </pre>
<p>This section contains settings which apply only to a specific mountpoint. Within this section you can reserve a specific mountpoint and set a source username/password for that mountpoint (not yet implemented) as well as specify individual settings which will apply only to the supplied mountpoint. <p>This section contains the settings which apply only to a specific mountpoint and applies to
an incoming stream whether it is a relay or a source client. The purpose of the mount definition
is to state certain information that can override either global/default settings or settings
provided from the incoming stream.
</p>
<p>A mount does not need to be stated for each incoming source although you may want to
specific certain settings like the maximum number of listeners or a mountpoint specific
username/password. As a general rule, only define what you need to but each mount definition
needs at least the mount-name. Changes to most of these will apply across a configuration file
re-read even on active streams, however some only apply when the stream starts or ends.
</p> </p>
<h4>mount-name</h4> <h4>mount-name</h4>
<div class="indentedbox"> <div class="indentedbox">
...@@ -397,11 +413,67 @@ This multi-level fallback allows clients to cascade several mountpoints. ...@@ -397,11 +413,67 @@ This multi-level fallback allows clients to cascade several mountpoints.
When enabled, this allows a connecting source client or relay on this mountpoint to move When enabled, this allows a connecting source client or relay on this mountpoint to move
listening clients back from the fallback mount. listening clients back from the fallback mount.
</div> </div>
<h4>no-yp</h4> <h4>no-yp (deprecated)</h4>
<div class="indentedbox">
<p>Setting this option prevents this mountpoint from advertising on YP. The default is 0
so YP advertising can occur however you may want to prevent it here if you intend listeners
to connect to a local relay instead. Deprecated option, replaced by &lt;public&gt;
</p>
</div>
<h4>public</h4>
<div class="indentedbox">
<p>The default setting for this is -1 indicating that it is up to the source client or
relay to determine if this mountpoint should advertise. A setting of 0 will prevent any
advertising and a setting of 1 will force it to advertise. If you do force advertising
you may need to set other settings listed below as the YP server can refuse to advertise
if there is not enough information provided.
</p>
</div>
<h4>stream-name</h4>
<div class="indentedbox">
<p>Setting this will add the specified name to the stats (and therefore YP) for this
mountpoint even if the source client/relay provide one.
</p>
</div>
<h4>stream-description</h4>
<div class="indentedbox">
<p>Setting this will add the specified description to the stats (and therefore YP) for
this mountpoint even if the source client/relay provide one.
</p>
</div>
<h4>stream-url</h4>
<div class="indentedbox">
<p>Setting this will add the specified URL to the stats (and therefore YP) for this
mountpoint even if the source client/relay provide one. The URL is generally for
directing people to a website.
</p>
</div>
<h4>genre</h4>
<div class="indentedbox">
<p>Setting this will add the specified genre to the stats (and therefore YP) for this
mountpoint even if the source client/relay provide one. This can be anything be using
certain key words can help searches in the YP directories.
</p>
</div>
<h4>bitrate</h4>
<div class="indentedbox">
<p>Setting this will add the specified bitrate to the stats (and therefore YP) for this
mountpoint even if the source client/relay provide one. This is stated in kbps.
</p>
</div>
<h4>type</h4>
<div class="indentedbox">
<p>Setting this will add the specified mime type to the stats (and therefore YP) for
this mountpoint even if the source client/relay provide one. It is very unlikely that
this will be needed.
</p>
</div>
<h4>subtype</h4>
<div class="indentedbox"> <div class="indentedbox">
Setting this option prevents this mountpoint from advertising on YP. The default is 0 so YP <p>Setting this will add the specified subtype to the stats (and therefore YP) for
advertising occurs however you may want to prevent it here if you intend listeners to connect this mountpoint. The subtype is really to help the YP server to identify the components
to a local relay instead of the type. An example setting is vorbis/theora do indicate the codecs in an Ogg stream
</p>
</div> </div>
<h4>burst-size</h4> <h4>burst-size</h4>
<div class="indentedbox"> <div class="indentedbox">
......
...@@ -191,6 +191,12 @@ void config_clear(ice_config_t *c) ...@@ -191,6 +191,12 @@ void config_clear(ice_config_t *c)
xmlFree(mount->password); xmlFree(mount->password);
xmlFree(mount->dumpfile); xmlFree(mount->dumpfile);
xmlFree(mount->fallback_mount); xmlFree(mount->fallback_mount);
xmlFree(mount->stream_name);
xmlFree(mount->stream_description);
xmlFree(mount->stream_url);
xmlFree(mount->stream_genre);
xmlFree(mount->bitrate);
xmlFree(mount->type);
if (mount->cluster_password) { if (mount->cluster_password) {
xmlFree(mount->cluster_password); xmlFree(mount->cluster_password);
} }
...@@ -529,6 +535,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ...@@ -529,6 +535,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
mount->max_listeners = -1; mount->max_listeners = -1;
mount->burst_size = -1; mount->burst_size = -1;
mount->mp3_meta_interval = -1; mount->mp3_meta_interval = -1;
mount->yp_public = -1;
mount->next = NULL; mount->next = NULL;
do { do {
...@@ -577,7 +584,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ...@@ -577,7 +584,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
} }
else if (strcmp(node->name, "no-yp") == 0) { else if (strcmp(node->name, "no-yp") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
mount->no_yp = atoi(tmp); mount->yp_public = atoi(tmp) == 0 ? -1 : 0;
if(tmp) xmlFree(tmp); if(tmp) xmlFree(tmp);
} }
else if (strcmp(node->name, "hidden") == 0) { else if (strcmp(node->name, "hidden") == 0) {
...@@ -635,10 +642,36 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ...@@ -635,10 +642,36 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
} else if (strcmp(node->name, "cluster-password") == 0) { } else if (strcmp(node->name, "cluster-password") == 0) {
mount->cluster_password = (char *)xmlNodeListGetString( mount->cluster_password = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1); doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "stream-name") == 0) {
mount->stream_name = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "stream-description") == 0) {
mount->stream_description = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "stream-url") == 0) {
mount->stream_url = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "genre") == 0) {
mount->stream_genre = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "bitrate") == 0) {
mount->bitrate = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "public") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
mount->yp_public = atoi (tmp);
if(tmp) xmlFree(tmp);
} else if (strcmp(node->name, "type") == 0) {
mount->type = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "subtype") == 0) {
mount->subtype = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
} }
} while ((node = node->next)); } while ((node = node->next));
} }
static void _parse_relay(xmlDocPtr doc, xmlNodePtr node, static void _parse_relay(xmlDocPtr doc, xmlNodePtr node,
ice_config_t *configuration) ice_config_t *configuration)
{ {
......
...@@ -57,7 +57,6 @@ typedef struct _mount_proxy { ...@@ -57,7 +57,6 @@ typedef struct _mount_proxy {
int burst_size; /* amount to send to a new client if possible, -1 take int burst_size; /* amount to send to a new client if possible, -1 take
* from global setting */ * from global setting */
unsigned int queue_size_limit; unsigned int queue_size_limit;
int no_yp; /* Do we prevent YP on this mount */
int hidden; /* Do we list this on the xsl pages */ int hidden; /* Do we list this on the xsl pages */
unsigned int source_timeout; /* source timeout in seconds */ unsigned int source_timeout; /* source timeout in seconds */
int mp3_meta_interval; /* outgoing per-stream metadata interval */ int mp3_meta_interval; /* outgoing per-stream metadata interval */
...@@ -65,6 +64,16 @@ typedef struct _mount_proxy { ...@@ -65,6 +64,16 @@ typedef struct _mount_proxy {
char *auth_type; /* Authentication type */ char *auth_type; /* Authentication type */
char *cluster_password; char *cluster_password;
config_options_t *auth_options; /* Options for this type */ config_options_t *auth_options; /* Options for this type */
char *stream_name;
char *stream_description;
char *stream_url;
char *stream_genre;
char *bitrate;
char *type;
char *subtype;
int yp_public;
struct _mount_proxy *next; struct _mount_proxy *next;
} mount_proxy; } mount_proxy;
......
...@@ -449,6 +449,7 @@ int connection_complete_source (source_t *source) ...@@ -449,6 +449,7 @@ int connection_complete_source (source_t *source)
if (global.sources < config->source_limit) if (global.sources < config->source_limit)
{ {
char *contenttype; char *contenttype;
mount_proxy *mountinfo;
format_type_t format_type; format_type_t format_type;
/* setup format handler */ /* setup format handler */
...@@ -488,11 +489,6 @@ int connection_complete_source (source_t *source) ...@@ -488,11 +489,6 @@ int connection_complete_source (source_t *source)
global.sources++; global.sources++;
global_unlock(); global_unlock();
/* set global settings first */
source->queue_size_limit = config->queue_size_limit;
source->timeout = config->source_timeout;
source->burst_size = config->burst_size;
/* for relays, we don't yet have a client, however we do require one /* for relays, we don't yet have a client, however we do require one
* to retrieve the stream from. This is created here, quite late, * to retrieve the stream from. This is created here, quite late,
* because we can't use this client to return an error code/message, * because we can't use this client to return an error code/message,
...@@ -515,7 +511,11 @@ int connection_complete_source (source_t *source) ...@@ -515,7 +511,11 @@ int connection_complete_source (source_t *source)
} }
} }
source_update_settings (config, source); source->running = 1;
mountinfo = config_find_mount (config, source->mount);
if (mountinfo == NULL)
source_update_settings (config, source, mountinfo);
source_recheck_mounts ();
config_release_config(); config_release_config();
source->shutdown_rwlock = &_source_shutdown_rwlock; source->shutdown_rwlock = &_source_shutdown_rwlock;
......
...@@ -201,7 +201,7 @@ static void format_mp3_apply_settings (client_t *client, format_plugin_t *format ...@@ -201,7 +201,7 @@ static void format_mp3_apply_settings (client_t *client, format_plugin_t *format
{ {
mp3_state *source_mp3 = format->_state; mp3_state *source_mp3 = format->_state;
if (mount->mp3_meta_interval <= 0) if (mount== NULL || mount->mp3_meta_interval <= 0)
{ {
char *metadata = httpp_getvar (client->parser, "icy-metaint"); char *metadata = httpp_getvar (client->parser, "icy-metaint");
source_mp3->interval = -1; source_mp3->interval = -1;
...@@ -214,7 +214,7 @@ static void format_mp3_apply_settings (client_t *client, format_plugin_t *format ...@@ -214,7 +214,7 @@ static void format_mp3_apply_settings (client_t *client, format_plugin_t *format
} }
else else
source_mp3->interval = mount->mp3_meta_interval; source_mp3->interval = mount->mp3_meta_interval;
DEBUG2 ("mp3 interval %d, %d", mount->mp3_meta_interval, source_mp3->interval); DEBUG1 ("mp3 interval %d", source_mp3->interval);
} }
......
...@@ -240,8 +240,6 @@ void source_clear_source (source_t *source) ...@@ -240,8 +240,6 @@ void source_clear_source (source_t *source)
source->no_mount = 0; source->no_mount = 0;
source->shoutcast_compat = 0; source->shoutcast_compat = 0;
source->max_listeners = -1; source->max_listeners = -1;
source->yp_public = 0;
source->yp_prevent = 0;
source->hidden = 0; source->hidden = 0;
source->client_stats_update = 0; source->client_stats_update = 0;
util_dict_free (source->audio_info); util_dict_free (source->audio_info);
...@@ -514,7 +512,6 @@ static void source_init (source_t *source) ...@@ -514,7 +512,6 @@ static void source_init (source_t *source)
ice_config_t *config = config_get_config(); ice_config_t *config = config_get_config();
char *listenurl, *str; char *listenurl, *str;
int listen_url_size; int listen_url_size;
char *s;
/* 6 for max size of port */ /* 6 for max size of port */
listen_url_size = strlen("http://") + strlen(config->hostname) + listen_url_size = strlen("http://") + strlen(config->hostname) +
...@@ -526,23 +523,6 @@ static void source_init (source_t *source) ...@@ -526,23 +523,6 @@ static void source_init (source_t *source)
config->hostname, config->port, source->mount); config->hostname, config->port, source->mount);
config_release_config(); config_release_config();
do
{
str = "0";
if (source->yp_prevent)
break;
if ((str = httpp_getvar(source->parser, "ice-public")))
break;
if ((str = httpp_getvar(source->parser, "icy-pub")))
break;
/* handle header from icecast v2 release */
if ((str = httpp_getvar(source->parser, "icy-public")))
break;
str = "0";
} while (0);
source->yp_public = atoi (str);
stats_event (source->mount, "public", str);
str = httpp_getvar(source->parser, "ice-audio-info"); str = httpp_getvar(source->parser, "ice-audio-info");
source->audio_info = util_dict_new(); source->audio_info = util_dict_new();
if (str) if (str)
...@@ -601,23 +581,6 @@ static void source_init (source_t *source) ...@@ -601,23 +581,6 @@ static void source_init (source_t *source)
avl_tree_unlock(global.source_tree); avl_tree_unlock(global.source_tree);
} }
slave_rebuild_mounts ();
if (source->yp_public) {
yp_add (source);
}
else {
/* If we are a private server, see if ic*-name and description
is provided, and if so, add them to the stats */
if ((s = httpp_getvar(source->parser, "ice-name"))) {
stats_event (source->mount, "server_name", s);
}
if ((s = httpp_getvar(source->parser, "icy-name"))) {
stats_event (source->mount, "server_name", s);
}
if ((s = httpp_getvar(source->parser, "ice-description"))) {
stats_event (source->mount, "server_description", s);
}
}
} }
...@@ -882,64 +845,202 @@ static void _parse_audio_info (source_t *source, const char *s) ...@@ -882,64 +845,202 @@ static void _parse_audio_info (source_t *source, const char *s)
/* Apply the mountinfo details to the source */ /* Apply the mountinfo details to the source */
static void source_apply_mount (source_t *source, mount_proxy *mountinfo) static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
{ {
char *str;
int val;
http_parser_t *parser = NULL;
DEBUG1("Applying mount information for \"%s\"", source->mount); DEBUG1("Applying mount information for \"%s\"", source->mount);
source->max_listeners = mountinfo->max_listeners; if (mountinfo)
source->fallback_override = mountinfo->fallback_override; {
source->no_mount = mountinfo->no_mount; source->max_listeners = mountinfo->max_listeners;
source->hidden = mountinfo->hidden; source->fallback_override = mountinfo->fallback_override;
stats_event_hidden (source->mount, NULL, source->hidden); source->no_mount = mountinfo->no_mount;
source->hidden = mountinfo->hidden;
}
/* if a setting is available in the mount details then use it, else
* check the parser details. */
if (source->client)
parser = source->client->parser;
/* public */
if (mountinfo && mountinfo->yp_public >= 0)
val = mountinfo->yp_public;
else
{
do {
str = httpp_getvar (parser, "ice-public");
if (str) break;
str = httpp_getvar (parser, "icy-pub");
if (str) break;
str = httpp_getvar (parser, "x-audiocast-public");
if (str) break;
/* handle header from icecast v2 release */
str = httpp_getvar (parser, "icy-public");
if (str) break;
str = "0";
} while (0);
val = atoi (str);
}
stats_event_args (source->mount, "public", "%d", val);
if (source->yp_public != val)
{
DEBUG1 ("YP changed to %d", val);
if (val)
yp_add (source->mount);
else
yp_remove (source->mount);
source->yp_public = val;
}
/* stream name */
if (mountinfo && mountinfo->stream_name)
str = mountinfo->stream_name;
else
{
do {
str = httpp_getvar (parser, "ice-name");
if (str) break;
str = httpp_getvar (parser, "icy-name");
if (str) break;
str = httpp_getvar (parser, "x-audiocast-name");
if (str) break;
str = "Unspecified name";
} while (0);
}
stats_event (source->mount, "server_name", str);
/* stream description */
if (mountinfo && mountinfo->stream_description)
str = mountinfo->stream_description;
else
{
do {
str = httpp_getvar (parser, "ice-description");
if (str) break;
str = httpp_getvar (parser, "icy-description");
if (str) break;
str = httpp_getvar (parser, "x-audiocast-description");
if (str) break;
str = "Unspecified description";
} while (0);
}
stats_event (source->mount, "server_description", str);
/* stream URL */
if (mountinfo && mountinfo->stream_url)
str = mountinfo->stream_url;
else
{
do {
str = httpp_getvar (parser, "ice-url");
if (str) break;
str = httpp_getvar (parser, "icy-url");
if (str) break;
str = httpp_getvar (parser, "x-audiocast-url");
if (str) break;
} while (0);
}
stats_event (source->mount, "server_url", str);
/* stream genre */
if (mountinfo && mountinfo->stream_genre)
str = mountinfo->stream_genre;
else
{
do {
str = httpp_getvar (parser, "ice-genre");
if (str) break;
str = httpp_getvar (parser, "icy-genre");
if (str) break;
str = httpp_getvar (parser, "x-audiocast-genre");
if (str) break;
str = "various";
} while (0);
}
stats_event (source->mount, "genre", str);
/* stream bitrate */
if (mountinfo && mountinfo->bitrate)
str = mountinfo->bitrate;
else
{
do {
str = httpp_getvar (parser, "ice-bitrate");
if (str) break;
str = httpp_getvar (parser, "icy-br");
if (str) break;
str = httpp_getvar (parser, "x-audiocast-bitrate");
} while (0);
}
stats_event (source->mount, "bitrate", str);
/* handle MIME-type */
if (mountinfo && mountinfo->type)
stats_event (source->mount, "server_type", mountinfo->type);
else
if (source->format)
stats_event (source->mount, "server_type", source->format->contenttype);
if (mountinfo && mountinfo->subtype)
stats_event (source->mount, "subtype", mountinfo->subtype);
if (mountinfo->fallback_mount) if (mountinfo && mountinfo->fallback_mount)
{
char *mount = source->fallback_mount;
source->fallback_mount = strdup (mountinfo->fallback_mount); source->fallback_mount = strdup (mountinfo->fallback_mount);
free (mount);
}
else
source->fallback_mount = NULL;
if (mountinfo->auth_type != NULL) if (mountinfo && mountinfo->auth_type != NULL && source->authenticator == NULL)
{ {
source->authenticator = auth_get_authenticator( source->authenticator = auth_get_authenticator(
mountinfo->auth_type, mountinfo->auth_options); mountinfo->auth_type, mountinfo->auth_options);
stats_event(source->mount, "authenticator", mountinfo->auth_type); stats_event(source->mount, "authenticator", mountinfo->auth_type);
} }
if (mountinfo->dumpfile)
if (mountinfo && mountinfo->dumpfile)
{ {
free (source->dumpfilename); char *filename = source->dumpfilename;
source->dumpfilename = strdup (mountinfo->dumpfile); source->dumpfilename = strdup (mountinfo->dumpfile);
free (filename);
} }
else
source->dumpfilename = NULL;
if (mountinfo->queue_size_limit) if (mountinfo && mountinfo->queue_size_limit)
source->queue_size_limit = mountinfo->queue_size_limit; source->queue_size_limit = mountinfo->queue_size_limit;
if (mountinfo->source_timeout) if (mountinfo && mountinfo->source_timeout)
source->timeout = mountinfo->source_timeout; source->timeout = mountinfo->source_timeout;
if (mountinfo->burst_size >= 0) if (mountinfo && mountinfo->burst_size >= 0)
source->burst_size = (unsigned int)mountinfo->burst_size; source->burst_size = (unsigned int)mountinfo->burst_size;
if (mountinfo->no_yp)
source->yp_prevent = 1;
if (source->format && source->format->apply_settings) if (source->format && source->format->apply_settings)
source->format->apply_settings (source->client, source->format, mountinfo); source->format->apply_settings (source->client, source->format, mountinfo);
} }
void source_update_settings (ice_config_t *config, source_t *source) /* update the specified source with details from the config or mount.
* mountinfo can be NULL in which case default settings should be taken
*/
void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo)
{ {
mount_proxy *mountinfo = config_find_mount (config, source->mount);
/* set global settings first */ /* set global settings first */
source->queue_size_limit = config->queue_size_limit; source->queue_size_limit = config->queue_size_limit;
source->timeout = config->source_timeout; source->timeout = config->source_timeout;
source->burst_size = config->burst_size; source->burst_size = config->burst_size;
source->dumpfilename = NULL;
if (mountinfo) source_apply_mount (source, mountinfo);
source_apply_mount (source, mountinfo);
if (source->fallback_mount) if (source->fallback_mount)