diff --git a/doc/icecast2_config_file.html b/doc/icecast2_config_file.html index a62bf82e..909d2f66 100644 --- a/doc/icecast2_config_file.html +++ b/doc/icecast2_config_file.html @@ -434,6 +434,17 @@ mountpoint that is just not available, then those clients will be disconnected. If clients are falling back to a mountpoint and the fallback-mount is not actively streaming but defines a fallback-mount itself then those clients may be moved there instead. This multi-level fallback allows clients to cascade several mountpoints. +

A fallback mount can also state a file that is located in webroot. This is useful for + playing a pre-recorded file in the case of a stream going down. It will repeat until either + the listener disconnects or a stream comes back available and takes the listeners back. + As per usual, the file format should match the stream format, failing to do so may cause + problems with playback. +

+

Note that the fallback file is not timed so be careful if you intend to relay this. + They are fine on slave streams but don't use them on master streams, if you do then the + relay will consume stream data at a faster rate and the listeners on the relay would + eventually get kicked off. +

fallback-override

diff --git a/src/client.c b/src/client.c index b86d0de0..9d417079 100644 --- a/src/client.c +++ b/src/client.c @@ -75,10 +75,11 @@ void client_destroy(client_t *client) /* write log entry if ip is set (some things don't set it, like outgoing * slave requests */ - if(client->con->ip) + if (client->con && client->con->ip) logging_access(client); - connection_close(client->con); + if (client->con) + connection_close(client->con); httpp_destroy(client->parser); global_lock (); diff --git a/src/format.c b/src/format.c index 6677ab75..d41dfcc9 100644 --- a/src/format.c +++ b/src/format.c @@ -150,8 +150,16 @@ int format_check_file_buffer (source_t *source, client_t *client) if (refbuf == NULL) { /* client refers to no data, must be from a move */ - find_client_start (source, client); - return -1; + if (source->client->con) + { + find_client_start (source, client); + return -1; + } + /* source -> file fallback, need a refbuf for data */ + refbuf = refbuf_new (4096); + client->refbuf = refbuf; + client->pos = refbuf->len; + client->intro_offset = 0; } if (client->pos == refbuf->len) { diff --git a/src/fserve.c b/src/fserve.c index 1d2d01f4..ebb84b26 100644 --- a/src/fserve.c +++ b/src/fserve.c @@ -317,7 +317,7 @@ static void *fserv_thread_function(void *arg) return NULL; } -static const char *fserve_content_type(const char *path) +char *fserve_content_type (const char *path) { char *ext = util_get_extension(path); mime_type exttype = {ext, NULL}; diff --git a/src/fserve.h b/src/fserve.h index 573d02bb..e29fc4df 100644 --- a/src/fserve.h +++ b/src/fserve.h @@ -32,6 +32,7 @@ typedef struct _fserve_t void fserve_initialize(void); void fserve_shutdown(void); int fserve_client_create(client_t *httpclient, const char *path); +char *fserve_content_type (const char *path); #endif diff --git a/src/source.c b/src/source.c index 673f818d..8ebbfb15 100644 --- a/src/source.c +++ b/src/source.c @@ -48,6 +48,7 @@ #include "util.h" #include "source.h" #include "format.h" +#include "fserve.h" #include "auth.h" #include "os.h" @@ -373,6 +374,8 @@ void source_move_clients (source_t *source, source_t *dest) { client_set_queue (client, NULL); client->check_buffer = format_check_file_buffer; + if (source->con == NULL) + client->intro_offset = -1; } avl_insert (dest->pending_tree, (void *)client); @@ -397,6 +400,8 @@ void source_move_clients (source_t *source, source_t *dest) { client_set_queue (client, NULL); client->check_buffer = format_check_file_buffer; + if (source->con == NULL) + client->intro_offset = -1; } avl_insert (dest->pending_tree, (void *)client); count++; @@ -435,10 +440,16 @@ static refbuf_t *get_next_buffer (source_t *source) delay = 0; while (global.running == ICE_RUNNING && source->running) { - int fds; + int fds = 0; time_t current = time (NULL); - fds = util_timed_wait_for_fd (source->con->sock, delay); + if (source->client->con) + fds = util_timed_wait_for_fd (source->con->sock, delay); + else + { + thread_sleep (delay*1000); + source->last_read = current; + } if (current >= source->client_stats_update) { @@ -470,7 +481,7 @@ static refbuf_t *get_next_buffer (source_t *source) } source->last_read = current; refbuf = source->format->get_buffer (source); - if (source->client->con->error) + if (source->client->con && source->client->con->error) { INFO1 ("End of Stream %s", source->mount); source->running = 0; @@ -585,7 +596,8 @@ static void source_init (source_t *source) stats_event_inc (NULL, "source_total_connections"); stats_event (source->mount, "slow_listeners", "0"); - sock_set_blocking (source->con->sock, SOCK_NONBLOCK); + if (source->client->con) + sock_set_blocking (source->con->sock, SOCK_NONBLOCK); DEBUG0("Source creation complete"); source->last_read = time (NULL); @@ -1095,6 +1107,9 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo) */ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo) { + /* skip if source is a fallback to file */ + if (source->running && source->client->con == NULL) + return; /* set global settings first */ source->queue_size_limit = config->queue_size_limit; source->timeout = config->source_timeout; @@ -1147,26 +1162,29 @@ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy void *source_client_thread (void *arg) { source_t *source = arg; - const char ok_msg[] = "HTTP/1.0 200 OK\r\n\r\n"; - int bytes; - const char *agent; - source->client->respcode = 200; - bytes = sock_write_bytes (source->client->con->sock, ok_msg, sizeof (ok_msg)-1); - if (bytes < (int)(sizeof (ok_msg)-1)) + if (source->client && source->client->con) { - global_lock(); - global.sources--; - global_unlock(); - WARN0 ("Error writing 200 OK message to source client"); - source_free_source (source); - return NULL; - } - stats_event (source->mount, "source_ip", source->client->con->ip); - agent = httpp_getvar (source->client->parser, "user-agent"); - if (agent) - stats_event (source->mount, "user_agent", agent); + const char ok_msg[] = "HTTP/1.0 200 OK\r\n\r\n"; + int bytes; + const char *agent; + source->client->respcode = 200; + bytes = sock_write_bytes (source->client->con->sock, ok_msg, sizeof (ok_msg)-1); + if (bytes < (int)(sizeof (ok_msg)-1)) + { + global_lock(); + global.sources--; + global_unlock(); + WARN0 ("Error writing 200 OK message to source client"); + source_free_source (source); + return NULL; + } + stats_event (source->mount, "source_ip", source->client->con->ip); + agent = httpp_getvar (source->client->parser, "user-agent"); + if (agent) + stats_event (source->mount, "user_agent", agent); + } stats_event_inc(NULL, "source_client_connections"); stats_event (source->mount, "listeners", "0"); @@ -1214,6 +1232,67 @@ static void source_run_script (char *command, char *mountpoint) #endif +static void *source_fallback_file (void *arg) +{ + char *mount = arg; + char *type; + char *path; + unsigned int len; + FILE *file = NULL; + source_t *source = NULL; + ice_config_t *config; + http_parser_t *parser; + + do + { + if (mount == NULL || mount[0] != '/') + break; + config = config_get_config(); + len = strlen (config->webroot_dir) + strlen (mount) + 1; + path = malloc (len); + if (path) + snprintf (path, len, "%s%s", config->webroot_dir, mount); + + config_release_config (); + if (path == NULL) + break; + + file = fopen (path, "rb"); + if (file == NULL) + { + DEBUG1 ("unable to open file \"%s\"", path); + free (path); + break; + } + free (path); + source = source_reserve (mount); + if (source == NULL) + { + DEBUG1 ("mountpoint \"%s\" already reserved", mount); + break; + } + type = fserve_content_type (mount); + parser = httpp_create_parser(); + httpp_initialize (parser, NULL); + httpp_setvar (parser, "content-type", type); + + source->hidden = 1; + source->yp_public = 0; + source->intro_file = file; + source->parser = parser; + file = NULL; + + if (connection_complete_source (source) < 0) + break; + source_client_thread (source); + } while (0); + if (file) + fclose (file); + free (mount); + return NULL; +} + + /* rescan the mount list, so that xsl files are updated to show * unconnected but active fallback mountpoints */ @@ -1249,6 +1328,17 @@ void source_recheck_mounts (void) else stats_event (mount->mountname, NULL, NULL); + /* check for fallback to file */ + if (global.running == ICE_RUNNING && mount->fallback_mount) + { + source_t *fallback = source_find_mount (mount->fallback_mount); + if (fallback == NULL) + { + thread_create ("Fallback file thread", source_fallback_file, + strdup (mount->fallback_mount), THREAD_DETACHED); + } + } + mount = mount->next; } avl_tree_unlock (global.source_tree);