1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-07-07 02:44:13 -04:00

Initial revision

svn path=/trunk/icecast/; revision=1996
This commit is contained in:
Jack Moffitt 2001-09-10 02:21:46 +00:00
commit 61316a25d7
38 changed files with 3638 additions and 0 deletions

1
AUTHORS Normal file
View File

@ -0,0 +1 @@
Jack Moffitt <jack@icecast.org>

1
COPYING Normal file
View File

@ -0,0 +1 @@
<insert GPL here>

16
Makefile.am Normal file
View File

@ -0,0 +1,16 @@
## Process this file with automake to produce Makefile.in
AUTOMAKE_OPTIONS = foreign dist-zip
SUBDIRS = src
EXTRA_DIST = README AUTHORS COPYING
# SCCS Definitions (for BitKeeper)
GET = true
debug:
$(MAKE) all CFLAGS="@DEBUG@"
profile:
$(MAKE) all CFLAGS="@PROFILE@"

1
README Normal file
View File

@ -0,0 +1 @@
This is still experimental.

18
TODO Normal file
View File

@ -0,0 +1,18 @@
BUGS
----
- stats get off?
FEATURES
--------
- pull out vorbis comments. and send to stats.
- directory server GUID checks
directory server does GET /GUID-asldjfasldfjalsdkfjasldkfj HTTP/1.0
and either gets a 404 if it's wrong, or a 200 if it's correct.
- adding new stats type, event. events don't modify the global stats tree,
ie, source /1234.ogg disconnected
- stats.xml, a users requests this, and gets an xml dump of the current stats.

195
acinclude.m4 Normal file
View File

@ -0,0 +1,195 @@
# Configure paths for libogg
# Jack Moffitt <jack@icecast.org> 10-21-2000
# Shamelessly stolen from Owen Taylor and Manish Singh
dnl AM_PATH_OGG([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
dnl Test for libogg, and define OGG_CFLAGS and OGG_LIBS
dnl
AC_DEFUN(AM_PATH_OGG,
[dnl
dnl Get the cflags and libraries
dnl
AC_ARG_WITH(ogg-prefix,[ --with-ogg-prefix=PFX Prefix where libogg is installed (optional)], ogg_prefix="$withval", ogg_prefix="")
AC_ARG_ENABLE(oggtest, [ --disable-oggtest Do not try to compile and run a test Ogg program],, enable_oggtest=yes)
if test "x$ogg_prefix" != "xNONE" ; then
ogg_args="$ogg_args --prefix=$ogg_prefix"
OGG_CFLAGS="-I$ogg_prefix/include"
OGG_LIBS="-L$ogg_prefix/lib"
elif test "$prefix" != ""; then
ogg_args="$ogg_args --prefix=$prefix"
OGG_CFLAGS="-I$prefix/include"
OGG_LIBS="-L$prefix/lib"
fi
OGG_LIBS="$OGG_LIBS -logg"
AC_MSG_CHECKING(for Ogg)
no_ogg=""
if test "x$enable_oggtest" = "xyes" ; then
ac_save_CFLAGS="$CFLAGS"
ac_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $OGG_CFLAGS"
LIBS="$LIBS $OGG_LIBS"
dnl
dnl Now check if the installed Ogg is sufficiently new.
dnl
rm -f conf.oggtest
AC_TRY_RUN([
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ogg/ogg.h>
int main ()
{
system("touch conf.oggtest");
return 0;
}
],, no_ogg=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
if test "x$no_ogg" = "x" ; then
AC_MSG_RESULT(yes)
ifelse([$1], , :, [$1])
else
AC_MSG_RESULT(no)
if test -f conf.oggtest ; then
:
else
echo "*** Could not run Ogg test program, checking why..."
CFLAGS="$CFLAGS $OGG_CFLAGS"
LIBS="$LIBS $OGG_LIBS"
AC_TRY_LINK([
#include <stdio.h>
#include <ogg/ogg.h>
], [ return 0; ],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding Ogg or finding the wrong"
echo "*** version of Ogg. If it is not finding Ogg, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means Ogg was incorrectly installed"
echo "*** or that you have moved Ogg since it was installed. In the latter case, you"
echo "*** may want to edit the ogg-config script: $OGG_CONFIG" ])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
OGG_CFLAGS=""
OGG_LIBS=""
ifelse([$2], , :, [$2])
fi
AC_SUBST(OGG_CFLAGS)
AC_SUBST(OGG_LIBS)
rm -f conf.oggtest
])
# Configure paths for libvorbis
# Jack Moffitt <jack@icecast.org> 10-21-2000
# Shamelessly stolen from Owen Taylor and Manish Singh
dnl AM_PATH_VORBIS([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
dnl Test for libvorbis, and define VORBIS_CFLAGS and VORBIS_LIBS
dnl
AC_DEFUN(AM_PATH_VORBIS,
[dnl
dnl Get the cflags and libraries
dnl
AC_ARG_WITH(vorbis-prefix,[ --with-vorbis-prefix=PFX Prefix where libvorbis is installed (optional)], vorbis_prefix="$withval", vorbis_prefix="")
AC_ARG_ENABLE(vorbistest, [ --disable-vorbistest Do not try to compile and run a test Vorbis program],, enable_vorbistest=yes)
if test "x$vorbis_prefix" != "xNONE" ; then
vorbis_args="$vorbis_args --prefix=$vorbis_prefix"
VORBIS_CFLAGS="-I$vorbis_prefix/include"
VORBIS_LIBDIR="-L$vorbis_prefix/lib"
elif test "$prefix" != ""; then
vorbis_args="$vorbis_args --prefix=$prefix"
VORBIS_CFLAGS="-I$prefix/include"
VORBIS_LIBDIR="-L$prefix/lib"
fi
VORBIS_LIBS="$VORBIS_LIBDIR -lvorbis -lm"
VORBISFILE_LIBS="-lvorbisfile"
VORBISENC_LIBS="-lvorbisenc"
AC_MSG_CHECKING(for Vorbis)
no_vorbis=""
if test "x$enable_vorbistest" = "xyes" ; then
ac_save_CFLAGS="$CFLAGS"
ac_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $VORBIS_CFLAGS"
LIBS="$LIBS $VORBIS_LIBS $OGG_LIBS"
dnl
dnl Now check if the installed Vorbis is sufficiently new.
dnl
rm -f conf.vorbistest
AC_TRY_RUN([
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vorbis/codec.h>
int main ()
{
system("touch conf.vorbistest");
return 0;
}
],, no_vorbis=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
if test "x$no_vorbis" = "x" ; then
AC_MSG_RESULT(yes)
ifelse([$1], , :, [$1])
else
AC_MSG_RESULT(no)
if test -f conf.vorbistest ; then
:
else
echo "*** Could not run Vorbis test program, checking why..."
CFLAGS="$CFLAGS $VORBIS_CFLAGS"
LIBS="$LIBS $VORBIS_LIBS $OGG_LIBS"
AC_TRY_LINK([
#include <stdio.h>
#include <vorbis/codec.h>
], [ return 0; ],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding Vorbis or finding the wrong"
echo "*** version of Vorbis. If it is not finding Vorbis, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means Vorbis was incorrectly installed"
echo "*** or that you have moved Vorbis since it was installed." ])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
VORBIS_CFLAGS=""
VORBIS_LIBS=""
VORBISFILE_LIBS=""
VORBISENC_LIBS=""
ifelse([$2], , :, [$2])
fi
AC_SUBST(VORBIS_CFLAGS)
AC_SUBST(VORBIS_LIBS)
AC_SUBST(VORBISFILE_LIBS)
AC_SUBST(VORBISENC_LIBS)
rm -f conf.vorbistest
])

61
autogen.sh Executable file
View File

@ -0,0 +1,61 @@
#!/bin/sh
# Run this to set up the build system: configure, makefiles, etc.
# (based on the version in enlightenment's cvs)
package="icecast"
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
cd "$srcdir"
DIE=0
(autoconf --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "You must have autoconf installed to compile $package."
echo "Download the appropriate package for your distribution,"
echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
DIE=1
}
(automake --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "You must have automake installed to compile $package."
echo "Download the appropriate package for your system,
echo "or get the source from one of the GNU ftp sites"
echo "listed in http://www.gnu.org/order/ftp.html"
DIE=1
}
(libtool --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "You must have libtool installed to compile $package."
echo "Download the appropriate package for your system,
echo "or get the source from one of the GNU ftp sites"
echo "listed in http://www.gnu.org/order/ftp.html"
DIE=1
}
if test "$DIE" -eq 1; then
exit 1
fi
if test -z "$*"; then
echo "I am going to run ./configure with no arguments - if you wish "
echo "to pass any to it, please specify them on the $0 command line."
fi
echo "Generating configuration files for $package, please wait...."
echo " aclocal $ACLOCAL_FLAGS"
aclocal $ACLOCAL_FLAGS
#echo " autoheader"
#autoheader
echo " libtoolize --automake"
libtoolize --automake
echo " automake --add-missing"
automake --add-missing
echo " autoconf"
autoconf
$srcdir/configure "$@" && echo

35
conf/icecast.xml Normal file
View File

@ -0,0 +1,35 @@
<icecast>
<location>Jack's House</location>
<admin>jack@icecast.org</admin>
<limits>
<clients>100</clients>
<sources>2</sources>
<threadpool>5</threadpool>
<client-timeout>15</client-timeout>
</limits>
<source-password>hackme</source-password>
<directory>
<touch-freq>5</touch-freq>
<server>
<host>yp.icecast.org</host>
<touch-freq>15</touch-freq>
</server>
</directory>
<hostname>i.cantcode.com</hostname>
<port>8000</port>
<!--<bind-address>127.0.0.1</bind-address>-->
<paths>
<basedir>/usr/local/icecast</basedir>
<logdir>/tmp</logdir>
</paths>
<logging>
<accesslog>access.log</accesslog>
<errorlog>error.log</errorlog>
</logging>
</icecast>

106
configure.in Normal file
View File

@ -0,0 +1,106 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(src/main.c)
AM_INIT_AUTOMAKE(icecast,2.0)
AC_PROG_CC
AC_CANONICAL_HOST
AM_PROG_LIBTOOL
dnl Set some options based on environment
if test -z "$GCC"; then
case $host in
*-*-irix*)
DEBUG="-g -signed"
CFLAGS="-O2 -w -signed"
PROFILE="-p -g3 -O2 -signed"
;;
sparc-sun-solaris*)
DEBUG="-v -g"
CFLAGS="-xO4 -fast -w -fsimple -native -xcg92"
PROFILE="-v -xpg -g -xO4 -fast -native -fsimple -xcg92 -Dsuncc"
;;
*)
DEBUG="-g"
CFLAGS="-O"
PROFILE="-g -p"
;;
esac
else
case $host in
*-*-linux*)
DEBUG="-g -Wall -fsigned-char -D_REENTRANT -D_GNU_SOURCE"
CFLAGS="-O20 -ffast-math -fsigned-char -D_REENTRANT -D_GNU_SOURCE"
PROFILE="-Wall -W -pg -g -O20 -ffast-math -fsigned-char -D_REENTRANT -D_GNU_SOURCE"
;;
sparc-sun-*)
DEBUG="-g -Wall -fsigned-char -mv8"
CFLAGS="-O20 -ffast-math -fsigned-char -mv8"
PROFILE="-pg -g -O20 -fsigned-char -mv8"
;;
*)
DEBUG="-g -Wall -fsigned-char"
CFLAGS="-O20 -fsigned-char"
PROFILE="-O20 -g -pg -fsigned-char"
;;
esac
fi
dnl Checks for programs.
dnl Checks for libraries.
dnl IPV6
AC_SEARCH_LIBS(inet_pton, socket, AC_DEFINE(HAVE_IPV6, 1, [Define if you have IPV6 support]))
AC_SEARCH_LIBS(getipnodebyname, nsl,
AC_DEFINE(HAVE_GETIPNODEBYNAME, 1,
[Define if you have the getipnodebyname function])
)
dnl Checks for header files.
AC_HEADER_STDC
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
dnl Check for types
dnl Checks for library functions.
dnl -- configure options --
AC_ARG_WITH(xml-config,
[ --with-xml-config=PATH use xml-config in PATH to find libxml ],
[if ! test -x "$with_xml_config"
then
AC_MSG_ERROR([$with_xml_config cannot be executed])
fi
XMLCONFIG="$with_xml_config"]
)
if test -z "$XMLCONFIG"
then
AC_CHECK_PROGS(XMLCONFIG, [xml2-config xml-config])
fi
if test -n "$XMLCONFIG"
then
LIBS="$LIBS `$XMLCONFIG --libs`"
CPPFLAGS="$CPPFLAGS `$XMLCONFIG --cflags`"
AC_CHECK_FUNC(xmlParseFile,, [AC_MSG_ERROR([There was a problem linking with libxml])])
else
AC_MSG_ERROR([xml-config could not be found])
fi
AM_PATH_OGG(LIBS="$LIBS $OGG_LIBS", AC_MSG_ERROR(must have Ogg installed!))
AM_PATH_VORBIS(LIBS="$LIBS $VORBIS_LIBS", AC_MSG_ERROR(must have Vorbis installed!))
dnl Make substitutions
AC_SUBST(LIBTOOL_DEPS)
AC_SUBST(OPT)
AC_SUBST(LIBS)
AC_SUBST(DEBUG)
AC_SUBST(CFLAGS)
AC_SUBST(PROFILE)
AC_OUTPUT(Makefile src/Makefile src/avl/Makefile src/httpp/Makefile src/thread/Makefile src/log/Makefile src/net/Makefile src/timing/Makefile)

29
src/Makefile.am Normal file
View File

@ -0,0 +1,29 @@
## Process this with automake to create Makefile.in
AUTOMAKE_OPTIONS = foreign
SUBDIRS = avl thread httpp net log timing
bin_PROGRAMS = icecast
noinst_HEADERS = config.h os.h logging.h sighandler.h connection.h global.h\
util.h source.h stats.h refbuf.h client.h format.h format_vorbis.h
icecast_SOURCES = config.c main.c logging.c sighandler.c connection.c global.c\
util.c source.c stats.c refbuf.c client.c format.c format_vorbis.c
icecast_LDADD = net/libicenet.la thread/libicethread.la httpp/libicehttpp.la\
log/libicelog.la avl/libiceavl.la timing/libicetiming.la
LIBS = -lpthread
INCLUDES = -I$(srcdir)/net -I$(srcdir)/thread -I$(srcdir)/avl -I$(srcdir)/httpp \
-I$(srcdir)/log -I$(srcdir)/timing
# SCCS stuff (for BitKeeper)
GET = true
debug:
$(MAKE) all CFLAGS="@DEBUG@"
profile:
$(MAKE) all CFLAGS="@PROFILE@"

1
src/TODO Normal file
View File

@ -0,0 +1 @@
need a shutdown function in case anything else in the code needs to have icecast gracefully shutdown.

46
src/client.c Normal file
View File

@ -0,0 +1,46 @@
/* client.c
**
** client interface implementation
**
*/
#include <stdlib.h>
#include <string.h>
#include "thread.h"
#include "avl.h"
#include "httpp.h"
#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h"
client_t *client_create(connection_t *con, http_parser_t *parser)
{
client_t *client = (client_t *)malloc(sizeof(client_t));
client->con = con;
client->parser = parser;
client->queue = NULL;
client->pos = 0;
return client;
}
void client_destroy(client_t *client)
{
refbuf_t *refbuf;
/* write log entry */
logging_access(client);
connection_close(client->con);
httpp_destroy(client->parser);
while ((refbuf = refbuf_queue_remove(&client->queue)))
refbuf_release(refbuf);
free(client);
}

28
src/client.h Normal file
View File

@ -0,0 +1,28 @@
/* client.h
**
** client data structions and function definitions
**
*/
#ifndef __CLIENT_H__
#define __CLIENT_H__
typedef struct _client_tag
{
/* the clients connection */
connection_t *con;
/* the clients http headers */
http_parser_t *parser;
/* http response code for this client */
int respcode;
/* buffer queue */
refbuf_queue_t *queue;
/* position in first buffer */
unsigned long pos;
} client_t;
client_t *client_create(connection_t *con, http_parser_t *parser);
void client_destroy(client_t *client);
#endif /* __CLIENT_H__ */

296
src/config.c Normal file
View File

@ -0,0 +1,296 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xmlmemory.h>
#include <parser.h>
#include "config.h"
#define CONFIG_DEFAULT_LOCATION "Earth"
#define CONFIG_DEFAULT_ADMIN "icemaster@localhost"
#define CONFIG_DEFAULT_CLIENT_LIMIT 256
#define CONFIG_DEFAULT_SOURCE_LIMIT 16
#define CONFIG_DEFAULT_THREADPOOL_SIZE 4
#define CONFIG_DEFAULT_CLIENT_TIMEOUT 30
#define CONFIG_DEFAULT_HEADER_TIMEOUT 15
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
#define CONFIG_DEFAULT_TOUCH_FREQ 5
#define CONFIG_DEFAULT_HOSTNAME "localhost"
#define CONFIG_DEFAULT_PORT 8888
#define CONFIG_DEFAULT_BASE_DIR "/usr/local/icecast"
#define CONFIG_DEFAULT_LOG_DIR "/usr/local/icecast/logs"
#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
#define CONFIG_DEFAULT_ERROR_LOG "error.log"
ice_config_t _configuration;
char *_config_filename;
static void _set_defaults(void);
static void _parse_root(xmlDocPtr doc, xmlNodePtr node);
static void _parse_limits(xmlDocPtr doc, xmlNodePtr node);
static void _parse_directory(xmlDocPtr doc, xmlNodePtr node);
static void _parse_paths(xmlDocPtr doc, xmlNodePtr node);
static void _parse_logging(xmlDocPtr doc, xmlNodePtr node);
static void _add_server(xmlDocPtr doc, xmlNodePtr node);
void config_initialize(void)
{
memset(&_configuration, 0, sizeof(ice_config_t));
_set_defaults();
_config_filename = NULL;
}
void config_shutdown(void)
{
if (_config_filename) free(_config_filename);
if (_configuration.location) free(_configuration.location);
if (_configuration.admin) free(_configuration.admin);
if (_configuration.source_password) free(_configuration.source_password);
if (_configuration.hostname) free(_configuration.hostname);
if (_configuration.base_dir) free(_configuration.base_dir);
if (_configuration.log_dir) free(_configuration.log_dir);
if (_configuration.access_log) free(_configuration.access_log);
if (_configuration.error_log) free(_configuration.error_log);
memset(&_configuration, 0, sizeof(ice_config_t));
}
int config_parse_file(const char *filename)
{
xmlDocPtr doc;
xmlNodePtr node;
if (filename == NULL || strcmp(filename, "") == 0) return CONFIG_EINSANE;
_config_filename = (char *)strdup(filename);
doc = xmlParseFile(_config_filename);
if (doc == NULL) {
return -1;
}
node = xmlDocGetRootElement(doc);
if (node == NULL) {
xmlFreeDoc(doc);
return CONFIG_ENOROOT;
}
if (strcmp(node->name, "icecast") != 0) {
xmlFreeDoc(doc);
return CONFIG_EBADROOT;
}
xmlDocDump(stdout, doc);
_parse_root(doc, node->xmlChildrenNode);
xmlFreeDoc(doc);
return 0;
}
int config_parse_cmdline(int arg, char **argv)
{
return 0;
}
int config_rehash(void)
{
return 0;
}
ice_config_t *config_get_config(void)
{
return &_configuration;
}
static void _set_defaults(void)
{
_configuration.location = (char *)strdup(CONFIG_DEFAULT_LOCATION);
_configuration.admin = (char *)strdup(CONFIG_DEFAULT_ADMIN);
_configuration.client_limit = CONFIG_DEFAULT_CLIENT_LIMIT;
_configuration.source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
_configuration.threadpool_size = CONFIG_DEFAULT_THREADPOOL_SIZE;
_configuration.client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
_configuration.header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
_configuration.source_password = (char *)strdup(CONFIG_DEFAULT_SOURCE_PASSWORD);
_configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ;
_configuration.dir_list = NULL;
_configuration.hostname = (char *)strdup(CONFIG_DEFAULT_HOSTNAME);
_configuration.port = CONFIG_DEFAULT_PORT;
_configuration.bind_address = NULL;
_configuration.base_dir = (char *)strdup(CONFIG_DEFAULT_BASE_DIR);
_configuration.log_dir = (char *)strdup(CONFIG_DEFAULT_LOG_DIR);
_configuration.access_log = (char *)strdup(CONFIG_DEFAULT_ACCESS_LOG);
_configuration.error_log = (char *)strdup(CONFIG_DEFAULT_ERROR_LOG);
}
static void _parse_root(xmlDocPtr doc, xmlNodePtr node)
{
char *tmp;
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
if (strcmp(node->name, "location") == 0) {
if (_configuration.location) free(_configuration.location);
_configuration.location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "admin") == 0) {
if (_configuration.admin) free(_configuration.admin);
_configuration.admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "source-password") == 0) {
if (_configuration.source_password) free(_configuration.source_password);
_configuration.source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "hostname") == 0) {
if (_configuration.hostname) free(_configuration.hostname);
_configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "port") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.port = atoi(tmp);
if (tmp) free(tmp);
} else if (strcmp(node->name, "bind-address") == 0) {
if (_configuration.bind_address) free(_configuration.bind_address);
_configuration.bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "limits") == 0) {
_parse_limits(doc, node->xmlChildrenNode);
} else if (strcmp(node->name, "directory") == 0) {
_parse_directory(doc, node->xmlChildrenNode);
} else if (strcmp(node->name, "paths") == 0) {
_parse_paths(doc, node->xmlChildrenNode);
} else if (strcmp(node->name, "logging") == 0) {
_parse_logging(doc, node->xmlChildrenNode);
}
} while ((node = node->next));
}
static void _parse_limits(xmlDocPtr doc, xmlNodePtr node)
{
char *tmp;
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
if (strcmp(node->name, "clients") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.client_limit = atoi(tmp);
if (tmp) free(tmp);
} else if (strcmp(node->name, "sources") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.source_limit = atoi(tmp);
if (tmp) free(tmp);
} else if (strcmp(node->name, "threadpool") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.threadpool_size = atoi(tmp);
if (tmp) free(tmp);
} else if (strcmp(node->name, "client-timeout") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.client_timeout = atoi(tmp);
if (tmp) free(tmp);
} else if (strcmp(node->name, "header-timeout") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.header_timeout = atoi(tmp);
if (tmp) free(tmp);
}
} while ((node = node->next));
}
static void _parse_directory(xmlDocPtr doc, xmlNodePtr node)
{
char *tmp;
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
if (strcmp(node->name, "server") == 0) {
_add_server(doc, node->xmlChildrenNode);
} else if (strcmp(node->name, "touch-freq") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.touch_freq = atoi(tmp);
if (tmp) free(tmp);
}
} while ((node = node->next));
}
static void _parse_paths(xmlDocPtr doc, xmlNodePtr node)
{
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
if (strcmp(node->name, "basedir") == 0) {
if (_configuration.base_dir) free(_configuration.base_dir);
_configuration.base_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "logdir") == 0) {
if (_configuration.log_dir) free(_configuration.log_dir);
_configuration.log_dir = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
}
} while ((node = node->next));
}
static void _parse_logging(xmlDocPtr doc, xmlNodePtr node)
{
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
if (strcmp(node->name, "accesslog") == 0) {
if (_configuration.access_log) free(_configuration.access_log);
_configuration.access_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (strcmp(node->name, "errorlog") == 0) {
if (_configuration.error_log) free(_configuration.error_log);
_configuration.error_log = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
}
} while ((node = node->next));
}
static void _add_server(xmlDocPtr doc, xmlNodePtr node)
{
ice_config_dir_t *dirnode, *server;
int addnode;
char *tmp;
server = (ice_config_dir_t *)malloc(sizeof(ice_config_dir_t));
server->touch_freq = _configuration.touch_freq;
server->host = NULL;
addnode = 0;
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
if (strcmp(node->name, "host") == 0) {
server->host = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
addnode = 1;
} else if (strcmp(node->name, "touch-freq") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
server->touch_freq = atoi(tmp);
if (tmp) free(tmp);
}
server->next = NULL;
} while ((node = node->next));
if (addnode) {
dirnode = _configuration.dir_list;
if (dirnode == NULL) {
_configuration.dir_list = server;
} else {
while (dirnode->next) dirnode = dirnode->next;
dirnode->next = server;
}
server = NULL;
addnode = 0;
}
if (server) {
if (server->host) free(server->host);
free(server);
server = NULL;
}
}

55
src/config.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef __CONFIG_H__
#define __CONFIG_H__
#define CONFIG_EINSANE -1
#define CONFIG_ENOROOT -2
#define CONFIG_EBADROOT -3
typedef struct ice_config_dir_tag
{
char *host;
int touch_freq;
struct ice_config_dir_tag *next;
} ice_config_dir_t;
typedef struct ice_config_tag
{
char *location;
char *admin;
int client_limit;
int source_limit;
int threadpool_size;
int client_timeout;
int header_timeout;
char *source_password;
int touch_freq;
ice_config_dir_t *dir_list;
char *hostname;
int port;
char *bind_address;
char *base_dir;
char *log_dir;
char *access_log;
char *error_log;
} ice_config_t;
void config_initialize(void);
void config_shutdown(void);
int config_parse_file(const char *filename);
int config_parse_cmdline(int arg, char **argv);
int config_rehash(void);
ice_config_t *config_get_config(void);
#endif /* __CONFIG_H__ */

58
src/configtest.c Normal file
View File

@ -0,0 +1,58 @@
#include <stdio.h>
#include "config.h"
void _dump_config(ice_config_t *config);
int main(void)
{
ice_config_t *config;
config_initialize();
config_parse_file("icecast.xml");
config = config_get_config();
_dump_config(config);
config_shutdown();
return 0;
}
void _dump_config(ice_config_t *config)
{
ice_config_dir_t *node;
printf("-----\n");
printf("location = %s\n", config->location);
printf("admin = %s\n", config->admin);
printf("client_limit = %d\n", config->client_limit);
printf("source_limit = %d\n", config->source_limit);
printf("threadpool_size = %d\n", config->threadpool_size);
printf("client_timeout = %d\n", config->client_timeout);
printf("source_password = %s\n", config->source_password);
printf("touch_freq = %d\n", config->touch_freq);
node = config->dir_list;
while (node) {
printf("directory.touch_freq = %d\n", node->touch_freq);
printf("directory.host = %s\n", node->host);
node = node->next;
}
printf("hostname = %s\n", config->hostname);
printf("port = %d\n", config->port);
printf("bind_address = %s\n", config->bind_address);
printf("base_dir = %s\n", config->base_dir);
printf("log_dir = %s\n", config->log_dir);
printf("access_log = %s\n", config->access_log);
printf("error_log = %s\n", config->error_log);
printf("-----\n");
}

505
src/connection.c Normal file
View File

@ -0,0 +1,505 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include "os.h"
#include "thread.h"
#include "avl.h"
#include "sock.h"
#include "log.h"
#include "httpp.h"
#include "config.h"
#include "global.h"
#include "util.h"
#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "stats.h"
#include "format.h"
#include "logging.h"
#include "source.h"
#define CATMODULE "connection"
typedef struct con_queue_tag {
connection_t *con;
struct con_queue_tag *next;
} con_queue_t;
typedef struct _thread_queue_tag {
long thread_id;
struct _thread_queue_tag *next;
} thread_queue_t;
static mutex_t _connection_mutex;
static unsigned long _current_id = 0;
static int _initialized = 0;
static cond_t _pool_cond;
static con_queue_t *_queue = NULL;
static mutex_t _queue_mutex;
static thread_queue_t *_conhands = NULL;
static rwlock_t _source_shutdown_rwlock;
static void *_handle_connection(void *arg);
void connection_initialize(void)
{
if (_initialized) return;
thread_mutex_create(&_connection_mutex);
thread_mutex_create(&_queue_mutex);
thread_rwlock_create(&_source_shutdown_rwlock);
thread_cond_create(&_pool_cond);
_initialized = 1;
}
void connection_shutdown(void)
{
if (!_initialized) return;
thread_cond_destroy(&_pool_cond);
thread_rwlock_destroy(&_source_shutdown_rwlock);
thread_mutex_destroy(&_queue_mutex);
thread_mutex_destroy(&_connection_mutex);
_initialized = 0;
}
static connection_t *_create_connection(void)
{
connection_t *con;
con = (connection_t *)malloc(sizeof(connection_t));
memset(con, 0, sizeof(connection_t));
return con;
}
static unsigned long _next_connection_id(void)
{
unsigned long id;
thread_mutex_lock(&_connection_mutex);
id = _current_id++;
thread_mutex_unlock(&_connection_mutex);
return id;
}
static connection_t *_accept_connection(void)
{
int sock;
fd_set rfds;
connection_t *con;
struct timeval tv;
char *ip;
FD_ZERO(&rfds);
FD_SET(global.serversock, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 30000;
if (select(global.serversock + 1, &rfds, NULL, NULL, &tv) <= 0) {
return NULL;
}
/* malloc enough room for 123.123.123.123\0 */
ip = (char *)malloc(16);
sock = sock_accept(global.serversock, ip, 16);
if (sock >= 0) {
con = _create_connection();
con->sock = sock;
con->con_time = time(NULL);
con->id = _next_connection_id();
con->ip = ip;
return con;
}
if (!sock_recoverable(sock_error()))
WARN2("accept() failed with error %d: %s", sock_error(), strerror(sock_error()));
free(ip);
return NULL;
}
static void _add_connection(connection_t *con)
{
con_queue_t *node;
node = (con_queue_t *)malloc(sizeof(con_queue_t));
thread_mutex_lock(&_queue_mutex);
node->con = con;
node->next = _queue;
_queue = node;
thread_mutex_unlock(&_queue_mutex);
printf("connection added....\n");
}
static void _signal_pool(void)
{
thread_cond_signal(&_pool_cond);
}
static void _push_thread(thread_queue_t **queue, long thread_id)
{
/* create item */
thread_queue_t *item = (thread_queue_t *)malloc(sizeof(thread_queue_t));
item->thread_id = thread_id;
item->next = NULL;
thread_mutex_lock(&_queue_mutex);
if (*queue == NULL) {
*queue = item;
} else {
item->next = *queue;
*queue = item;
}
thread_mutex_unlock(&_queue_mutex);
}
static long _pop_thread(thread_queue_t **queue)
{
long id;
thread_queue_t *item;
thread_mutex_lock(&_queue_mutex);
item = *queue;
if (item == NULL) {
thread_mutex_unlock(&_queue_mutex);
return -1;
}
*queue = item->next;
item->next = NULL;
id = item->thread_id;
free(item);
thread_mutex_unlock(&_queue_mutex);
return id;
}
static void _build_pool(void)
{
ice_config_t *config;
int i, tid;
char buff[64];
config = config_get_config();
for (i = 0; i < config->threadpool_size; i++) {
snprintf(buff, 64, "Connection Thread #%d", i);
tid = thread_create(buff, _handle_connection, NULL, THREAD_ATTACHED);
_push_thread(&_conhands, tid);
}
}
static void _destroy_pool(void)
{
long id;
int i;
i = 0;
thread_cond_broadcast(&_pool_cond);
id = _pop_thread(&_conhands);
while (id != -1) {
thread_join(id);
_signal_pool();
id = _pop_thread(&_conhands);
}
}
void connection_accept_loop(void)
{
connection_t *con;
_build_pool();
while (global.running == ICE_RUNNING) {
con = _accept_connection();
if (con) {
_add_connection(con);
_signal_pool();
}
}
_destroy_pool();
/* wait for all the sources to shutdown */
thread_rwlock_wlock(&_source_shutdown_rwlock);
thread_rwlock_unlock(&_source_shutdown_rwlock);
}
static connection_t *_get_connection(void)
{
con_queue_t *node = NULL;
con_queue_t *oldnode = NULL;
connection_t *con = NULL;
thread_mutex_lock(&_queue_mutex);
if (_queue) {
node = _queue;
while (node->next) {
oldnode = node;
node = node->next;
}
/* node is now the last node
** and oldnode is the previous one, or NULL
*/
if (oldnode) oldnode->next = NULL;
else (_queue) = NULL;
}
thread_mutex_unlock(&_queue_mutex);
if (node) {
con = node->con;
free(node);
}
return con;
}
static void *_handle_connection(void *arg)
{
char header[4096];
connection_t *con;
http_parser_t *parser;
source_t *source;
stats_connection_t *stats;
avl_node *node;
http_var_t *var;
client_t *client;
int bytes;
while (global.running == ICE_RUNNING) {
memset(header, 0, 4096);
thread_cond_wait(&_pool_cond);
if (global.running != ICE_RUNNING) break;
/* grab a connection and set the socket to blocking */
con = _get_connection();
stats_event_inc(NULL, "connections");
sock_set_blocking(con->sock, SOCK_BLOCK);
/* fill header with the http header */
if (util_read_header(con->sock, header, 4096) == 0) {
/* either we didn't get a complete header, or we timed out */
connection_close(con);
continue;
}
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
if (httpp_parse(parser, header, strlen(header))) {
/* handle the connection or something */
if (strcmp("ice", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) != 0 && strcmp("http", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) != 0) {
printf("DEBUG: bad protocol\n");
connection_close(con);
httpp_destroy(parser);
continue;
}
if (parser->req_type == httpp_req_source) {
printf("DEBUG: source logging in\n");
stats_event_inc(NULL, "source_connections");
if (strcmp((httpp_getvar(parser, "ice-password") != NULL) ? httpp_getvar(parser, "ice-password") : "", (config_get_config()->source_password != NULL) ? config_get_config()->source_password : "") != 0) {
printf("DEBUG: bad password\n");
INFO1("Source (%s) attempted to login with bad password", httpp_getvar(parser, HTTPP_VAR_URI));
connection_close(con);
httpp_destroy(parser);
continue;
}
global_lock();
if (global.sources >= config_get_config()->source_limit) {
printf("TOO MANY SOURCE, KICKING THIS ONE\n");
INFO1("Source (%d) logged in, but there are too many sources", httpp_getvar(parser, HTTPP_VAR_URI));
connection_close(con);
httpp_destroy(parser);
global_unlock();
continue;
}
global.sources++;
global_unlock();
stats_event_inc(NULL, "sources");
source = source_create(con, parser, httpp_getvar(parser, HTTPP_VAR_URI), FORMAT_TYPE_VORBIS);
source->shutdown_rwlock = &_source_shutdown_rwlock;
sock_set_blocking(con->sock, SOCK_NONBLOCK);
thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED);
continue;
} else if (parser->req_type == httpp_req_stats) {
printf("DEBUG: stats connection...\n");
stats_event_inc(NULL, "stats_connections");
if (strcmp((httpp_getvar(parser, "ice-password") != NULL) ? httpp_getvar(parser, "ice-password") : "", (config_get_config()->source_password != NULL) ? config_get_config()->source_password : "") != 0) {
printf("DEBUG: bad password\n");
connection_close(con);
httpp_destroy(parser);
continue;
}
stats_event_inc(NULL, "stats");
/* create stats connection and create stats handler thread */
stats = (stats_connection_t *)malloc(sizeof(stats_connection_t));
stats->parser = parser;
stats->con = con;
thread_create("Stats Connection", stats_connection, (void *)stats, THREAD_DETACHED);
continue;
} else if (parser->req_type == httpp_req_play || parser->req_type == httpp_req_get) {
printf("DEBUG: client coming in...\n");
/* make a client */
client = client_create(con, parser);
stats_event_inc(NULL, "client_connections");
/* there are several types of HTTP GET clients
** media clients, which are looking for a source (eg, URI = /stream.ogg)
** stats clients, which are looking for /stats.xml
** and director server authorizers, which are looking for /GUID-xxxxxxxx (where xxxxxx is the GUID in question
** we need to handle the latter two before the former, as the latter two
** aren't subject to the limits.
*/
// TODO: add GUID-xxxxxx
if (strcmp(httpp_getvar(parser, HTTPP_VAR_URI), "/stats.xml") == 0) {
printf("sending stats.xml\n");
stats_sendxml(client);
continue;
}
global_lock();
if (global.clients >= config_get_config()->client_limit) {
if (parser->req_type == httpp_req_get) {
client->respcode = 504;
bytes = sock_write(client->con->sock, "HTTP/1.0 504 Server Full\r\nContent-Type: text/html\r\n\r\n"\
"<b>The server is already full. Try again later.</b>\r\n");
if (bytes > 0) client->con->sent_bytes = bytes;
}
client_destroy(client);
global_unlock();
continue;
}
global_unlock();
avl_tree_rlock(global.source_tree);
source = source_find_mount(httpp_getvar(parser, HTTPP_VAR_URI));
if (source) {
printf("DEBUG: source found for client\n");
global_lock();
if (global.clients >= config_get_config()->client_limit) {
if (parser->req_type == httpp_req_get) {
client->respcode = 504;
bytes = sock_write(client->con->sock, "HTTP/1.0 504 Server Full\r\nContent-Type: text/html\r\n\r\n"\
"<b>The server is already full. Try again later.</b>\r\n");
if (bytes > 0) client->con->sent_bytes = bytes;
}
client_destroy(client);
global_unlock();
continue;
}
global.clients++;
global_unlock();
if (parser->req_type == httpp_req_get) {
client->respcode = 200;
sock_write(client->con->sock, "HTTP/1.0 200 OK\r\nContent-Type: application/x-ogg\r\n");
/* iterate through source http headers and send to client */
avl_tree_rlock(source->parser->vars);
node = avl_get_first(source->parser->vars);
while (node) {
var = (http_var_t *)node->key;
if (strcasecmp(var->name, "ice-password") && !strncasecmp("ice-", var->name, 4)) {
printf("DEBUG: sending %s: %s\n", var->name, var->value);
sock_write(client->con->sock, "%s: %s\r\n", var->name, var->value);
}
node = avl_get_next(node);
}
avl_tree_unlock(source->parser->vars);
sock_write(client->con->sock, "\r\n");
sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
}
avl_tree_wlock(source->pending_tree);
avl_insert(source->pending_tree, (void *)client);
avl_tree_unlock(source->pending_tree);
}
avl_tree_unlock(global.source_tree);
if (!source) {
printf("DEBUG: source not found for client\n");
if (parser->req_type == httpp_req_get) {
client->respcode = 404;
bytes = sock_write(client->con->sock, "HTTP/1.0 404 Source Not Found\r\nContent-Type: text/html\r\n\r\n"\
"<b>The source you requested could not be found.</b>\r\n");
if (bytes > 0) client->con->sent_bytes = bytes;
}
client_destroy(client);
}
continue;
} else {
printf("DEBUG: wrong request type\n");
connection_close(con);
httpp_destroy(parser);
continue;
}
} else {
printf("DEBUG: parsing failed\n");
connection_close(con);
httpp_destroy(parser);
continue;
}
}
thread_exit(0);
return NULL;
}
void connection_close(connection_t *con)
{
sock_close(con->sock);
if (con->ip) free(con->ip);
if (con->host) free(con->host);
free(con);
}

23
src/connection.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef __CONNECTION_H__
#define __CONNECTION_H__
typedef struct connection_tag
{
unsigned long id;
time_t con_time;
long long sent_bytes;
int sock;
int error;
char *ip;
char *host;
} connection_t;
void connection_initialize(void);
void connection_shutdown(void);
void connection_accept_loop(void);
void connection_close(connection_t *con);
#endif /* __CONNECTION_H__ */

31
src/format.c Normal file
View File

@ -0,0 +1,31 @@
/* format.c
**
** format plugin implementation
**
*/
#include <stdlib.h>
#include <string.h>
#include "connection.h"
#include "refbuf.h"
#include "format.h"
#include "format_vorbis.h"
format_plugin_t *format_get_plugin(format_type_t type)
{
format_plugin_t *plugin;
switch (type) {
case FORMAT_TYPE_VORBIS:
plugin = format_vorbis_get_plugin();
break;
default:
plugin = NULL;
break;
}
return plugin;
}

40
src/format.h Normal file
View File

@ -0,0 +1,40 @@
/* format.h
**