From 8ce3dbb95718838d5f22b5c0c54d41fa5eb5976e Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sun, 18 Aug 2002 05:06:58 +0000 Subject: [PATCH] File serving, from the webroot. svn path=/trunk/icecast/; revision=3852 --- conf/icecast.xml | 12 ++- src/Makefile.am | 4 +- src/config.c | 6 ++ src/config.h | 1 + src/connection.c | 13 +++ src/fserve.c | 272 +++++++++++++++++++++++++++++++++++++++++++++++ src/fserve.h | 24 +++++ src/main.c | 4 + 8 files changed, 329 insertions(+), 7 deletions(-) create mode 100644 src/fserve.c create mode 100644 src/fserve.h diff --git a/conf/icecast.xml b/conf/icecast.xml index 4ac4e776..d343ddff 100644 --- a/conf/icecast.xml +++ b/conf/icecast.xml @@ -31,11 +31,13 @@ - - /usr/local/icecast - /tmp - /usr/local/icecast/web - + 1 + + + /usr/local/icecast + /tmp + /usr/local/icecast/web + access.log diff --git a/src/Makefile.am b/src/Makefile.am index 29a64718..f2b905e9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,10 +8,10 @@ bin_PROGRAMS = icecast noinst_HEADERS = config.h os.h logging.h sighandler.h connection.h global.h\ util.h slave.h source.h stats.h refbuf.h client.h format.h format_vorbis.h\ - compat.h format_mp3.h + compat.h format_mp3.h fserve.h icecast_SOURCES = config.c main.c logging.c sighandler.c connection.c global.c\ util.c slave.c source.c stats.c refbuf.c client.c format.c format_vorbis.c\ - format_mp3.c xslt.c + format_mp3.c xslt.c fserve.c icecast_LDADD = net/libicenet.la thread/libicethread.la httpp/libicehttpp.la\ log/libicelog.la avl/libiceavl.la timing/libicetiming.la diff --git a/src/config.c b/src/config.c index 6f37f7c4..2f1a7b75 100644 --- a/src/config.c +++ b/src/config.c @@ -16,6 +16,7 @@ #define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme" #define CONFIG_DEFAULT_RELAY_PASSWORD "changeme" #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_PORT 8888 @@ -162,6 +163,7 @@ static void _set_defaults(void) _configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD; _configuration.relay_password = CONFIG_DEFAULT_RELAY_PASSWORD; _configuration.ice_login = CONFIG_DEFAULT_ICE_LOGIN; + _configuration.fileserve = CONFIG_DEFAULT_FILESERVE; _configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ; _configuration.dir_list = NULL; _configuration.hostname = CONFIG_DEFAULT_HOSTNAME; @@ -214,6 +216,10 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node) tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); _configuration.ice_login = atoi(tmp); if (tmp) xmlFree(tmp); + } else if (strcmp(node->name, "fileserve") == 0) { + tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + _configuration.fileserve = atoi(tmp); + if (tmp) xmlFree(tmp); } else if (strcmp(node->name, "hostname") == 0) { if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname); _configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); diff --git a/src/config.h b/src/config.h index d407e421..5a0fe590 100644 --- a/src/config.h +++ b/src/config.h @@ -25,6 +25,7 @@ typedef struct ice_config_tag int header_timeout; int source_timeout; int ice_login; + int fileserve; char *source_password; char *relay_password; diff --git a/src/connection.c b/src/connection.c index 8629a57f..63d38b57 100644 --- a/src/connection.c +++ b/src/connection.c @@ -34,6 +34,7 @@ #include "format.h" #include "logging.h" #include "xslt.h" +#include "fserve.h" #include "source.h" @@ -530,6 +531,18 @@ static void _handle_get_request(connection_t *con, free(fullpath); return; } + else if(config_get_config()->fileserve && + stat(fullpath, &statbuf) == 0) { + client->respcode = 200; + bytes = sock_write(client->con->sock, + "HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", + fserve_content_type(fullpath)); + if(bytes > 0) client->con->sent_bytes = bytes; + if(fserve_client_create(client, fullpath) < 0) + client_destroy(client); + free(fullpath); + return; + } free(fullpath); if(strcmp(util_get_extension(uri), "m3u") == 0) { diff --git a/src/fserve.c b/src/fserve.c new file mode 100644 index 00000000..c7a06a76 --- /dev/null +++ b/src/fserve.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#else +#include +#include +#endif + +#include "thread.h" +#include "avl.h" +#include "httpp.h" +#include "sock.h" + +#include "connection.h" +#include "global.h" +#include "refbuf.h" +#include "client.h" +#include "stats.h" +#include "format.h" +#include "log.h" +#include "logging.h" +#include "config.h" +#include "util.h" + +#include "fserve.h" + +#undef CATMODULE +#define CATMODULE "fserve" + +#define BUFSIZE 4096 + +static avl_tree *client_tree; +static avl_tree *pending_tree; + +static cond_t fserv_cond; +static thread_t *fserv_thread; +static int run_fserv; + +/* avl tree helper */ +static int _compare_clients(void *compare_arg, void *a, void *b); +static int _remove_client(void *key); +static int _free_client(void *key); +void *fserv_thread_function(void *arg); + +void fserve_initialize(void) +{ + if(!config_get_config()->fileserve) + return; + + client_tree = avl_tree_new(_compare_clients, NULL); + pending_tree = avl_tree_new(_compare_clients, NULL); + thread_cond_create(&fserv_cond); + + run_fserv = 1; + + fserv_thread = thread_create("File Serving Thread", + fserv_thread_function, NULL, THREAD_ATTACHED); +} + +void fserve_shutdown(void) +{ + if(!config_get_config()->fileserve) + return; + + run_fserv = 0; + thread_cond_signal(&fserv_cond); + thread_join(fserv_thread); + + thread_cond_destroy(&fserv_cond); + avl_tree_free(client_tree, _free_client); + avl_tree_free(pending_tree, _free_client); +} + +void *fserv_thread_function(void *arg) +{ + avl_node *client_node, *pending_node; + fserve_t *client; + int sbytes, bytes; + + while (run_fserv) { + avl_tree_rlock(client_tree); + + client_node = avl_get_first(client_tree); + if(!client_node) { + pending_node = avl_get_first(pending_tree); + if(!pending_node) { + /* There are no current clients. Wait until there are... */ + avl_tree_unlock(client_tree); + thread_cond_wait(&fserv_cond); + continue; + } + } + + while(client_node) { + avl_node_wlock(client_node); + + client = (fserve_t *)client_node->key; + + if(client->offset >= client->datasize) { + /* Grab a new chunk */ + bytes = fread(client->buf, 1, BUFSIZE, client->file); + if(bytes <= 0) { + client->client->con->error = 1; + avl_node_unlock(client_node); + client_node = avl_get_next(client_node); + continue; + } + client->offset = 0; + client->datasize = bytes; + } + + /* Now try and send current chunk. */ + sbytes = sock_write_bytes(client->client->con->sock, + &client->buf[client->offset], + client->datasize - client->offset); + + // TODO: remove clients if they take too long. + if(sbytes >= 0) { + client->offset += sbytes; + client->client->con->sent_bytes += sbytes; + } + else if(!sock_recoverable(sock_error())) { + DEBUG0("Fileserving client had fatal error, disconnecting"); + client->client->con->error = 1; + } + else + DEBUG0("Fileserving client had recoverable error"); + + avl_node_unlock(client_node); + client_node = avl_get_next(client_node); + } + + avl_tree_unlock(client_tree); + + /* Now we need a write lock instead, to delete done clients. */ + avl_tree_wlock(client_tree); + + client_node = avl_get_first(client_tree); + while(client_node) { + client = (fserve_t *)client_node->key; + if(client->client->con->error) { + client_node = avl_get_next(client_node); + avl_delete(client_tree, (void *)client, _free_client); + continue; + } + client_node = avl_get_next(client_node); + } + + avl_tree_wlock(pending_tree); + + /* And now insert new clients. */ + client_node = avl_get_first(pending_tree); + while(client_node) { + client = (fserve_t *)client_node->key; + avl_insert(client_tree, client); + client_node = avl_get_next(client_node); + + } + + /* clear pending */ + while(avl_get_first(pending_tree)) { + avl_delete(pending_tree, avl_get_first(pending_tree)->key, + _remove_client); + } + + avl_tree_unlock(pending_tree); + avl_tree_unlock(client_tree); + } + + /* Shutdown path */ + + avl_tree_wlock(pending_tree); + while(avl_get_first(pending_tree)) + avl_delete(pending_tree, avl_get_first(pending_tree)->key, + _free_client); + avl_tree_unlock(pending_tree); + + avl_tree_wlock(client_tree); + while(avl_get_first(client_tree)) + avl_delete(client_tree, avl_get_first(client_tree)->key, + _free_client); + avl_tree_unlock(client_tree); + + thread_exit(0); + return NULL; +} + +char *fserve_content_type(char *path) +{ + char *ext = util_get_extension(path); + + if(!strcmp(ext, "ogg")) + return "application/x-ogg"; + else if(!strcmp(ext, "mp3")) + return "audio/mpeg"; + else if(!strcmp(ext, "html")) + return "text/html"; + else if(!strcmp(ext, "txt")) + return "text/plain"; + else + return "application/octet-stream"; + /* TODO Add more types */ +} + +static void fserve_client_destroy(fserve_t *client) +{ + if(client) { + if(client->buf) + free(client->buf); + if(client->file) + fclose(client->file); + + if(client->client) + client_destroy(client->client); + free(client); + } +} + +int fserve_client_create(client_t *httpclient, char *path) +{ + fserve_t *client = calloc(1, sizeof(fserve_t)); + + client->client = httpclient; + client->file = fopen(path, "rb"); + if(!client->file) { + fserve_client_destroy(client); + return -1; + } + client->offset = 0; + client->datasize = 0; + client->buf = malloc(BUFSIZE); + + avl_tree_wlock(pending_tree); + avl_insert(pending_tree, client); + avl_tree_unlock(pending_tree); + + thread_cond_signal(&fserv_cond); + + return 0; +} + +static int _compare_clients(void *compare_arg, void *a, void *b) +{ + connection_t *cona = (connection_t *)a; + connection_t *conb = (connection_t *)b; + + if (cona->id < conb->id) return -1; + if (cona->id > conb->id) return 1; + + return 0; +} + +static int _remove_client(void *key) +{ + return 1; +} + +static int _free_client(void *key) +{ + fserve_t *client = (fserve_t *)key; + + fserve_client_destroy(client); + + return 1; +} + diff --git a/src/fserve.h b/src/fserve.h new file mode 100644 index 00000000..229e7954 --- /dev/null +++ b/src/fserve.h @@ -0,0 +1,24 @@ +#ifndef __FSERVE_H__ +#define __FSERVE_H__ + +#include + +typedef struct +{ + client_t *client; + + FILE *file; + int offset; + int datasize; + unsigned char *buf; +} fserve_t; + +void fserve_initialize(void); +void fserve_shutdown(void); +char *fserve_content_type(char *path); +int fserve_client_create(client_t *httpclient, char *path); + + +#endif + + diff --git a/src/main.c b/src/main.c index 3bca42c1..c0e01ab3 100644 --- a/src/main.c +++ b/src/main.c @@ -27,6 +27,7 @@ #include "stats.h" #include "logging.h" #include "xslt.h" +#include "fserve.h" #ifdef _WIN32 #define snprintf _snprintf @@ -53,10 +54,13 @@ static void _initialize_subsystems(void) global_initialize(); refbuf_initialize(); xslt_initialize(); + DEBUG0("Calling fserve_initialize()"); + fserve_initialize(); } static void _shutdown_subsystems(void) { + fserve_shutdown(); xslt_shutdown(); refbuf_shutdown(); stats_shutdown();