1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-11-03 04:17:17 -05:00

resync with recent work on trunk, playlistlog, shoutcast-mount

svn path=/icecast/branches/kh/icecast/; revision=8215
This commit is contained in:
Karl Heyes 2004-11-17 22:31:42 +00:00
parent 4987a4265d
commit 87e69eb421
17 changed files with 252 additions and 101 deletions

View File

@ -164,6 +164,7 @@ The URL which icecast2 uses to communicate with the Directory server. The value
</listen-socket>
<fileserve>1</fileserve>
<shoutcast-mount>/live.nsv</shoutcast-mount>
</pre>
<p>This section contains miscellaneous server settings. Note that multiple listen-socket sections may be configured in order to have icecast2 listen on multiple network interfaces. If a bind-address is not specified for a particular listen-socket, then the hostname parameter will be used to specify the address that will be bound.
</p>
@ -183,6 +184,12 @@ This optional flag will indicate that this port will operate in 'shoutcast-compa
<div class="indentedbox">
This flag turns on the icecast2 fileserver from which static files can be served. All files are served relative to the path specified in the &lt;paths&gt;&lt;webroot&gt; configuration setting.
</div>
<h4>shoutcast-mount</h4>
<div class="indentedbox">
An optional mountpoint to use when shoutcast DSP compatible clients connect. The default is /stream but can
be overridden here to use an alternative name which may include an extension that some clients require for
certain formats.
</div>
<p>
<br />
<br />
@ -471,6 +478,7 @@ Aliases are used to provide a way to create multiple mountpoints that refer to t
&lt;logging&gt;
&lt;accesslog&gt;access.log&lt;/accesslog&gt;
&lt;errorlog&gt;error.log&lt;/errorlog&gt;
&lt;playlistlog&gt;playlist.log&lt;/playlistlog&gt;
&lt;loglevel&gt;4&lt;/loglevel&gt; &lt;-- 4 Debug, 3 Info, 2 Warn, 1 Error --&gt;
&lt;/logging&gt;
</pre>
@ -486,6 +494,10 @@ Into this file, all requests made to the icecast2 will be logged. This file is
<div class="indentedbox">
All icecast generated log messages will be written to this file. If the loglevel is set too high (Debug for instance) then this file can grow fairly large over time. Currently, there is no log-rotation implemented.
</div>
<h4>playlistlog</h4>
<div class="indentedbox">
Into this file, a log of all metadata for each mountpoint will be written.  The format of the logfile will most likely change over time as we narrow in on a standard format for this.  Currently, the file is pipe delimited.  This option is optional and can be removed entirely from the config file.
</div>
<h4>loglevel</h4>
<div class="indentedbox">
Indicates what messages are logged by icecast. Log messages are categorized into one of 4 types, Debug, Info, Warn, and Error.<br /><br />The following mapping can be used to set the appropraite value :

View File

@ -294,7 +294,6 @@ void admin_handle_request(client_t *client, char *uri)
{
char *mount, *command_string;
int command;
int noauth = 0;
DEBUG1("Admin request (%s)", uri);
if (!((strcmp(uri, "/admin.cgi") == 0) ||
@ -321,33 +320,15 @@ void admin_handle_request(client_t *client, char *uri)
return;
}
mount = httpp_get_query_param(client->parser, "mount");
if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) {
source_t *source;
ice_config_t *config = config_get_config ();
mount = "/";
noauth = 1;
avl_tree_rlock(global.source_tree);
source = source_find_mount_raw(mount);
if (source == NULL) {
WARN2("Admin command %s on non-existent source %s",
command_string, mount);
avl_tree_unlock(global.source_tree);
client_send_400(client, "Mount / does not exist");
return;
}
else {
if (source->shoutcast_compat == 0) {
ERROR0("Illegal call to change metadata, source not shoutcast compatible");
avl_tree_unlock (global.source_tree);
client_send_400 (client, "Illegal metadata call");
return;
}
}
avl_tree_unlock(global.source_tree);
httpp_set_query_param (client->parser, "mount", config->shoutcast_mount);
config_release_config ();
}
mount = httpp_get_query_param(client->parser, "mount");
if(mount != NULL) {
source_t *source;
@ -372,7 +353,9 @@ void admin_handle_request(client_t *client, char *uri)
}
else
{
if (!source->shoutcast_compat)
INFO2("Received admin command %s on mount \"%s\"",
command_string, mount);
if (source->shoutcast_compat == 0)
{
if (source->running == 0 && source->on_demand == 0)
{
@ -382,18 +365,16 @@ void admin_handle_request(client_t *client, char *uri)
client_send_400 (client, "Source is not available");
return;
}
}
INFO2("Received admin command %s on mount \"%s\"",
command_string, mount);
if (client->authenticated != 1)
{
if (connection_check_source_pass(client->parser, mount) == 0)
if (client->authenticated != 1)
{
INFO1("Bad or missing password on mount modification admin "
"request (command: %s)", command_string);
avl_tree_unlock(global.source_tree);
client_send_401(client);
return;
if (connection_check_source_pass(client->parser, mount) == 0)
{
INFO1("Bad or missing password on mount modification admin "
"request (command: %s)", command_string);
avl_tree_unlock(global.source_tree);
client_send_401(client);
return;
}
}
}
admin_handle_mount_request (client, source, command);
@ -1007,6 +988,9 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
DEBUG2("Metadata on mountpoint %s changed to \"%s\"",
source->mount, value);
stats_event(source->mount, "title", value);
/* At this point, we assume that the metadata passed in
is encoded in UTF-8 */
logging_playlist(source->mount, value, source->listeners);
/* If we get an update on the mountpoint, force a
* yp touch */
yp_touch (source->mount);

View File

@ -39,10 +39,12 @@
#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
#define CONFIG_DEFAULT_RELAY_PASSWORD "changeme"
#define CONFIG_DEFAULT_SHOUTCAST_MOUNT "/stream"
#define CONFIG_DEFAULT_ICE_LOGIN 0
#define CONFIG_DEFAULT_FILESERVE 1
#define CONFIG_DEFAULT_TOUCH_FREQ 5
#define CONFIG_DEFAULT_HOSTNAME "localhost"
#define CONFIG_DEFAULT_PLAYLIST_LOG NULL
#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
#define CONFIG_DEFAULT_ERROR_LOG "error.log"
#define CONFIG_DEFAULT_LOG_LEVEL 4
@ -150,10 +152,14 @@ void config_clear(ice_config_t *c)
xmlFree(c->adminroot_dir);
if (c->pidfile)
xmlFree(c->pidfile);
if (c->playlist_log && c->playlist_log != CONFIG_DEFAULT_PLAYLIST_LOG)
xmlFree(c->playlist_log);
if (c->access_log && c->access_log != CONFIG_DEFAULT_ACCESS_LOG)
xmlFree(c->access_log);
if (c->error_log && c->error_log != CONFIG_DEFAULT_ERROR_LOG)
xmlFree(c->error_log);
if (c->shoutcast_mount && c->shoutcast_mount != CONFIG_DEFAULT_SHOUTCAST_MOUNT)
xmlFree(c->shoutcast_mount);
for(i=0; i < MAX_LISTEN_SOCKETS; i++) {
if (c->listeners[i].bind_address) xmlFree(c->listeners[i].bind_address);
}
@ -324,6 +330,7 @@ static void _set_defaults(ice_config_t *configuration)
configuration->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
configuration->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
configuration->source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
configuration->shoutcast_mount = CONFIG_DEFAULT_SHOUTCAST_MOUNT;
configuration->ice_login = CONFIG_DEFAULT_ICE_LOGIN;
configuration->fileserve = CONFIG_DEFAULT_FILESERVE;
configuration->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
@ -345,6 +352,7 @@ static void _set_defaults(ice_config_t *configuration)
configuration->log_dir = CONFIG_DEFAULT_LOG_DIR;
configuration->webroot_dir = CONFIG_DEFAULT_WEBROOT_DIR;
configuration->adminroot_dir = CONFIG_DEFAULT_ADMINROOT_DIR;
configuration->playlist_log = CONFIG_DEFAULT_PLAYLIST_LOG;
configuration->access_log = CONFIG_DEFAULT_ACCESS_LOG;
configuration->error_log = CONFIG_DEFAULT_ERROR_LOG;
configuration->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
@ -439,6 +447,11 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
configuration->master_relay_auth = atoi(tmp);
xmlFree (tmp);
} else if (strcmp(node->name, "shoutcast-mount") == 0) {
if (configuration->shoutcast_mount &&
configuration->shoutcast_mount != CONFIG_DEFAULT_SHOUTCAST_MOUNT)
xmlFree(configuration->shoutcast_mount);
configuration->shoutcast_mount = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "limits") == 0) {
_parse_limits(doc, node->xmlChildrenNode, configuration);
} else if (strcmp(node->name, "relay") == 0) {
@ -919,6 +932,9 @@ static void _parse_logging(xmlDocPtr doc, xmlNodePtr node,
} else if (strcmp(node->name, "errorlog") == 0) {
if (configuration->error_log && configuration->error_log != CONFIG_DEFAULT_ERROR_LOG) xmlFree(configuration->error_log);
configuration->error_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "playlistlog") == 0) {
if (configuration->playlist_log && configuration->playlist_log != CONFIG_DEFAULT_PLAYLIST_LOG) xmlFree(configuration->playlist_log);
configuration->playlist_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "loglevel") == 0) {
char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
configuration->loglevel = atoi(tmp);

View File

@ -107,6 +107,7 @@ typedef struct ice_config_tag
int fileserve;
int on_demand; /* global setting for all relays */
char *shoutcast_mount;
char *source_password;
char *admin_username;
char *admin_password;
@ -143,6 +144,7 @@ typedef struct ice_config_tag
char *access_log;
char *error_log;
char *playlist_log;
int loglevel;
int chroot;

View File

@ -610,7 +610,7 @@ int connection_check_relay_pass(http_parser_t *parser)
return ret;
}
int connection_check_source_pass(http_parser_t *parser, char *mount)
int connection_check_source_pass(http_parser_t *parser, const char *mount)
{
ice_config_t *config = config_get_config();
char *pass = config->source_password;

View File

@ -52,7 +52,7 @@ int connection_complete_source (struct source_tag *source);
void connection_inject_event(int eventnum, void *event_data);
int connection_check_source_pass(http_parser_t *parser, char *mount);
int connection_check_source_pass(http_parser_t *parser, const char *mount);
int connection_check_relay_pass(http_parser_t *parser);
int connection_check_admin_pass(http_parser_t *parser);

View File

@ -64,6 +64,10 @@ format_type_t format_get_type(char *contenttype)
return FORMAT_TYPE_MP3;
else if(strcmp(contenttype, "video/nsv") == 0)
return FORMAT_TYPE_NSV;
else if(strcmp(contenttype, "audio/aac") == 0)
return FORMAT_TYPE_AAC;
else if(strcmp(contenttype, "audio/aacp") == 0)
return FORMAT_TYPE_AACPLUS;
else
return FORMAT_ERROR;
}
@ -80,6 +84,12 @@ const char *format_get_mimetype(format_type_t type)
case FORMAT_TYPE_NSV:
return "video/nsv";
break;
case FORMAT_TYPE_AAC:
return "audio/aac";
break;
case FORMAT_TYPE_AACPLUS:
return "audio/aacp";
break;
default:
return NULL;
}
@ -91,19 +101,29 @@ int format_get_plugin(format_type_t type, source_t *source)
switch (type)
{
case FORMAT_TYPE_OGG:
ret = format_ogg_get_plugin (source);
break;
case FORMAT_TYPE_MP3:
ret = format_mp3_get_plugin (source);
break;
case FORMAT_TYPE_NSV:
ret = format_mp3_get_plugin (source);
source->format->format_description = "NSV Video";
source->format->type = FORMAT_TYPE_NSV;
break;
default:
break;
case FORMAT_TYPE_OGG:
ret = format_ogg_get_plugin (source);
break;
case FORMAT_TYPE_MP3:
ret = format_mp3_get_plugin (source);
break;
case FORMAT_TYPE_NSV:
ret = format_mp3_get_plugin (source);
source->format->format_description = "NSV Video";
source->format->type = FORMAT_TYPE_NSV;
break;
case FORMAT_TYPE_AAC:
ret = format_mp3_get_plugin (source);
source->format->format_description = "AAC Audio";
source->format->type = FORMAT_TYPE_AAC;
break;
case FORMAT_TYPE_AACPLUS:
ret = format_mp3_get_plugin (source);
source->format->format_description = "AACPlus Audio";
source->format->type = FORMAT_TYPE_AACPLUS;
break;
default:
break;
}
stats_event (source->mount, "content-type",
format_get_mimetype(source->format->type));

View File

@ -31,7 +31,9 @@ typedef enum _format_type_tag
FORMAT_TYPE_OGG,
FORMAT_TYPE_VORBIS,
FORMAT_TYPE_MP3,
FORMAT_TYPE_NSV
FORMAT_TYPE_NSV,
FORMAT_TYPE_AAC,
FORMAT_TYPE_AACPLUS
} format_type_t;
typedef struct _format_plugin_tag

View File

@ -95,8 +95,11 @@ int format_mp3_get_plugin (source_t *source)
plugin->_state = state;
meta = refbuf_new (1);
memcpy (meta->data, "", 1);
/* initial metadata needs to be blank for sending to clients and for
comparing with new metadata */
meta = refbuf_new (2);
memcpy (meta->data, "\0\0", 2);
meta->len = 1;
state->metadata = meta;
state->interval = -1;
@ -531,8 +534,9 @@ static refbuf_t *mp3_get_filter_meta (source_t *source)
bytes -= metadata_remaining;
memmove (src, src+metadata_remaining, bytes);
/* assign metadata if it's not 1 byte, as that indicates a change */
if (source_mp3->build_metadata_len > 1)
/* assign metadata if it's greater than 1 byte, and the text has changed */
if (source_mp3->build_metadata_len > 1 &&
strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0)
{
refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
memcpy (meta->data, source_mp3->build_metadata,

View File

@ -691,21 +691,53 @@ static refbuf_t *ogg_get_buffer (source_t *source)
if (ogg_info->send_yp_info)
{
char *tag;
tag = ogg_info->title;
if (tag == NULL)
tag = "unknown";
stats_event (source->mount, "title", tag);
INFO1("Updating title \"%s\"", tag);
char *title;
char *artist;
char *metadata = NULL;
unsigned int len = 0;
title = ogg_info->title;
if (title)
INFO1("Updating title \"%s\"", title);
stats_event (source->mount, "title", title);
artist = ogg_info->artist;
if (artist)
INFO1("Updating artist \"%s\"", artist);
stats_event (source->mount, "artist", artist);
if (artist)
{
if (title)
{
len += strlen(artist) + strlen(title) + 3;
metadata = calloc (1, len);
snprintf (metadata, len, "%s - %s", artist, title);
}
else
{
len += strlen(artist);
metadata = calloc (1, len);
snprintf (metadata, len, "%s", artist);
}
}
else
{
if (title)
{
len += strlen (title);
metadata = calloc (1, len);
snprintf (metadata, len, "%s", title);
}
}
if (metadata)
{
logging_playlist (source->mount, metadata, source->listeners);
free (metadata);
}
tag = ogg_info->artist;
if (tag == NULL)
tag = "unknown";
stats_event (source->mount, "artist", tag);
if (ogg_info->bitrate)
stats_event_args (source->mount, "ice-bitrate", "%u", ogg_info->bitrate/1000);
INFO1("Updating artist \"%s\"", tag);
ogg_info->send_yp_info = 0;
yp_touch (source->mount);
}

View File

@ -410,23 +410,56 @@ static int process_vorbis_headers (source_t *source)
static void update_stats (source_t *source, vorbis_comment *vc)
{
char *tag;
/* put known comments in the stats, this could be site specific */
tag = vorbis_comment_query (vc, "TITLE", 0);
if (tag == NULL)
tag = "unknown";
else
INFO1 ("title set to \"%s\"", tag);
stats_event (source->mount, "title", tag);
char *artist;
char *title;
char *metadata = NULL;
unsigned int len = 1;
tag = vorbis_comment_query (vc, "ARTIST", 0);
if (tag)
/* put known comments in the stats, this could be site specific */
title = vorbis_comment_query (vc, "TITLE", 0);
if (title)
{
INFO1 ("artist set to \"%s\"", tag);
stats_event (source->mount, "artist", tag);
INFO1 ("title set to \"%s\"", title);
len += strlen (title);
}
stats_event (source->mount, "title", title);
artist = vorbis_comment_query (vc, "ARTIST", 0);
if (artist)
{
INFO1 ("artist set to \"%s\"", artist);
len += strlen (artist);
}
stats_event (source->mount, "artist", artist);
if (artist)
{
if (title)
{
len += strlen(artist) + strlen(title) + 3;
metadata = calloc (1, len);
snprintf (metadata, len, "%s - %s", artist, title);
}
else
{
len += strlen(artist);
metadata = calloc (1, len);
snprintf (metadata, len, "%s", artist);
}
}
else
stats_event (source->mount, "artist", NULL);
{
if (title)
{
len += strlen (title);
metadata = calloc (1, len);
snprintf (metadata, len, "%s", title);
}
}
if (metadata)
{
logging_playlist (source->mount, metadata, source->listeners);
free (metadata);
}
}

View File

@ -466,7 +466,7 @@ int fserve_client_create(client_t *httpclient, char *path)
bytes = sock_write(httpclient->con->sock,
"HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n");
if(bytes > 0) httpclient->con->sent_bytes = bytes;
fserve_client_destroy (httpclient);
fserve_client_destroy (client);
return -1;
}

View File

@ -37,6 +37,7 @@
/* the global log descriptors */
int errorlog = 0;
int accesslog = 0;
int playlistlog = 0;
#ifdef _WIN32
/* Since strftime's %z option on win32 is different, we need
@ -156,6 +157,39 @@ void logging_access(client_t *client)
}
/* This function will provide a log of metadata for each
mountpoint. The metadata *must* be in UTF-8, and thus
you can assume that the log itself is UTF-8 encoded */
void logging_playlist(char *mount, char *metadata, long listeners)
{
char datebuf[128];
struct tm thetime;
time_t now;
if (playlistlog == -1) {
return;
}
now = time(NULL);
localtime_r (&now, &thetime);
/* build the data */
#ifdef _WIN32
memset(datebuf, '\000', sizeof(datebuf));
get_clf_time(datebuf, sizeof(datebuf)-1, &thetime);
#else
strftime (datebuf, sizeof(datebuf), LOGGING_FORMAT_CLF, &thetime);
#endif
/* This format MAY CHANGE OVER TIME. We are looking into finding a good
standard format for this, if you have any ideas, please let us know */
log_write_direct (playlistlog, "%s|%s|%d|%s",
datebuf,
mount,
listeners,
metadata);
}
void restart_logging (ice_config_t *config)
{
@ -175,4 +209,12 @@ void restart_logging (ice_config_t *config)
log_set_filename (accesslog, fn_error);
log_reopen (accesslog);
}
if (config->playlist_log)
{
char fn_error[FILENAME_MAX];
snprintf (fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->playlist_log);
log_set_filename (playlistlog, fn_error);
log_reopen (playlistlog);
}
}

View File

@ -20,6 +20,7 @@
extern int errorlog;
extern int accesslog;
extern int playlistlog;
/* these are all ERRORx and WARNx where _x_ is the number of parameters
** it takes. it turns out most other copmilers don't have support for
@ -88,6 +89,7 @@ extern int accesslog;
#define LOGGING_FORMAT_CLF "%d/%b/%Y:%H:%M:%S %z"
void logging_access(client_t *client);
void logging_playlist(char *mount, char *metadata, long listeners);
void restart_logging (ice_config_t *config);
#endif /* __LOGGING_H__ */

View File

@ -180,6 +180,7 @@ static int _start_logging(void)
{
char fn_error[FILENAME_MAX];
char fn_access[FILENAME_MAX];
char fn_playlist[FILENAME_MAX];
char buf[1024];
int log_to_stderr;
@ -202,6 +203,7 @@ static int _start_logging(void)
strerror(errno));
_fatal_error(buf);
}
log_set_level(errorlog, config->loglevel);
if(strcmp(config->access_log, "-")) {
@ -222,8 +224,25 @@ static int _start_logging(void)
_fatal_error(buf);
}
if(config->playlist_log) {
snprintf(fn_playlist, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->playlist_log);
playlistlog = log_open(fn_playlist);
if (playlistlog < 0) {
buf[sizeof(buf)-1] = 0;
snprintf(buf, sizeof(buf)-1,
"FATAL: could not open playlist logging (%s): %s",
log_to_stderr?"standard error":fn_playlist,
strerror(errno));
_fatal_error(buf);
}
log_to_stderr = 0;
} else {
playlistlog = -1;
}
log_set_level(errorlog, config->loglevel);
log_set_level(accesslog, 4);
log_set_level(playlistlog, 4);
if (errorlog >= 0 && accesslog >= 0) return 1;

View File

@ -498,22 +498,12 @@ static ypdata_t *create_yp_entry (source_t *source)
if (url == NULL)
break;
config = config_get_config();
if (source->format->type == FORMAT_TYPE_NSV) {
ret = snprintf (url, len, "http://%s:%d%s?stream.nsv", config->hostname, config->port, source->mount);
}
else {
ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, source->mount);
}
ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, source->mount);
if (ret >= (signed)len)
{
s = realloc (url, ++ret);
if (s) url = s;
if (source->format->type == FORMAT_TYPE_NSV) {
snprintf (url, ret, "http://%s:%d%s?file=stream.nsv", config->hostname, config->port, source->mount);
}
else {
snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, source->mount);
}
snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, source->mount);
}
config_release_config();
yp->listen_url = util_url_escape (url);

View File

@ -54,14 +54,7 @@
<a href="auth.xsl">Click to Listen</a>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="content-type='video/nsv'">
<a href="{@mount}%3Ffile%3Dstream.nsv.m3u">Click to Listen</a>
</xsl:when>
<xsl:otherwise>
<a href="{@mount}.m3u">Click to Listen</a>
</xsl:otherwise>
</xsl:choose>
<a href="{@mount}.m3u">Click to Listen</a>
</xsl:otherwise>
</xsl:choose>
</td></tr>