Commit d13ebde7 authored by Michael Smith's avatar Michael Smith

Allow rereading config files.

Lots of new locking happening so that it's safe to have the config file
disappear under the rest of the program

Does NOT affect currently-running sources at the moment

svn path=/trunk/icecast/; revision=4406
parent 4c08a155
(Note: from here on, the changelog generally only includes new features, not
bug fixes)
2003-03-05
Implemented the ability to reread the config file on SIGHUP. For now, this
does not affect configuration for currently running sources (only new
sources and global parameters like max-listeners)
2003-03-02
More features:
......
......@@ -50,6 +50,8 @@ FEATURES
- option to use ipv6 (equiv to using <bind-address>::</bindaddress>, I think.
- per-mountpoint listener maximums.
- abstract all admin functionality to a set of commands, and command handlers.
Make /admin/* just parse according to a set of rules, and dispatch generic
commands through that.
Use this for alternative admin interfaces (GUI? telnet interface?)
......@@ -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 fserve.h xslt.h geturl.h yp.h
compat.h format_mp3.h fserve.h xslt.h geturl.h yp.h event.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 fserve.c geturl.c yp.c
format_mp3.c xslt.c fserve.c geturl.c yp.c event.c
icecast_LDADD = net/libicenet.la thread/libicethread.la httpp/libicehttpp.la\
log/libicelog.la avl/libiceavl.la timing/libicetiming.la
......
This diff is collapsed.
......@@ -8,6 +8,8 @@
#define MAX_YP_DIRECTORIES 25
#include "thread/thread.h"
typedef struct ice_config_dir_tag
{
char *host;
......@@ -40,6 +42,8 @@ typedef struct _mount_proxy {
typedef struct ice_config_tag
{
char *config_filename;
char *location;
char *admin;
......@@ -90,15 +94,30 @@ typedef struct ice_config_tag
int num_yp_directories;
} ice_config_t;
typedef struct {
mutex_t config_lock;
mutex_t relay_lock;
mutex_t mounts_lock;
} ice_config_locks;
void config_initialize(void);
void config_shutdown(void);
int config_parse_file(const char *filename);
int config_parse_file(const char *filename, ice_config_t *configuration);
int config_initial_parse_file(const char *filename);
int config_parse_cmdline(int arg, char **argv);
void config_set_config(ice_config_t *config);
void config_clear(ice_config_t *config);
int config_rehash(void);
ice_config_locks *config_locks(void);
ice_config_t *config_get_config(void);
void config_release_config(void);
/* To be used ONLY in one-time startup code */
ice_config_t *config_get_config_unlocked(void);
#endif /* __CONFIG_H__ */
......
......@@ -11,7 +11,7 @@ int main(void)
config_parse_file("icecast.xml");
config = config_get_config();
config = config_get_config_unlocked();
_dump_config(config);
......
......@@ -40,6 +40,7 @@
#include "geturl.h"
#include "format.h"
#include "format_mp3.h"
#include "event.h"
#define CATMODULE "connection"
......@@ -112,6 +113,10 @@ connection_t *create_connection(sock_t sock, char *ip) {
con->con_time = time(NULL);
con->id = _next_connection_id();
con->ip = ip;
con->event_number = EVENT_NO_EVENT;
con->event = NULL;
return con;
}
......@@ -209,10 +214,13 @@ static void _build_pool(void)
int i;
thread_type *tid;
char buff[64];
int threadpool_size;
config = config_get_config();
threadpool_size = config->threadpool_size;
config_release_config();
for (i = 0; i < config->threadpool_size; i++) {
for (i = 0; i < threadpool_size; i++) {
snprintf(buff, 64, "Connection Thread #%d", i);
tid = thread_create(buff, _handle_connection, NULL, THREAD_ATTACHED);
_push_thread(&_conhands, tid);
......@@ -290,6 +298,16 @@ static connection_t *_get_connection(void)
return con;
}
void connection_inject_event(int eventnum, void *event_data) {
connection_t *con = calloc(1, sizeof(connection_t));
con->event_number = eventnum;
con->event = event_data;
_add_connection(con);
_signal_pool();
}
/* TODO: Make this return an appropriate error code so that we can use HTTP
* codes where appropriate
*/
......@@ -297,12 +315,18 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t
source_t *source;
char *contenttype;
mount_proxy *mountproxy, *mountinfo = NULL;
int source_limit;
ice_config_t *config;
config = config_get_config();
source_limit = config->source_limit;
config_release_config();
/* check to make sure this source wouldn't
** be over the limit
*/
global_lock();
if (global.sources >= config_get_config()->source_limit) {
if (global.sources >= source_limit) {
INFO1("Source (%s) logged in, but there are too many sources", mount);
global_unlock();
return 0;
......@@ -312,7 +336,11 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t
stats_event_inc(NULL, "sources");
mountproxy = config_get_config()->mounts;
config = config_get_config();
mountproxy = config->mounts;
thread_mutex_lock(&(config_locks()->mounts_lock));
config_release_config();
while(mountproxy) {
if(!strcmp(mountproxy->mountname, mount)) {
mountinfo = mountproxy;
......@@ -327,15 +355,18 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t
format_type_t format = format_get_type(contenttype);
if (format == FORMAT_ERROR) {
WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
thread_mutex_unlock(&(config_locks()->mounts_lock));
goto fail;
} else {
source = source_create(client, con, parser, mount,
format, mountinfo);
thread_mutex_unlock(&(config_locks()->mounts_lock));
}
} else {
format_type_t format = FORMAT_TYPE_MP3;
ERROR0("No content-type header, falling back to backwards compatibility mode for icecast 1.x relays. Assuming content is mp3.");
source = source_create(client, con, parser, mount, format, mountinfo);
thread_mutex_unlock(&(config_locks()->mounts_lock));
}
source->send_return = 1;
......@@ -408,17 +439,22 @@ static int _check_pass_ice(http_parser_t *parser, char *correctpass)
static int _check_relay_pass(http_parser_t *parser)
{
char *pass = config_get_config()->relay_password;
ice_config_t *config = config_get_config();
char *pass = config->relay_password;
if(!pass)
pass = config_get_config()->source_password;
pass = config->source_password;
config_release_config();
return _check_pass_http(parser, "relay", pass);
}
static int _check_admin_pass(http_parser_t *parser)
{
char *pass = config_get_config()->admin_password;
char *user = config_get_config()->admin_username;
ice_config_t *config = config_get_config();
char *pass = config->admin_password;
char *user = config->admin_username;
config_release_config();
if(!pass || !user)
return 0;
......@@ -427,11 +463,16 @@ static int _check_admin_pass(http_parser_t *parser)
static int _check_source_pass(http_parser_t *parser, char *mount)
{
char *pass = config_get_config()->source_password;
ice_config_t *config = config_get_config();
char *pass = config->source_password;
char *user = "source";
int ret;
int ice_login = config->ice_login;
mount_proxy *mountinfo = config->mounts;
thread_mutex_lock(&(config_locks()->mounts_lock));
config_release_config();
mount_proxy *mountinfo = config_get_config()->mounts;
while(mountinfo) {
if(!strcmp(mountinfo->mountname, mount)) {
if(mountinfo->password)
......@@ -443,13 +484,15 @@ static int _check_source_pass(http_parser_t *parser, char *mount)
mountinfo = mountinfo->next;
}
thread_mutex_unlock(&(config_locks()->mounts_lock));
if(!pass) {
WARN0("No source password set, rejecting source");
return 0;
}
ret = _check_pass_http(parser, user, pass);
if(!ret && config_get_config()->ice_login)
if(!ret && ice_login)
{
ret = _check_pass_ice(parser, pass);
if(ret)
......@@ -627,6 +670,19 @@ static void _handle_get_request(connection_t *con,
int bytes;
struct stat statbuf;
source_t *source;
int fileserve;
char *host;
int port;
ice_config_t *config;
int client_limit;
config = config_get_config();
fileserve = config->fileserve;
host = config->hostname;
port = config->port;
client_limit = config->client_limit;
config_release_config();
DEBUG0("Client connected");
......@@ -689,8 +745,8 @@ static void _handle_get_request(connection_t *con,
free(fullpath);
return;
}
else if(config_get_config()->fileserve &&
stat(fullpath, &statbuf) == 0) {
else if(fileserve && stat(fullpath, &statbuf) == 0)
{
fserve_client_create(client, fullpath);
free(fullpath);
return;
......@@ -709,14 +765,14 @@ static void _handle_get_request(connection_t *con,
"HTTP/1.0 200 OK\r\n"
"Content-Type: audio/x-mpegurl\r\n\r\n"
"http://%s:%d%s",
config_get_config()->hostname,
config_get_config()->port,
host,
port,
sourceuri
);
if(bytes > 0) client->con->sent_bytes = bytes;
client_destroy(client);
}
else if(config_get_config()->fileserve) {
else if(fileserve) {
fullpath = util_get_path_from_normalised_uri(sourceuri);
if(stat(fullpath, &statbuf) == 0) {
fserve_client_create(client, fullpath);
......@@ -774,7 +830,7 @@ static void _handle_get_request(connection_t *con,
}
global_lock();
if (global.clients >= config_get_config()->client_limit) {
if (global.clients >= client_limit) {
client_send_504(client,
"The server is already full. Try again later.");
global_unlock();
......@@ -788,7 +844,7 @@ static void _handle_get_request(connection_t *con,
DEBUG0("Source found for client");
global_lock();
if (global.clients >= config_get_config()->client_limit) {
if (global.clients >= client_limit) {
client_send_504(client,
"The server is already full. Try again later.");
global_unlock();
......@@ -847,6 +903,21 @@ static void *_handle_connection(void *arg)
/* grab a connection and set the socket to blocking */
while ((con = _get_connection())) {
/* Handle meta-connections */
if(con->event_number > 0) {
switch(con->event_number) {
case EVENT_CONFIG_READ:
event_config_read(con->event);
break;
default:
ERROR1("Unknown event number: %d", con->event_number);
break;
}
free(con);
continue;
}
stats_event_inc(NULL, "connections");
sock_set_blocking(con->sock, SOCK_BLOCK);
......
......@@ -21,6 +21,10 @@ typedef struct connection_tag
char *ip;
char *host;
/* For 'fake' connections */
int event_number;
void *event;
} connection_t;
void connection_initialize(void);
......@@ -31,6 +35,8 @@ connection_t *create_connection(sock_t sock, char *ip);
int connection_create_source(struct _client_tag *client, connection_t *con,
http_parser_t *parser, char *mount);
void connection_inject_event(int eventnum, void *event_data);
extern rwlock_t _source_shutdown_rwlock;
#endif /* __CONNECTION_H__ */
#include "event.h"
#include "config.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h"
#define CATMODULE "event"
void event_config_read(void *arg)
{
int ret;
ice_config_t *config;
ice_config_t new_config;
/* reread config file */
config = config_get_config(); /* Both to get the lock, and to be able
to find out the config filename */
ret = config_parse_file(config->config_filename, &new_config);
if(ret < 0) {
ERROR0("Error parsing config, not replacing existing config");
switch(ret) {
case CONFIG_EINSANE:
ERROR0("Config filename null or blank");
break;
case CONFIG_ENOROOT:
ERROR1("Root element not found in %s", config->config_filename);
break;
case CONFIG_EBADROOT:
ERROR1("Not an icecast2 config file: %s",
config->config_filename);
break;
default:
ERROR1("Parse error in reading %s", config->config_filename);
break;
}
config_release_config();
}
else {
config_clear(config);
config_set_config(&new_config);
config_release_config();
}
}
#ifndef __EVENT_H__
#define __EVENT_H__
#define EVENT_NO_EVENT 0
#define EVENT_CONFIG_READ 1
void event_config_read(void *nothing);
#endif /* __EVENT_H__ */
......@@ -78,7 +78,12 @@ static void create_mime_mappings(char *fn);
void fserve_initialize(void)
{
if(!config_get_config()->fileserve)
ice_config_t *config = config_get_config();
int serve = config->fileserve;
config_release_config();
if(!serve)
return;
create_mime_mappings(MIMETYPESFILE);
......@@ -95,7 +100,12 @@ void fserve_initialize(void)
void fserve_shutdown(void)
{
if(!config_get_config()->fileserve)
ice_config_t *config = config_get_config();
int serve = config->fileserve;
config_release_config();
if(!serve)
return;
if(!run_fserv)
......@@ -345,6 +355,11 @@ int fserve_client_create(client_t *httpclient, char *path)
{
fserve_t *client = calloc(1, sizeof(fserve_t));
int bytes;
int client_limit;
ice_config_t *config = config_get_config();
client_limit = config->client_limit;
config_release_config();
client->file = fopen(path, "rb");
if(!client->file) {
......@@ -358,7 +373,7 @@ int fserve_client_create(client_t *httpclient, char *path)
client->buf = malloc(BUFSIZE);
global_lock();
if(global.clients >= config_get_config()->client_limit) {
if(global.clients >= client_limit) {
httpclient->respcode = 504;
bytes = sock_write(httpclient->con->sock,
"HTTP/1.0 504 Server Full\r\n"
......
......@@ -125,7 +125,7 @@ static int _start_logging(void)
{
char fn_error[FILENAME_MAX];
char fn_access[FILENAME_MAX];
ice_config_t *config = config_get_config();
ice_config_t *config = config_get_config_unlocked();
if(strcmp(config->error_log, "-")) {
snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, config->error_log);
......@@ -157,9 +157,10 @@ static int _setup_socket(void)
{
ice_config_t *config;
config = config_get_config();
config = config_get_config_unlocked();
global.serversock = sock_get_server_socket(config->port, config->bind_address);
if (global.serversock == SOCK_ERROR)
return 0;
......@@ -180,7 +181,8 @@ static int _start_listening(void)
static int _server_proc_init(void)
{
if (!_setup_socket()) {
fprintf(stderr, "Could not create listener socket on port %d\n", config_get_config()->port);
fprintf(stderr, "Could not create listener socket on port %d\n",
config_get_config_unlocked()->port);
return 0;
}
......@@ -205,7 +207,7 @@ static void _server_proc(void)
static void _ch_root_uid_setup(void)
{
ice_config_t *conf = config_get_config();
ice_config_t *conf = config_get_config_unlocked();
#ifdef CHUID
struct passwd *user;
struct group *group;
......@@ -291,7 +293,9 @@ int main(int argc, char **argv)
_initialize_subsystems();
/* parse the config file */
ret = config_parse_file(filename);
config_get_config();
ret = config_initial_parse_file(filename);
config_release_config();
if (ret < 0) {
fprintf(stderr, "FATAL: error parsing config file:");
switch (ret) {
......
......@@ -10,6 +10,7 @@
#include "refbuf.h"
#include "client.h"
#include "logging.h"
#include "event.h"
#include "sighandler.h"
......@@ -34,16 +35,22 @@ void sighandler_initialize(void)
void _sig_hup(int signo)
{
INFO1("Caught signal %d, rehashing config and reopening logfiles (unimplemented)...", signo);
/* We do this elsewhere because it's a bad idea to hang around for too
* long re-reading an entire config file inside a signal handler. Bad
* practice.
*/
INFO1("Caught signal %d, scheduling config reread ...",
signo);
/* reread config file */
/* reopen logfiles */
connection_inject_event(EVENT_CONFIG_READ, NULL);
/* reopen logfiles (TODO: We don't do this currently) */
#ifdef __linux__
/* linux requires us to reattach the signal handler */
/* some OSes require us to reattach the signal handler */
signal(SIGHUP, _sig_hup);
#endif
}
void _sig_die(int signo)
......
......@@ -49,11 +49,18 @@ thread_type *_slave_thread_id;
static int _initialized = 0;
void slave_initialize(void) {
ice_config_t *config;
if (_initialized) return;
config = config_get_config();
/* Don't create a slave thread if it isn't configured */
if (config_get_config()->master_server == NULL &&
config_get_config()->relay == NULL)
if (config->master_server == NULL &&
config->relay == NULL)
{
config_release_config();
return;
}
config_release_config();
_initialized = 1;
_slave_thread_id = thread_create("Slave Thread", _slave_thread, NULL, THREAD_ATTACHED);
......@@ -131,28 +138,48 @@ static void create_relay_stream(char *server, int port,
static void *_slave_thread(void *arg) {
sock_t mastersock;
char buf[256];
int interval = config_get_config()->master_update_interval;
int interval;
char *authheader, *data;
int len;
char *username = "relay";
char *password = config_get_config()->master_password;
char *password;
int max_interval;
relay_server *relay;
ice_config_t *config;
config = config_get_config();
password = config->master_password;
interval = max_interval = config->master_update_interval;
if(password == NULL)
password = config_get_config()->source_password;
password = config->source_password;
config_release_config();
while (_initialized) {
if (config_get_config()->master_update_interval > ++interval) {
if (max_interval > ++interval) {
thread_sleep(1000000);
continue;
}
else
else {
/* In case it's been reconfigured */
config = config_get_config();
max_interval = config->master_update_interval;
config_release_config();
interval = 0;
}
config = config_get_config();
if(config->master_server != NULL) {
char *server = config->master_server;
int port = config->master_server_port;
config_release_config();
mastersock = sock_connect_wto(server, port, 0);
if(config_get_config()->master_server != NULL) {
mastersock = sock_connect_wto(config_get_config()->master_server,
config_get_config()->master_server_port, 0);
if (mastersock == SOCK_ERROR) {
WARN0("Relay slave failed to contact master server to fetch stream list");
continue;
......@@ -180,19 +207,23 @@ static void *_slave_thread(void *arg) {
if (!source_find_mount(buf)) {
avl_tree_unlock(global.source_tree);
create_relay_stream(
config_get_config()->master_server,
config_get_config()->master_server_port,
buf, NULL, 0);
create_relay_stream(server, port, buf, NULL, 0);
}
else
avl_tree_unlock(global.source_tree);
}
sock_close(mastersock);
}
else {
config_release_config();
}
/* And now, we process the individual mounts... */
relay = config_get_config()->relay;
config = config_get_config();
relay = config->relay;
thread_mutex_lock(&(config_locks()->relay_lock));
config_release_config();
while(relay) {
avl_tree_rlock(global.source_tree);
if(!source_find_mount(relay->localmount)) {
......@@ -205,6 +236,8 @@ static void *_slave_thread(void *arg) {
avl_tree_unlock(global.source_tree);
relay = relay->next;
}
thread_mutex_unlock(&(config_locks()->relay_lock));
}
thread_exit(0);
return NULL;
......
......@@ -59,7 +59,6 @@ source_t *source_create(client_t *client, connection_t *con,
http_parser_t *parser, const char *mount, format_type_t type,
mount_proxy *mountinfo)
{
int i = 0;
source_t *src;
src = (source_t *)malloc(sizeof(source_t));
......@@ -79,17 +78,6 @@ source_t *source_create(client_t *client, connection_t *con,
src->dumpfilename = NULL;
src->dumpfile = NULL;
src->audio_info = util_dict_new();
for (i=0;i<config_get_config()->num_yp_directories;i++) {
if (config_get_config()->yp_url[i]) {
src->ypdata[src->num_yp_directories] = yp_create_ypdata();
src->ypdata[src->num_yp_directories]->yp_url =
config_get_config()->yp_url[i];
src->ypdata[src->num_yp_directories]->yp_url_timeout =
config_get_config()->yp_url_timeout[i];
src->ypdata[src->num_yp_directories]->yp_touch_interval = 0;
src->num_yp_directories++;
}
}
if(mountinfo != NULL) {
src->fallback_mount = mountinfo->fallback_mount;
......@@ -191,9 +179,31 @@ void *source_main(void *arg)
int suppress_yp = 0;
char *ai;
long queue_limit = config_get_config()->queue_size_limit;
long queue_limit;
ice_config_t *config;
char *hostname;
int port;
config = config_get_config();
queue_limit = config->queue_size_limit;
timeout = config->source_timeout;
hostname = config->hostname;
port = config->port;
for (i=0;i<config->num_yp_directories;i++) {
if (config->yp_url[i]) {
source->ypdata[source->num_yp_directories] = yp_create_ypdata();
source->ypdata[source->num_yp_directories]->yp_url =
config->yp_url[i];
source->ypdata[source->num_yp_directories]->yp_url_timeout =
config->yp_url_timeout[i];
source->ypdata[source->num_yp_directories]->yp_touch_interval = 0;
source->num_yp_directories++;
}
}
timeout = config_get_config()->source_timeout;
config_release_config();
/* grab a read lock, to make sure we get a chance to cleanup */
thread_rwlock_rlock(source->shutdown_rwlock);
......@@ -273,12 +283,11 @@ void *source_main(void *arg)
}
/* 6 for max size of port */
listen_url_size = strlen("http://") +
strlen(config_get_config()->hostname) +
strlen(hostname) +
strlen(":") + 6 + strlen(source->mount) + 1;
source->ypdata[i]->listen_url = malloc(listen_url_size);
sprintf(source->ypdata[i]->listen_url, "http://%s:%d%s",
config_get_config()->hostname, config_get_config()->port,
source->mount);
hostname, port, source->mount);
}
if(!suppress_yp) {
......@@ -609,6 +618,9 @@ done:
global.sources--;
global_unlock();
if(source->dumpfile)
fclose(source->dumpfile);
/* release our hold on the lock so the main thread can continue cleaning up */
thread_rwlock_unlock(source->shutdown_rwlock);
......@@ -616,9 +628,6 @@ done:
avl_delete(global.source_tree, source, source_free_source);
avl_tree_unlock(global.source_tree);