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 2003-03-05
bug fixes) 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 2003-03-02
More features: More features:
......
...@@ -50,6 +50,8 @@ FEATURES ...@@ -50,6 +50,8 @@ FEATURES
- option to use ipv6 (equiv to using <bind-address>::</bindaddress>, I think. - 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 ...@@ -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 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\ 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 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\ 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
......
This diff is collapsed.
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#define MAX_YP_DIRECTORIES 25 #define MAX_YP_DIRECTORIES 25
#include "thread/thread.h"
typedef struct ice_config_dir_tag typedef struct ice_config_dir_tag
{ {
char *host; char *host;
...@@ -40,6 +42,8 @@ typedef struct _mount_proxy { ...@@ -40,6 +42,8 @@ typedef struct _mount_proxy {
typedef struct ice_config_tag typedef struct ice_config_tag
{ {
char *config_filename;
char *location; char *location;
char *admin; char *admin;
...@@ -90,15 +94,30 @@ typedef struct ice_config_tag ...@@ -90,15 +94,30 @@ typedef struct ice_config_tag
int num_yp_directories; int num_yp_directories;
} ice_config_t; } 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_initialize(void);
void config_shutdown(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); 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); int config_rehash(void);
ice_config_locks *config_locks(void);
ice_config_t *config_get_config(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__ */ #endif /* __CONFIG_H__ */
......
...@@ -11,7 +11,7 @@ int main(void) ...@@ -11,7 +11,7 @@ int main(void)
config_parse_file("icecast.xml"); config_parse_file("icecast.xml");
config = config_get_config(); config = config_get_config_unlocked();
_dump_config(config); _dump_config(config);
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "geturl.h" #include "geturl.h"
#include "format.h" #include "format.h"
#include "format_mp3.h" #include "format_mp3.h"
#include "event.h"
#define CATMODULE "connection" #define CATMODULE "connection"
...@@ -112,6 +113,10 @@ connection_t *create_connection(sock_t sock, char *ip) { ...@@ -112,6 +113,10 @@ connection_t *create_connection(sock_t sock, char *ip) {
con->con_time = time(NULL); con->con_time = time(NULL);
con->id = _next_connection_id(); con->id = _next_connection_id();
con->ip = ip; con->ip = ip;
con->event_number = EVENT_NO_EVENT;
con->event = NULL;
return con; return con;
} }
...@@ -209,10 +214,13 @@ static void _build_pool(void) ...@@ -209,10 +214,13 @@ static void _build_pool(void)
int i; int i;
thread_type *tid; thread_type *tid;
char buff[64]; char buff[64];
int threadpool_size;
config = config_get_config(); 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); snprintf(buff, 64, "Connection Thread #%d", i);
tid = thread_create(buff, _handle_connection, NULL, THREAD_ATTACHED); tid = thread_create(buff, _handle_connection, NULL, THREAD_ATTACHED);
_push_thread(&_conhands, tid); _push_thread(&_conhands, tid);
...@@ -290,6 +298,16 @@ static connection_t *_get_connection(void) ...@@ -290,6 +298,16 @@ static connection_t *_get_connection(void)
return con; 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 /* TODO: Make this return an appropriate error code so that we can use HTTP
* codes where appropriate * codes where appropriate
*/ */
...@@ -297,12 +315,18 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t ...@@ -297,12 +315,18 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t
source_t *source; source_t *source;
char *contenttype; char *contenttype;
mount_proxy *mountproxy, *mountinfo = NULL; 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 /* check to make sure this source wouldn't
** be over the limit ** be over the limit
*/ */
global_lock(); 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); INFO1("Source (%s) logged in, but there are too many sources", mount);
global_unlock(); global_unlock();
return 0; return 0;
...@@ -312,7 +336,11 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t ...@@ -312,7 +336,11 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t
stats_event_inc(NULL, "sources"); 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) { while(mountproxy) {
if(!strcmp(mountproxy->mountname, mount)) { if(!strcmp(mountproxy->mountname, mount)) {
mountinfo = mountproxy; mountinfo = mountproxy;
...@@ -327,15 +355,18 @@ int connection_create_source(client_t *client, connection_t *con, http_parser_t ...@@ -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); format_type_t format = format_get_type(contenttype);
if (format == FORMAT_ERROR) { if (format == FORMAT_ERROR) {
WARN1("Content-type \"%s\" not supported, dropping source", contenttype); WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
thread_mutex_unlock(&(config_locks()->mounts_lock));
goto fail; goto fail;
} else { } else {
source = source_create(client, con, parser, mount, source = source_create(client, con, parser, mount,
format, mountinfo); format, mountinfo);
thread_mutex_unlock(&(config_locks()->mounts_lock));
} }
} else { } else {
format_type_t format = FORMAT_TYPE_MP3; 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."); 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); source = source_create(client, con, parser, mount, format, mountinfo);
thread_mutex_unlock(&(config_locks()->mounts_lock));
} }
source->send_return = 1; source->send_return = 1;
...@@ -408,17 +439,22 @@ static int _check_pass_ice(http_parser_t *parser, char *correctpass) ...@@ -408,17 +439,22 @@ static int _check_pass_ice(http_parser_t *parser, char *correctpass)
static int _check_relay_pass(http_parser_t *parser) 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) if(!pass)
pass = config_get_config()->source_password; pass = config->source_password;
config_release_config();
return _check_pass_http(parser, "relay", pass); return _check_pass_http(parser, "relay", pass);
} }
static int _check_admin_pass(http_parser_t *parser) static int _check_admin_pass(http_parser_t *parser)
{ {
char *pass = config_get_config()->admin_password; ice_config_t *config = config_get_config();
char *user = config_get_config()->admin_username; char *pass = config->admin_password;
char *user = config->admin_username;
config_release_config();
if(!pass || !user) if(!pass || !user)
return 0; return 0;
...@@ -427,11 +463,16 @@ static int _check_admin_pass(http_parser_t *parser) ...@@ -427,11 +463,16 @@ static int _check_admin_pass(http_parser_t *parser)
static int _check_source_pass(http_parser_t *parser, char *mount) 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"; char *user = "source";
int ret; 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) { while(mountinfo) {
if(!strcmp(mountinfo->mountname, mount)) { if(!strcmp(mountinfo->mountname, mount)) {
if(mountinfo->password) if(mountinfo->password)
...@@ -443,13 +484,15 @@ static int _check_source_pass(http_parser_t *parser, char *mount) ...@@ -443,13 +484,15 @@ static int _check_source_pass(http_parser_t *parser, char *mount)
mountinfo = mountinfo->next; mountinfo = mountinfo->next;
} }
thread_mutex_unlock(&(config_locks()->mounts_lock));
if(!pass) { if(!pass) {
WARN0("No source password set, rejecting source"); WARN0("No source password set, rejecting source");
return 0; return 0;
} }
ret = _check_pass_http(parser, user, pass); ret = _check_pass_http(parser, user, pass);
if(!ret && config_get_config()->ice_login) if(!ret && ice_login)
{ {
ret = _check_pass_ice(parser, pass); ret = _check_pass_ice(parser, pass);
if(ret) if(ret)
...@@ -627,6 +670,19 @@ static void _handle_get_request(connection_t *con, ...@@ -627,6 +670,19 @@ static void _handle_get_request(connection_t *con,
int bytes; int bytes;
struct stat statbuf; struct stat statbuf;
source_t *source; 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"); DEBUG0("Client connected");
...@@ -689,8 +745,8 @@ static void _handle_get_request(connection_t *con, ...@@ -689,8 +745,8 @@ static void _handle_get_request(connection_t *con,
free(fullpath); free(fullpath);
return; return;
} }
else if(config_get_config()->fileserve && else if(fileserve && stat(fullpath, &statbuf) == 0)
stat(fullpath, &statbuf) == 0) { {
fserve_client_create(client, fullpath); fserve_client_create(client, fullpath);
free(fullpath); free(fullpath);
return; return;
...@@ -709,14 +765,14 @@ static void _handle_get_request(connection_t *con, ...@@ -709,14 +765,14 @@ static void _handle_get_request(connection_t *con,
"HTTP/1.0 200 OK\r\n" "HTTP/1.0 200 OK\r\n"
"Content-Type: audio/x-mpegurl\r\n\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n"
"http://%s:%d%s", "http://%s:%d%s",
config_get_config()->hostname, host,
config_get_config()->port, port,
sourceuri sourceuri
); );
if(bytes > 0) client->con->sent_bytes = bytes; if(bytes > 0) client->con->sent_bytes = bytes;
client_destroy(client); client_destroy(client);
} }
else if(config_get_config()->fileserve) { else if(fileserve) {
fullpath = util_get_path_from_normalised_uri(sourceuri); fullpath = util_get_path_from_normalised_uri(sourceuri);
if(stat(fullpath, &statbuf) == 0) { if(stat(fullpath, &statbuf) == 0) {
fserve_client_create(client, fullpath); fserve_client_create(client, fullpath);
...@@ -774,7 +830,7 @@ static void _handle_get_request(connection_t *con, ...@@ -774,7 +830,7 @@ static void _handle_get_request(connection_t *con,
} }
global_lock(); global_lock();
if (global.clients >= config_get_config()->client_limit) { if (global.clients >= client_limit) {
client_send_504(client, client_send_504(client,
"The server is already full. Try again later."); "The server is already full. Try again later.");
global_unlock(); global_unlock();
...@@ -788,7 +844,7 @@ static void _handle_get_request(connection_t *con, ...@@ -788,7 +844,7 @@ static void _handle_get_request(connection_t *con,
DEBUG0("Source found for client"); DEBUG0("Source found for client");
global_lock(); global_lock();
if (global.clients >= config_get_config()->client_limit) { if (global.clients >= client_limit) {
client_send_504(client, client_send_504(client,
"The server is already full. Try again later."); "The server is already full. Try again later.");
global_unlock(); global_unlock();
...@@ -847,6 +903,21 @@ static void *_handle_connection(void *arg) ...@@ -847,6 +903,21 @@ static void *_handle_connection(void *arg)
/* grab a connection and set the socket to blocking */ /* grab a connection and set the socket to blocking */
while ((con = _get_connection())) { 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"); stats_event_inc(NULL, "connections");
sock_set_blocking(con->sock, SOCK_BLOCK); sock_set_blocking(con->sock, SOCK_BLOCK);
......
...@@ -21,6 +21,10 @@ typedef struct connection_tag ...@@ -21,6 +21,10 @@ typedef struct connection_tag
char *ip; char *ip;
char *host; char *host;
/* For 'fake' connections */
int event_number;
void *event;
} connection_t; } connection_t;
void connection_initialize(void); void connection_initialize(void);
...@@ -31,6 +35,8 @@ connection_t *create_connection(sock_t sock, char *ip); ...@@ -31,6 +35,8 @@ connection_t *create_connection(sock_t sock, char *ip);
int connection_create_source(struct _client_tag *client, connection_t *con, int connection_create_source(struct _client_tag *client, connection_t *con,
http_parser_t *parser, char *mount); http_parser_t *parser, char *mount);
void connection_inject_event(int eventnum, void *event_data);
extern rwlock_t _source_shutdown_rwlock; extern rwlock_t _source_shutdown_rwlock;
#endif /* __CONNECTION_H__ */ #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); ...@@ -78,7 +78,12 @@ static void create_mime_mappings(char *fn);
void fserve_initialize(void) 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; return;
create_mime_mappings(MIMETYPESFILE); create_mime_mappings(MIMETYPESFILE);
...@@ -95,7 +100,12 @@ void fserve_initialize(void) ...@@ -95,7 +100,12 @@ void fserve_initialize(void)
void fserve_shutdown(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; return;
if(!run_fserv) if(!run_fserv)
...@@ -345,6 +355,11 @@ int fserve_client_create(client_t *httpclient, char *path) ...@@ -345,6 +355,11 @@ int fserve_client_create(client_t *httpclient, char *path)
{ {
fserve_t *client = calloc(1, sizeof(fserve_t)); fserve_t *client = calloc(1, sizeof(fserve_t));
int bytes; 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"); client->file = fopen(path, "rb");
if(!client->file) { if(!client->file) {
...@@ -358,7 +373,7 @@ int fserve_client_create(client_t *httpclient, char *path) ...@@ -358,7 +373,7 @@ int fserve_client_create(client_t *httpclient, char *path)
client->buf = malloc(BUFSIZE); client->buf = malloc(BUFSIZE);
global_lock(); global_lock();
if(global.clients >= config_get_config()->client_limit) { if(global.clients >= client_limit) {
httpclient->respcode = 504; httpclient->respcode = 504;
bytes = sock_write(httpclient->con->sock, bytes = sock_write(httpclient->con->sock,
"HTTP/1.0 504 Server Full\r\n" "HTTP/1.0 504 Server Full\r\n"
......
...@@ -125,7 +125,7 @@ static int _start_logging(void) ...@@ -125,7 +125,7 @@ static int _start_logging(void)
{ {
char fn_error[FILENAME_MAX]; char fn_error[FILENAME_MAX];
char fn_access[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, "-")) { if(strcmp(config->error_log, "-")) {
snprintf(fn_error, FILENAME_MAX, "%s%s%s", config->log_dir, PATH_SEPARATOR, 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) ...@@ -157,9 +157,10 @@ static int _setup_socket(void)
{ {
ice_config_t *config; ice_config_t *config;
config = config_get_config(); config = config_get_config_unlocked();
global.serversock = sock_get_server_socket(config->port, config->bind_address); global.serversock = sock_get_server_socket(config->port, config->bind_address);
if (global.serversock == SOCK_ERROR) if (global.serversock == SOCK_ERROR)
return 0; return 0;
...@@ -180,7 +181,8 @@ static int _start_listening(void) ...@@ -180,7 +181,8 @@ static int _start_listening(void)
static int _server_proc_init(void) static int _server_proc_init(void)
{ {
if (!_setup_socket()) { 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; return 0;
} }
...@@ -205,7 +207,7 @@ static void _server_proc(void) ...@@ -205,7 +207,7 @@ static void _server_proc(void)
static void _ch_root_uid_setup(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 #ifdef CHUID
struct passwd *user; struct passwd *user;
struct group *group; struct group *group;
...@@ -291,7 +293,9 @@ int main(int argc, char **argv) ...@@ -291,7 +293,9 @@ int main(int argc, char **argv)
_initialize_subsystems(); _initialize_subsystems();
/* parse the config file */ /* parse the config file */
ret = config_parse_file(filename); config_get_config();
ret = config_initial_parse_file(filename);
config_release_config();
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "FATAL: error parsing config file:"); fprintf(stderr, "FATAL: error parsing config file:");
switch (ret) { switch (ret) {
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "refbuf.h" #include "refbuf.h"
#include "client.h" #include "client.h"
#include "logging.h" #include "logging.h"
#include "event.h"
#include "sighandler.h" #include "sighandler.h"
...@@ -34,16 +35,22 @@ void sighandler_initialize(void) ...@@ -34,16 +35,22 @@ void sighandler_initialize(void)
void _sig_hup(int signo) 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 */ /* reread config file */
/* reopen logfiles */ connection_inject_event(EVENT_CONFIG_READ, NULL);
/* reopen logfiles (TODO: We don't do this currently) */
#ifdef __linux__ /* some OSes require us to reattach the signal handler */
/* linux requires us to reattach the signal handler */
signal(SIGHUP, _sig_hup); signal(SIGHUP, _sig_hup);
#endif
} }
void _sig_die(int signo) void _sig_die(int signo)
......