Commit 8ce3dbb9 authored by Michael Smith's avatar Michael Smith

File serving, from the webroot.

svn path=/trunk/icecast/; revision=3852
parent 2aa432dc
......@@ -31,11 +31,13 @@
<!--<master-update-interval>120</master-update-interval>-->
<!--<master-password>hackme</master-password>-->
<paths>
<basedir>/usr/local/icecast</basedir>
<logdir>/tmp</logdir>
<webroot>/usr/local/icecast/web</webroot>
</paths>
<fileserve>1</fileserve>
<paths>
<basedir>/usr/local/icecast</basedir>
<logdir>/tmp</logdir>
<webroot>/usr/local/icecast/web</webroot>
</paths>
<logging>
<accesslog>access.log</accesslog>
......
......@@ -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
......
......@@ -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);
......
......@@ -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;
......
......@@ -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) {
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#else
#include <winsock2.h>
#include <windows.h>
#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;
}
#ifndef __FSERVE_H__
#define __FSERVE_H__
#include <stdio.h>
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
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment