mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
File serving, from the webroot.
svn path=/trunk/icecast/; revision=3852
This commit is contained in:
parent
2aa432dc0f
commit
8ce3dbb957
@ -31,11 +31,13 @@
|
|||||||
<!--<master-update-interval>120</master-update-interval>-->
|
<!--<master-update-interval>120</master-update-interval>-->
|
||||||
<!--<master-password>hackme</master-password>-->
|
<!--<master-password>hackme</master-password>-->
|
||||||
|
|
||||||
<paths>
|
<fileserve>1</fileserve>
|
||||||
<basedir>/usr/local/icecast</basedir>
|
|
||||||
<logdir>/tmp</logdir>
|
<paths>
|
||||||
<webroot>/usr/local/icecast/web</webroot>
|
<basedir>/usr/local/icecast</basedir>
|
||||||
</paths>
|
<logdir>/tmp</logdir>
|
||||||
|
<webroot>/usr/local/icecast/web</webroot>
|
||||||
|
</paths>
|
||||||
|
|
||||||
<logging>
|
<logging>
|
||||||
<accesslog>access.log</accesslog>
|
<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\
|
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\
|
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\
|
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\
|
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\
|
icecast_LDADD = net/libicenet.la thread/libicethread.la httpp/libicehttpp.la\
|
||||||
log/libicelog.la avl/libiceavl.la timing/libicetiming.la
|
log/libicelog.la avl/libiceavl.la timing/libicetiming.la
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
|
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
|
||||||
#define CONFIG_DEFAULT_RELAY_PASSWORD "changeme"
|
#define CONFIG_DEFAULT_RELAY_PASSWORD "changeme"
|
||||||
#define CONFIG_DEFAULT_ICE_LOGIN 0
|
#define CONFIG_DEFAULT_ICE_LOGIN 0
|
||||||
|
#define CONFIG_DEFAULT_FILESERVE 1
|
||||||
#define CONFIG_DEFAULT_TOUCH_FREQ 5
|
#define CONFIG_DEFAULT_TOUCH_FREQ 5
|
||||||
#define CONFIG_DEFAULT_HOSTNAME "localhost"
|
#define CONFIG_DEFAULT_HOSTNAME "localhost"
|
||||||
#define CONFIG_DEFAULT_PORT 8888
|
#define CONFIG_DEFAULT_PORT 8888
|
||||||
@ -162,6 +163,7 @@ static void _set_defaults(void)
|
|||||||
_configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
|
_configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
|
||||||
_configuration.relay_password = CONFIG_DEFAULT_RELAY_PASSWORD;
|
_configuration.relay_password = CONFIG_DEFAULT_RELAY_PASSWORD;
|
||||||
_configuration.ice_login = CONFIG_DEFAULT_ICE_LOGIN;
|
_configuration.ice_login = CONFIG_DEFAULT_ICE_LOGIN;
|
||||||
|
_configuration.fileserve = CONFIG_DEFAULT_FILESERVE;
|
||||||
_configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ;
|
_configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ;
|
||||||
_configuration.dir_list = NULL;
|
_configuration.dir_list = NULL;
|
||||||
_configuration.hostname = CONFIG_DEFAULT_HOSTNAME;
|
_configuration.hostname = CONFIG_DEFAULT_HOSTNAME;
|
||||||
@ -214,6 +216,10 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node)
|
|||||||
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||||
_configuration.ice_login = atoi(tmp);
|
_configuration.ice_login = atoi(tmp);
|
||||||
if (tmp) xmlFree(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) {
|
} else if (strcmp(node->name, "hostname") == 0) {
|
||||||
if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname);
|
if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname);
|
||||||
_configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
_configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||||
|
@ -25,6 +25,7 @@ typedef struct ice_config_tag
|
|||||||
int header_timeout;
|
int header_timeout;
|
||||||
int source_timeout;
|
int source_timeout;
|
||||||
int ice_login;
|
int ice_login;
|
||||||
|
int fileserve;
|
||||||
|
|
||||||
char *source_password;
|
char *source_password;
|
||||||
char *relay_password;
|
char *relay_password;
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "xslt.h"
|
#include "xslt.h"
|
||||||
|
#include "fserve.h"
|
||||||
|
|
||||||
#include "source.h"
|
#include "source.h"
|
||||||
|
|
||||||
@ -530,6 +531,18 @@ static void _handle_get_request(connection_t *con,
|
|||||||
free(fullpath);
|
free(fullpath);
|
||||||
return;
|
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);
|
free(fullpath);
|
||||||
|
|
||||||
if(strcmp(util_get_extension(uri), "m3u") == 0) {
|
if(strcmp(util_get_extension(uri), "m3u") == 0) {
|
||||||
|
272
src/fserve.c
Normal file
272
src/fserve.c
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
24
src/fserve.h
Normal file
24
src/fserve.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#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 "stats.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "xslt.h"
|
#include "xslt.h"
|
||||||
|
#include "fserve.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
@ -53,10 +54,13 @@ static void _initialize_subsystems(void)
|
|||||||
global_initialize();
|
global_initialize();
|
||||||
refbuf_initialize();
|
refbuf_initialize();
|
||||||
xslt_initialize();
|
xslt_initialize();
|
||||||
|
DEBUG0("Calling fserve_initialize()");
|
||||||
|
fserve_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _shutdown_subsystems(void)
|
static void _shutdown_subsystems(void)
|
||||||
{
|
{
|
||||||
|
fserve_shutdown();
|
||||||
xslt_shutdown();
|
xslt_shutdown();
|
||||||
refbuf_shutdown();
|
refbuf_shutdown();
|
||||||
stats_shutdown();
|
stats_shutdown();
|
||||||
|
Loading…
Reference in New Issue
Block a user