1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-09-22 04:15:54 -04:00

sync up with recent branch tarball. No issues outstanding but some of this has

already been committed to trunk

svn path=/icecast/branches/kh/icecast/; revision=14281
This commit is contained in:
Karl Heyes 2007-12-08 05:19:45 +00:00
parent 5e08a0f6d5
commit 8d78b6e75f
93 changed files with 5873 additions and 2490 deletions

View File

@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
AUTOMAKE_OPTIONS = 1.6 foreign dist-zip
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src conf debian doc web admin win32
@ -8,7 +8,7 @@ SUBDIRS = src conf debian doc web admin win32
EXTRA_DIST = HACKING m4/acx_pthread.m4 m4/ogg.m4 \
m4/theora.m4 m4/vorbis.m4 m4/speex.m4\
m4/xiph_compiler.m4 m4/xiph_curl.m4 m4/xiph_net.m4 \
m4/xiph_types.m4 m4/xiph_xml2.m4 icecast.spec
m4/xiph_types.m4 m4/xiph_xml2.m4 m4/xiph_openssl.m4 icecast.spec
docdir = $(datadir)/doc/$(PACKAGE)
doc_DATA = README AUTHORS COPYING NEWS TODO

232
NEWS
View File

@ -1,16 +1,237 @@
Feature differences from SVN trunk
. allow for wildcards (*[] expansion) in mount-name and ban/allow files
. can limit mountpoint by outgoing bandwidth as well as a max listeners count
. can drop new listeners if server-wide bitrate is above a limit
. server xml reload, and reopen logging available via admin url
. When max-listeners reached, a HTTP 302 code can be sent to redirect
clients to alternative slave hosts.
. authenticated relays, those that match the relay user/pass, bypass the
max-listener check
. mount can filter out theora content, useful for define a local relay of a
theora+vorbis stream to have a vorbis only stream from the same source.
max-listener/bandwidth check
. mount can filter out theora content, useful for defining a local relay
of a theora+vorbis stream to have a vorbis only stream from the same source.
. multiple shoutcast source clients possible.
. a per-mount charset can be defined, translates to UTF8.
. stream-auth option for url authenticator.
. stream-auth/handlers option for url authenticator.
any extra tags are show in the conf/icecast.xml.dist file
2.3-kh27
. fix potential fserve thread race
. merge listener inline shoutcast metadata into previous block write. It is
very common and saves writing a very short packet per listener at metadata.
. reduce log memory usage when caching lines for web display
. reduce memory usage slightly on 64bit systems, in refbuf/avl storage
. be more 32/64bit clean
. fix possible corruption if reducing qblock size over reload
. get stats thread to update global outgoing_kbitrate stat, no point flooding
the stats queue.
2.3-kh26
. internal code cleanups
. fixed accidental bug which caused problems for non-ogg streams without a
mount defined
. if listener_remove used then remove queue reference before auth performed as
queue will expand. race case between auth and source thread on refcounting.
. Add error reporting with above case, makes identification easier.
2.3-kh25
. kh24 had a lockup bug on reload if auth url used.
. Don't pass listeners to the authentication queue if no auth handler defined
. auth listener_remove was not triggering if listener_add was not specified.
. make sure avl tree for mime types is cleared.
. remove pthread_exit in fserve, older glibc had a memory leak
. fix memory leak/corruption in YP handler, also make YP thread startup instead
of polling frequently.
. redo server connection lookup. Now we refcount the server connection details
from the xml, make the client refer to it. reduces locking as well
. always call the xml reload from the slave thread.
. fix memory leak over reload, when using multiple master within relay
. incorrect output appended to streamlist output for relays.
. race between stream shutdown and listener_remove could cause memory corruption
with queue buffers. Make source clear less strict.
. cleanup any fserve clients after other threads are down, removes lock race
. reduce burst limiter for listeners on sources.
. if available, log file descriptor limit allowed by kernel.
2.3-kh24
. make auth threads start/stop dynamically. reduces latency for auth'd listeners.
The handlers setting is still the maximum number of threads for that auth
. ip allow/deny list wasn't working right due to fnmatch return value.
. fix memory leak which occured on reload events.
. fix possible corruption when connection thread restarts from reload
. add <deny-agents> tag in <paths>. Drops listeners with useragents matching the
pattern in the specified file.
. internal code cleanups
2.3-kh23
. file serve thread is dynamically started/stopped, latency seems much better when
compared to a sleep/polling loop
. sometimes the empty virtual mountpoint stats were being provided instead of the
real source stats. This should finish off some of the work done earlier on stats
. Don't be case sensitive on range header in fserve parsing.
. a typo in the POST for listener_remove got by, ip and duration were not correct.
. small code cleanups from merges with trunk
. minor fix for global listeners count
. listener remove wasn't being triggered for listeners on sources.
2.3-kh22
. Implement a global outgoing_kbitrate stat, sum of all listeners on all sources.
. Small updates on the average bitrate handling
. implement a server-wide bandwidth limiter check for new listeners. If <max-bandwidth>
in <limits> is defined then new listeners can be rejected/redirected.
. drop non-gcc compiler flags from configure.in, drop _XOPEN_SOURCE as well
. send Cache control header, just in case proxies are involved
. another flash (on IE as well) hack posted on forum.
2.3-kh21
. IP ban/allow files can now use wildcard expansion to matching addresses
. allow mount= param on webroot xsl pages
. check for missing type in mount authentcation
. fix corruption if listen socket failed.
. if using a change owner setup, do the listener socket setup early (for root privs)
and prevent reload from altering the sockets.
. lag calculation incorrect on shoutcast style metadata inserts, fixed
. fix potential lock held in admin for certain error cases
2.3-kh20
. send http 302 response if auth url returns a location: header
. allow for any number of listen-sockets
. combine listener/connection thread into one thread
. use short timeout period in connection thread if requests are pending
. make primary thread be the slave thread which starts the connection thread, this
allows for the connection thread to be restarted with changes to listening sockets
. cleanup work from merging to trunk
2.3-kh19
. fix segv possibility in mp3 metadata
. in xml stats, make listeners id to be an attribute of <listener>
. cleanups from merge to trunk work.
. added per listener lag count, which shows in stats.
. added ip= in POST for listener_remove
. Use spin locks (if available) for a few short held locks, falls back to mutexes.
. missed some socket setup with the ipv4/ipv6 code. You don't have to wait when
restarting servers
2.3-kh18
. fix a segv case that occurs in a certain fallback mount case, this was introduced
recently when max listeners/bandwidth was worked on.
. fix possible crash case for a missing mime types file
. fix segv case added in kh16, on demand relays without a mount.
. win32 build should now handle wildcard mountpoints.
2.3-kh17
. fix segv issue in auth, has been triggered with htpasswd
. if inactive mounts with active fallbacks are removed from the xml, they will
now be removed from stats.
. small build fix for ipv6 update in kh16.
. memory leak fix in relay handling and mime types, 2 other minor ones elsewhere
. do http auth extraction for incoming clients earlier, username wasn't logging in
certain cases.
. mangeauth was not hooked into correctly from the last admin update.
. change streamlist trigger to be based on time instead of a counter. With the
shorter delay in slave, this was causing the streamlist to occur too often.
. http 302 wasn't sent 100% of the time. The redirection list is now only dropped
over XML reload.
2.3-kh16
. Implement max-bandwidth mount setting, this can be used to limit listener numbers
instead of/as well as the max-listeners option.
. Add patch from Gilou for source address bind for relays. Adds <bind> option to
<relay> and <master-bind> option for slave setups.
. Update to IPv6 handling in net/ code, no need to seperately specify an IPv6
socket for listening.
. Actually make the listener details appear on the stats pages if the mount is
specified (does not apply to listclients). Most won't notice this unless they
parse those xml stats for listener information.
. Handle xspf generation like m3u but uses an xsl page for layout
. Small changes in a few places, avg bitrate calculations, lower triggers for
listener send with fallback files.
. Internal duplication removed, source structures contained limits which were also
held within mount structures.
2.3-kh15
. fix for possible race (from kh14) leading to lockup when many streams are
starting up at the same time.
. don't provided listener details to all streams via admin access, only when
a mountpoint is provided. Should help in cases where there's large number
of listeners on several streams
2.3-kh14
. add <file-seekable> boolean to <mount>, default is true
. return 403 (not 401) response if the server in auth url is not contactable
for new listeners
. increase scope for source locks, certain initialisations can have race
conditions which leads to unpredictable results.
. minor fix for listclient.xsl, the case of listener stat names were changed.
. minor cleanup of stats updating
2.3-kh13
. Allow for wildcard matching (eg /*.mp3) in mount definitions on systems that
support it.
. for streamlist (slave requests), get the list of non-hidden streams from the
stats instead of the source or mount lists. This allows for streams without
a <mount> to be exported and also lists inactive mountpoints with active
fallbacks.
. Allow for specifying an id= parameter with listclients so that specific
listener details can be queried.
. ship a config.h used for a VC6 build in win32
2.3-kh12
. relays handle 302 redirection from master server.
. fix for possible crash with incoming shoutcast style source clients.
. changes to web/admin pages. while /status.xsl still exists, better to refer
to /index.html for web and /admin.html for admin.
. Filled in a few more global static stats, updated on reload xml
2.3-kh11
. added extra header for shoutcast source clients. some may need it.
. missed <server> tag in <relay> <master> from previous release
. changes to admin interface, aliases now work, reduce code size.
. relay page shows all relay masters, not just the first one.
2.3-kh10
. race fix for relays, hard to trigger but can cause a crash
. add <mimetypes> xml tag for locating file with mime types defined
. added warning messages for xml tags with no content, but don't prevent
icecast from starting.
2.3-kh9
. try a lower content length value for flash clients, some combinations of
browsers still have issues.
. add <admin_metadata_only> (boolean) to <mount> for ignoring incoming vorbis
comments. Only takes the artist= and title= settings from /admin eg
/admin/metadata?artist=abc&title=xyz&mount=%2Flive.ogg
. allow multiple <master> tags in <relay>, allows for switching between multiple
masters. Requires a fallback to keep listeners connected over switch though.
. add <retry-delay> setting for <relay>, default is <master-update-interval>
. Add <allow-ip> setting. similar to banfile but only allow connections from
certain IPs
. fix on win32 previous build, truncation bug reappeared on large web requests.
. minor build update for acx_pthread
. assume default pass-through (mp3/aac etc) stream charset ISO8859-1 not utf8
. added macro which enables the writing of large files (log/dump).
2.3-kh8a
. build had access log logging without IPs
2.3-kh8
. add "handlers" option for url auth, defines multiple auth threads for
processing incoming clients. Default is 1 per <authenication type="url">.
Internally the client data doesn't hold onto the auth_t now.
. banfile option, drops connections whose IP is listed in the banfile
. Added check for a still connected listener before auth attempted, no point
in doing slow url auth if listener has already disconnected.
. fix buildm3u for auth without a source stream.
. change content-length for flash to 319324133 to see if that works better
for IE7
. Added <accesslog_ip> boolean for runtime config of logging IPs, default on
. expand on relay failures messages to include the server and mountpoint
. fix possible race condition with shoutcast style connections.
. decrease frequency of connection thread wakeup.
. make connection thread sleep if accept fails, prevents busy loop
. Don't set CURLOPT_PASSWDFUNCTION if libcurl does not define it
. update debian directory (need feedback on that)
2.3-kh7
. fix slow memory leak on metadata updates if <charset> was defined
@ -18,7 +239,6 @@ Feature differences from SVN trunk
. fix segv on 0-value duration in source, minimum is 2 secs (default 60)
. fix small memory leak on xsl page requests.
. stats update. kbytes used, and frequency of read kbytes reduced.
. upate debian directory
. change vorbis stream rebuilding to flush at 1 sec at the most.
. minor changes to win32, buffer size issues for stats and xml cleanup
causes a crash in libxml2

View File

@ -4,6 +4,6 @@ AUTOMAKE_OPTIONS = foreign
admindir = $(pkgdatadir)/admin
dist_admin_DATA = listclients.xsl listmounts.xsl moveclients.xsl response.xsl \
logs.xsl showlog.xsl \
logs.xsl showlog.xsl xspf.xsl \
stats.xsl manageauth.xsl updatemetadata.xsl managerelays.xsl

View File

@ -12,8 +12,6 @@
<body bgcolor="#000" topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
<div class="main">
<h1>Listener Stats</h1>
<iframe scrolling="no" frameborder="0" width="100%" src="/adminbar.html" />
<div class="roundcont">
<div class="roundtop">
@ -46,22 +44,26 @@
</td></tr>
</table>
<br />
<table cellspacing="1" border="1" bordercolor="#C0C0C0" >
<tr>
<table cellpadding="5" border="1" bordercolor="#C0C0C0" >
<thead>
<td ><center><b>IP</b></center></td>
<td ><center><b>Connected For</b></center></td>
<td ><center><b>Connected (seconds)</b></center></td>
<td ><center><b>Lag (bytes)</b></center></td>
<td ><center><b>User Agent</b></center></td>
<td ><center><b>Action</b></center></td>
</tr>
</thead>
<tbody>
<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
<xsl:for-each select="listener">
<tr>
<td align="center"><xsl:value-of select="IP" /><xsl:if test="Username"> (<xsl:value-of select="Username" />)</xsl:if></td>
<td align="center"><xsl:value-of select="Connected" /> seconds</td>
<td align="center"><xsl:value-of select="UserAgent" /></td>
<td align="center"><a href="killclient.xsl?mount={$themount}&amp;id={ID}">Kick</a></td>
<td align="center"><xsl:value-of select="ip" /><xsl:if test="username"> (<xsl:value-of select="username" />)</xsl:if></td>
<td align="center"><xsl:value-of select="connected" /></td>
<td align="center"><xsl:value-of select="lag" /></td>
<td align="center"><xsl:value-of select="useragent" /></td>
<td align="center"><a href="killclient.xsl?mount={$themount}&amp;id={@id}">Kick</a></td>
</tr>
</xsl:for-each>
</tbody>
</table>
<br />
<br />

View File

@ -12,8 +12,6 @@
<body>
<div class="main">
<h1>Active Mountpoints</h1>
<iframe frameborder="0" scrolling="no" height="50" src="/adminbar.html" />
<div class="roundcont">
<div class="roundtop">

View File

@ -11,8 +11,6 @@
<body>
<div class="main">
<h1>Icecast2 logs</h1>
<iframe frameborder="0" scrolling="no" height="50" src="/adminbar.html" />
<div class="roundcont">
<div class="roundtop">

View File

@ -12,8 +12,6 @@
<body bgcolor="#000" topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
<div class="main">
<h1>Manage Listeners</h1>
<iframe scrolling="no" frameborder="0" width="100%" src="/adminbar.html" />
<div class="roundcont">
<div class="roundtop">

View File

@ -12,8 +12,6 @@
<body>
<div class="main">
<h1>Relay Management</h1>
<iframe frameborder="0" scrolling="no" height="50" src="/adminbar.html" />
<div class="roundcont">
<div class="roundtop">
@ -33,10 +31,13 @@
</xsl:choose>
</h3>
<table border="0" cellpadding="4">
<tr> <td>server</td> <td class="streamdata"> <xsl:value-of select="server" /></td> </tr>
<tr> <td>port</td> <td class="streamdata"> <xsl:value-of select="port" /></td> </tr>
<tr> <td>mountpoint</td> <td class="streamdata"> <xsl:value-of select="mount" /></td> </tr>
<xsl:for-each select="master">
<tr><td>Master</td> <td class="streamdata"> <xsl:value-of select="server" /></td>
<td class="streamdata"> <xsl:value-of select="port" /></td>
<td class="streamdata"> <xsl:value-of select="mount" /></td></tr>
</xsl:for-each>
<tr> <td>on demand</td> <td class="streamdata"> <xsl:value-of select="on_demand" /></td> </tr>
<tr> <td>slave relay</td> <td class="streamdata"> <xsl:value-of select="from_master" /></td> </tr>
</table>
<br />
<br></br>

View File

@ -12,8 +12,6 @@
<body bgcolor="#000" topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
<div class="main">
<h1>Moving Listeners From (<xsl:value-of select="current_source" />)</h1>
<iframe scrolling="no" frameborder="0" width="100%" src="/adminbar.html" />
<xsl:variable name = "currentmount" ><xsl:value-of select="current_source" /></xsl:variable>
<div class="roundcont">

View File

@ -12,8 +12,6 @@
<body>
<div class="main">
<h1>Icecast Server Response</h1>
<iframe frameborder="0" scrolling="no" height="50" src="/adminbar.html" />
<div class="roundcont">
<div class="roundtop">

View File

@ -12,8 +12,6 @@
<body bgcolor="#000" topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
<div class="main">
<h1>Icecast2 Admin</h1>
<iframe frameborder="0" scrolling="no" height="50" src="/adminbar.html" />
<br />

View File

@ -12,8 +12,6 @@
<body bhcolor="#000" topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
<div class="main">
<h1>Update Metadata</h1>
<iframe scrolling="no" frameborder="0" width="100%" src="/adminbar.html" />
<div class="roundcont">
<div class="roundtop">
@ -32,6 +30,7 @@
</table>
<input type="hidden" name="mount" value="{@mount}"/>
<input type="hidden" name="mode" value="updinfo"/>
<input type="hidden" name="charset" value="UTF-8"/>
</form>
<br />

73
admin/xspf.xsl Normal file
View File

@ -0,0 +1,73 @@
<!--
XSPF xslt stylesheet for Icecast 2.3.1 and above
Copyright (C) 2007 Thomas B. Ruecker, tbr@ruecker-itk.de
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
You should have received a copy of the GNU General Public License
along with this program; if not, write to the
Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-->
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
<xsl:output omit-xml-declaration="no" media-type="application/xspf+xml"
method="xml" indent="yes" encoding="UTF-8" />
<xsl:template match = "/icestats" >
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<title><xsl:value-of select="server" /></title>
<creator><xsl:value-of select="server" /></creator>
<trackList >
<!-- end of "header" -->
<xsl:for-each select="source">
<!-- do we need to do something about streams that need auth?-->
<track>
<location><xsl:value-of select="listenurl" /></location>
<xsl:if test="artist"><creator><xsl:value-of select="artist" /></creator></xsl:if>
<title><xsl:value-of select="title" /></title>
<!-- The <xsl:text>\n</xsl:text> elements in the following part are used
to enforce linebreaks this format seems to be expected by clients -->
<annotation>
<xsl:if test="server_name">Stream Title: <xsl:value-of select="server_name" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="server_description">Stream Description: <xsl:value-of select="server_description" /></xsl:if>
Content Type:<xsl:value-of select="server_type" /><xsl:text>
</xsl:text>
<xsl:if test="bitrate">Bitrate: <xsl:value-of select="bitrate" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="quality">Quality: <xsl:value-of select="quality" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="video_quality">Video Quality: <xsl:value-of select="video_quality" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="frame_size">Framesize: <xsl:value-of select="frame_size" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="frame_rate">Framerate: <xsl:value-of select="frame_rate" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="listeners">Current Listeners: <xsl:value-of select="listeners" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="listener_peak">Peak Listeners: <xsl:value-of select="listener_peak" /><xsl:text>
</xsl:text></xsl:if>
<xsl:if test="genre">Stream Genre: <xsl:value-of select="genre" /></xsl:if>
</annotation>
<xsl:if test="server_url"><info><xsl:value-of select="server_url" /></info></xsl:if>
</track>
</xsl:for-each>
</trackList>
</playlist>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,4 +1,7 @@
<icecast>
<location>UK</location>
<admin>webmaster@localhost</admin>
<limits>
<clients>100</clients>
<sources>2</sources>
@ -10,6 +13,9 @@
specific on how much to burst. Most people won't need to
change from the default 64k. Applies to all mountpoints -->
<burst-size>65535</burst-size>
<!--
<max-bandwidth>100M</max-bandwidth>
-->
</limits>
<authentication>
@ -36,14 +42,14 @@
listings. -->
<hostname>localhost</hostname>
<!-- You can use these two if you only want a single listener -->
<!-- port to use when talking to YP etc -->
<!--<port>8000</port> -->
<!--<bind-address>127.0.0.1</bind-address>-->
<!-- You may have multiple <listener> elements -->
<listen-socket>
<port>8000</port>
<!-- <bind-address>127.0.0.1</bind-address> -->
<!-- use <shoutcast-mount> in here to implicitly define port n+1 -->
</listen-socket>
<!--
<listen-socket>
@ -72,8 +78,7 @@
the details passed are based on <hostname> and <port> -->
<!--<master-redirect>1</master-redirect>-->
<!-- The maximum nuber of slaves that can register for new listener
redirection. -->
<!-- The maximum nuber of slaves that can register for new listener redirection. -->
<!--<max-redirect-slaves>10</max-redirect-slaves>-->
<!-- Relays. State connection information, and by default
@ -87,20 +92,41 @@
<mount>/example.ogg</mount>
<local-mount>/different.ogg</local-mount>
<on-demand>1</on-demand>
<retry-delay>30</retry-delay>
<relay-shoutcast-metadata>0</relay-shoutcast-metadata>
</relay>
-->
<!-- Allow multiple master servers to be specified, tries each one in turn.
<relay>
<local-mount>/stream.mp3</local-mount>
<master>
<server>a.b.c.d</server>
<port>8000</port>
<mount>/a</mount>
</master>
<master>
<server>a.b.c.d</server>
<port>80</port>
<mount>/</mount>
</master>
</relay>
-->
<!-- Only define a <mount> section if you want to use advanced options,
like alternative usernames or passwords
With a master/slave setup you need to define a mount in the master
or else the server will assume that the stream is not to be made
available to the slave.
<mount>
<mount-name>/example-complex.ogg</mount-name>
<mount-name>/*.ogg</mount-name>
<username>othersource</username>
<password>hackmemore</password>
<max-listeners>1</max-listeners>
<max-bandwidth>1000k</max-bandwidth>
<file-seekable>0</file-seekable>
<dump-file>/tmp/dump-example1.ogg</dump-file>
<burst-size>65536</burst-size>
<fallback-mount>/example2.ogg</fallback-mount>
@ -115,6 +141,7 @@
</authentication>
<on-connect>/home/icecast/bin/stream-start</on-connect>
<on-disconnect>/home/icecast/bin/stream-stop</on-disconnect>
<file-seekable>0</file-seekable>
</mount>
-->
<!-- other auth possibilities include running a command
@ -134,13 +161,14 @@
remove is passed id, mount, user, pass, duration
<authentication type="url">
state username/password if url requires it
<option name="username" value="admin"/>
<option name="password" value="hackme"/>
<option name="add" value="http://myauthserver.com/scripts/add_listener.php"/>
<option name="remove" value="http://myauthserver.com/scripts/del_listener.php"/>
<option name="handlers" value="3" />
<option name="stream_auth" value="http://myauthserver.com/scripts/auth_mount.php"/>
<option name="mount_add" value="http://myauthserver.com/scripts/add_mount.php"/>
<option name="mount_remove" value="http://myauthserver.com/scripts/del_mount.php"/>
<option name="listener_add" value="http://myauthserver.com/scripts/add_listener.php"/>
<option name="listener_remove" value="http://myauthserver.com/scripts/del_listener.php"/>
</authentication>
</mount -->
@ -163,7 +191,13 @@
<webroot>@pkgdatadir@/web</webroot>
<adminroot>@pkgdatadir@/admin</adminroot>
<!-- <pidfile>@pkgdatadir@/icecast.pid</pidfile> -->
<!-- <ssl_certificate>@pkgdatadir@/icecast.pem</ssl_certificate> -->
<!-- <ssl-certificate>@pkgdatadir@/icecast.pem</ssl-certificate> -->
<!-- <deny-ip>/path/to/file-with-IPs</deny-ip> -->
<!-- <allow-ip>/path/to/file-with-IPs</allow-ip> -->
<!-- <deny-agents>/path/to/file-with-useragents</deny-agents> -->
<!-- location of mime types files used for file serving -->
<!-- <mime-types>/etc/mime.types</mime-types> -->
<!-- Aliases: treat requests for 'source' path as being for 'dest' path
May be made specific to a port or bound address using the "port"
@ -176,14 +210,15 @@
this example will redirect all requests for http://server:port/ to
the status page
-->
<alias source="/" dest="/status.xsl"/>
<alias source="/" dest="/index.html"/>
</paths>
<logging>
<accesslog>access.log</accesslog>
<errorlog>error.log</errorlog>
<!-- <accesslog_ip>0<accesslog_ip> -->
<!-- <playlistlog>playlist.log</playlistlog> -->
<loglevel>4</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
<loglevel>3</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
<logsize>10000</logsize> <!-- Max size of a logfile -->
<!-- If logarchive is enabled (1), then when logsize is reached
the logfile will be moved to [error|access|playlist].log.DATESTAMP,

View File

@ -25,7 +25,7 @@
<logdir>@localstatedir@/log/@PACKAGE@</logdir>
<webroot>@pkgdatadir@/web</webroot>
<adminroot>@pkgdatadir@/admin</adminroot>
<alias source="/" dest="/status.xsl"/>
<alias source="/" dest="/index.html"/>
</paths>
<logging>
<accesslog>access.log</accesslog>

View File

@ -1,6 +1,6 @@
AC_INIT([Icecast], [2.3-kh7], [karl@xiph.org])
AC_INIT([Icecast], [2.3-kh27], [karl@xiph.org])
AC_PREREQ(2.54)
AC_PREREQ(2.59)
AC_CONFIG_SRCDIR(src/main.c)
dnl Process this file with autoconf to produce a configure script.
@ -11,36 +11,14 @@ AM_MAINTAINER_MODE
AC_PROG_CC
AC_CANONICAL_HOST
AC_PROG_LIBTOOL
AC_SYS_LARGEFILE
dnl Set some options based on environment
DEBUG="-g"
if test -z "$GCC"; then
XIPH_CPPFLAGS="-D_REENTRANT"
case $host in
*-*-irix*)
XIPH_CPPFLAGS="$XIPH_CPPFLAGS -w -signed"
PROFILE="-p -g3 -O2 -signed -D_REENTRANT"
;;
*-*-solaris*)
XIPH_CFLAGS="-xO4 -xcg92"
XIPH_CPPFLAGS="$XIPH_CPPFLAGS -v -w -fsimple -fast"
PROFILE="-xpg -g -Dsuncc"
;;
*)
XIPH_CFLAGS="-O"
PROFILE="-g -p"
;;
esac
case "$host" in
# for system header breakage
*bsd* | *irix*)
;;
*) AC_DEFINE([_XOPEN_SOURCE], 600, [Define if you have POSIX and XPG specifications])
;;
esac
PROFILE="-g -p"
else
XIPH_CPPFLAGS="-Wall -ffast-math -fsigned-char"
PROFILE="-pg -g"
@ -55,7 +33,7 @@ dnl Checks for header files.
AC_HEADER_STDC
AC_HEADER_TIME
AC_CHECK_HEADERS([alloca.h])
AC_CHECK_HEADERS([alloca.h fnmatch.h])
AC_CHECK_HEADERS(pwd.h, AC_DEFINE(CHUID, 1, [Define if you have pwd.h]),,)
AC_CHECK_HEADERS(unistd.h, AC_DEFINE(CHROOT, 1, [Define if you have unistd.h]),,)
@ -63,9 +41,10 @@ dnl Checks for typedefs, structures, and compiler characteristics.
XIPH_C__FUNC__
dnl Check for types
AC_TYPE_OFF_T
dnl Checks for library functions.
AC_CHECK_FUNCS(localtime_r poll atoll strtoll)
AC_CHECK_FUNCS([localtime_r poll atoll strtoll getrlimit])
AC_SEARCH_LIBS(nanosleep, rt posix4,
AC_DEFINE(HAVE_NANOSLEEP, 1, [Define if you have nanosleep]))
XIPH_NET
@ -140,17 +119,8 @@ XIPH_PATH_OPENSSL([
[ AC_MSG_NOTICE([SSL disabled!])
])
# don't log ip's in the access log
AC_ARG_ENABLE([log-ip],
AC_HELP_STRING([--disable-log-ip],[disable logging of IP's in access log]),
enable_logging_ip="$enableval",
enable_logging_ip="yes"
)
if test x$enable_logging_ip = xyes; then
AC_DEFINE([HAVE_LOGGING_IP],,[Define to log IP to access log])
fi
ICECAST_OPTIONAL="$ICECAST_OPTIONAL auth_cmd.o"
AC_DEFINE([MIMETYPESFILE],"/etc/mime.types", [Default location of mime types file])
dnl Make substitutions

40
debian/README.Debian vendored
View File

@ -1,12 +1,48 @@
icecast2 for Debian
-------------------
In relation to the comment below by Jonas Smedegaard and chroot issues,
I've modified the debian package so that it will install xsl pages as
well as icecast.xml under /usr/share/icecast2, and symlink from there
to /etc/icecast2.
I've no idea why it would be necessary to symlink admin/ and web/ into
/etc/icecast2, but the config file icecast.xml should be there at least
as it's a standard location for config files.
However the *real* file now lives in /usr/share/icecast2/etc/icecast2/icecast.xml
because it must be available to the server when it runs in a jail (chroot)
otherwise config reload won't work as /etc/icecast2 would normally be outside
the jail.
The only issue pending of solution is the resolver within the jail,
which will be required mostly for the YP servers name resolution.
I'm researching this right now.
-- Rama <rama@r23.cc> Sun, 2 Jul 2006 10:24:43 +0200
In the Debian packaging the configuration files have been symlinked from
the upstream location below /usr/share to /etc. This is needed to
satisfy FHS (/usr/share are for static content only).
If running icecast2 in a chroot environment, beware that the symlinks to
/etc will break. A possible (untestet!) solution might be to manually
put the configuration files back below /usr/share - and revert the hack
again before updating the package!
-- Jonas Smedegaard <dr@jones.dk> Thu, 20 May 2004 21:04:27 +0200
It is recommended to run icecast under a dedicated user account, which only
has access to write the log files. The Debian package creates such an
account, named 'icecast', and uses it by default, but you are free to
account, named 'icecast2', and uses it by default, but you are free to
reconfigure it and remove the account.
Edit /etc/default/icecast2 to change the init-script configuration.
-- Keegan Quinn <ice@thebasement.org>
It is possible (but discouraged for security reasons) to bind to a
priviledged port (like standard web port 80). Edit /etc/init.d/icecast2
to not change userid and instead set the correct userid and group in
/etc/icecast2/icecast2.xml. Beware that this way you rely on the
icecast2 binary to properly drop priviledges (instead of the much more
thoroughly audited start-stop-daemon). Thanks to Jürgen A. Erhard
<jae@jerhard.org> for the tip.
-- Keegan Quinn <ice@thebasement.org>

214
debian/changelog vendored
View File

@ -1,3 +1,216 @@
icecast2 (2.3-kh6-2) stable; urgency=low
* Non-maintainer unofficial branch upload.
* Fixed debian/rules and debian/icecast2.postinst so that the xsl
pages get installed in /usr/share/icecast2 and symlink them to
/etc/icecast2 (is that symlink useful?) so that chroot setup
is more straight forward.
* /etc/icecast2/icecast.xml becomes a symlink to
/usr/share/icecast2/etc/icecast2/icecast.xml for the same reason.
-- Rama <rama@r23.cc> Fri, 30 Jun 2006 23:13:56 +0200
icecast2 (2.3-kh6-1) stable; urgency=low
* Non-maintainer unofficial branch upload.
* Depends on libtheora > 1.0alpha6
* NOTE: this is not considered stable but rather experimental
-- Rama <rama@r23.cc> Thu, 25 May 2006 19:12:39 +0200
icecast2 (2.2.0-1) unstable; urgency=low
* New upstream release. Closes: bug#286739 (thanks - again - to Andre
Tomt <andre@tomt.net>).
* Debian subdir is again stripped from tarball, but autotools patching
(to aboid complaints about the missing dir) is now in diff. Updated
note in debian/copyright.
* Build-depend on libtheora-dev (current version is too old but as
soon as libtheora is updated it will then get built in).
* Updated source location in debian/copyright and debian/watch.
* Correct typo in long description.
-- Jonas Smedegaard <dr@jones.dk> Wed, 29 Dec 2004 15:04:17 +0100
icecast2 (2.1.0-1) unstable; urgency=medium
* New upstream release. Closes: bug#279869 (thanks to Andre Tomt
<andre@tomt.net>).
* Strip annoying debian subdir from upstream source.
* Update debian/copyright:
+ License is included now (no need to refer to CVS).
+ Remove stray repeated license above licensing section.
+ Add note about tarball not being pristine (and explain why).
+ Use capital "I" in initial introduction to upstream name.
* Use generic (but unofficial) buildinfo cdbs snippet.
* Drop cleaning up conf/icecast.xml.dist (handled properly upstream
now).
* Set urgency=medium to hopefully reach sarge.
* Correct README.Debian to mention user "icecast2" (not "icecast").
* Move and symlink stylesheet to /etc (similar to xslt files).
-- Jonas Smedegaard <dr@jones.dk> Sun, 7 Nov 2004 15:52:50 +0100
icecast2 (2.0.2.debian-3) unstable; urgency=high
* Fix wrong space in build-depends.
* Set urgency=high to hopefully get this compiled (even for sparc)
in time for sarge release.
-- Jonas Smedegaard <dr@jones.dk> Tue, 26 Oct 2004 14:23:04 +0200
icecast2 (2.0.2.debian-2) unstable; urgency=high
* Include "endscript" in logrotate rule. Closes: bug#274823 (thanks to
Jose Antonio <jose@shaolin.homeip.net>).
* Set urgency=high to still push earlier security fix.
-- Jonas Smedegaard <dr@jones.dk> Mon, 4 Oct 2004 11:00:31 +0200
icecast2 (2.0.2.debian-1) unstable; urgency=high
* New upstream release.
+ Fixes upstream announced security bug.
+ Set urgency=high due to the above.
+ Closes: bug#274320 (thanks to Jeroen Wolffelaar
<jeroen@wolffelaar.nl>).
+ Again - strip non-free win32/ResizableDialog.* from source.
+ While we are at it, move upstream debian subdirectory off to
dist/debian. Hack configure and configure.in to not mess around.
* Update location of upstream source in copyright and watch file.
* Use more flexible regexp in watch file.
* Rename Debian NEWS file in source to get recognized automativally.
* Devine man page within rules file.
* Drop unneeded preinst (from a time before official Debian where it
did not run properly as a daemon?).
* Build-depend on libcurl3-dev, and on the virtual package libcurl-dev
only as fallback (not mandatory, but aptitude chokes and so will the
build daemons as well, I suppose).
* Reload (which does a sighup) daemon after logrotate. Closes:
bug#265301 (thanks to David Pashley <david@davidpashley.com>).
-- Jonas Smedegaard <dr@jones.dk> Sat, 2 Oct 2004 11:27:14 +0200
icecast2 (2.0.1.debian-3) unstable; urgency=low
* Tolerate failure to remove icecast group on purge (it is used by
other packages as well, and was badly handled in older unstable
packages). This closes: Bug#246263 (thanks to Pete de Zwart
<dezwart@froob.net>).
* Fix logrotate script. Closes: Bug#249404, #255430 (thanks
to Julien Cristau <jcristau@ens-lyon.fr> and Mykola A. Nickishov
<mn@mn.com.ua>).
* Mention upstream website in long description.
* Build-depend on autotools-dev to let cdbs do clever autotools magic.
* Stylistic improvements to debian/rules:
+ Add copyright notice and editor hints at top.
+ Use only (cdbs-)generic make targets.
* Build-depend generically on libxslt-dev and libcurl-dev (instead of
libxslt1-dev and libcurl2-dev).
-- Jonas Smedegaard <dr@jones.dk> Wed, 7 Jul 2004 09:32:56 +0200
icecast2 (2.0.1.debian-2) unstable; urgency=low
* Really add ChangeLog from 2.0.0.
-- Jonas Smedegaard <dr@jones.dk> Thu, 20 May 2004 23:03:22 +0200
icecast2 (2.0.1.debian-1) unstable; urgency=medium
* New upstream release (thanks to Ian Kumlien <pomac@vapor.com>):
+ According to announcement on website, it "fixes a overflow buffer
which can cause server crashes under certain circumstances" so set
urgency=medium (the code change is one line only).
+ Again, remove the non-free win32/ResizableDialog.* as it is
still(!) distributed with official source.
+ Add ChangeLog from 2.0.0 missing from current release.
* Register with (and recommend) logrotate. Closes: Bug#299404 (thanks
to Julien Cristau <jcristau@ens-lyon.fr>).
* Add note to README.Debian about chroots not working with symlinks
due to FHS requirements of configuration files located below /etc.
Closes: Bug#250056 (thanks to Ian Kumlien <pomac@vapor.com>).
* Standards-Version: 3.6.1 (no changes needed).
* Explicitly note version in watch file, and add it to TODO.Debian.
* Add comment to watch file hinting on how to use it.
-- Jonas Smedegaard <dr@jones.dk> Thu, 20 May 2004 21:40:03 +0200
icecast2 (2.0.0.debian-1) unstable; urgency=low
* Re-release with non-free files (unused with Debian) stripped from
source:
+ Remove non-free win32/ResizableDialog.* from source, and remove
its copyright and licensing info from debian/copyright.
+ Add to debian/copyright GPL info taken from newer CVS, and email
from upstream to BTS permitting it to be used also with this
earlier release.
+ This closes: Bug#229720, thanks to upstream and Steve Langasek
<vorlon@debian.org>.
* Add TODO.Debian with reminder to clean this mess later.
-- Jonas Smedegaard <dr@jones.dk> Sun, 28 Mar 2004 16:02:27 +0200
icecast2 (2.0.0-2) unstable; urgency=low
* Add group if non-existing.
-- Jonas Smedegaard <dr@jones.dk> Mon, 26 Jan 2004 16:07:23 +0100
icecast2 (2.0.0-1) unstable; urgency=low
* New upstream release. Closes: Bug#223645, thanks to Nicholas Humfrey
<njh@ecs.soton.ac.uk>.
* Use upstream long description, and rearrange short description a
bit.
* Rewrite debian/copyright:
+ Note the upstream package name.
+ Drop Debian-related info also in debian/changelog.
+ Update location of upstream source.
+ Replace general copyright and license info (where was it found?
See bug#229720) with that of individual files where provided.
* Update debian/watch with new location.
* Use username icecast2 (instead of icecast also used in the package
icecast-server). Add NEWS.Debian with info on the change. Closes:
bug#215671, #226807, thanks to Michael Deegan
<debbts@cnspc18.murdoch.edu.au> and Robin Lee Powell
<rlpowell@digitalkingdom.org>.
* Make sure /etc/icecast2 is owned by icecast2 and not world readable.
Closes: bug#210860, thanks to Frank Barknecht <fbar@footils.org>.
* Install NEWS again (now that NEWS and ChangeLog are different).
* Build-depend on libcurl2-dev (again, and hope it works now...).
Closes: Bug#222274 thanks to Nicholas Humfrey <njh@ecs.soton.ac.uk>.
* Let icecast2 go into background by itself (using -b). Closes:
Bug#204061 (and add the actual content of the bugreport - how to
bind to a priviledged port by starting as root - to README.Debian),
thanks to Jürgen A. Erhard <jae@jerhard.org>.
* Let "configure --program-transform-name" rename icecast to icecast2.
* Let debhelper create /var/log/icecast2/.
* Keep debian/rules comments from showing during build.
-- Jonas Smedegaard <dr@jones.dk> Mon, 26 Jan 2004 06:30:26 +0100
icecast2 (1.9+2.0beta3-1) unstable; urgency=low
* New upstream release.
* Taking over maintainership. When Keegan some day comes through the
NM process he can take over maintainance.
* ChangeLog is provided upstream now, so use that (in favor of NEWS).
-- Jonas Smedegaard <dr@jones.dk> Tue, 16 Dec 2003 22:02:25 +0100
icecast2 (1.9+2.0alphasnap2+20030802-1.2) unstable; urgency=low
* Another sponsor-NMU (forgot to force including source).
-- Jonas Smedegaard <dr@jones.dk> Sun, 17 Aug 2003 10:25:16 +0200
icecast2 (1.9+2.0alphasnap2+20030802-1.1) unstable; urgency=low
* NMU by sponsor.
-- Jonas Smedegaard <dr@jones.dk> Sun, 17 Aug 2003 01:25:22 +0200
icecast2 (1.9+2.0alphasnap2+20030802-1) unstable; urgency=low
* Added a 'watch' file to automate tracking of updates.
@ -195,4 +408,3 @@ icecast2 (0.00.cvs030315-0.1) unstable; urgency=low
* Initial Release.
-- Keegan Quinn <ice@thebasement.org> Sun, 16 Mar 2003 13:45:23 -0800

24
debian/control vendored
View File

@ -1,22 +1,20 @@
Source: icecast2
Section: sound
Priority: optional
Maintainer: Keegan Quinn <ice@thebasement.org>
Build-Depends: cdbs, debhelper (>> 4.1.0), dh-buildinfo, libogg-dev (>> 1.0.0), libvorbis-dev (>> 1.0.0), libxslt1-dev, libxml2-dev
Uploaders: Jonas Smedegaard <dr@jones.dk>
Standards-Version: 3.6.0
Maintainer: Rama <rama@r23.cc>
Build-Depends: cdbs, autotools-dev, debhelper (>> 4.1.0), dh-buildinfo, libogg-dev (>> 1.0.0), libvorbis-dev (>> 1.0.0), libxslt-dev, libxml2-dev, libcurl3-dev | libcurl-dev, libtheora-dev (>= 0.0.0.alpha6)
Standards-Version: 3.6.1
Package: icecast2
Architecture: any
Depends: ${shlibs:Depends}
Recommends: ices2
Description: streaming Ogg Vorbis/MP3 media server
Icecast is an audio broadcasting system. It can stream music in both
MPEG 1 Layer 3 (MP3) and Ogg Vorbis formats, supports multiple
streams on a single port through the use of "mountpoints," includes
web-based status and management interfaces, and has many other
advanced features.
Description: Ogg Vorbis and MP3 streaming media server
Icecast is a streaming media server which currently supports Ogg
Vorbis and MP3 audio streams. It can be used to create an Internet
radio station or a privately running jukebox and many things in
between. It is very versatile in that new formats can be added
relatively easily and supports open standards for communication and
interaction.
.
Many standard audio players can connect and listen to Icecast-hosted
streams, since it's based on Nullsoft's Shoutcast protocol and HTTP.
Website: http://www.icecast.org/

104
debian/copyright vendored
View File

@ -1,26 +1,96 @@
This package was debianized by Keegan Quinn <ice@thebasement.org> on
Sun, 16 Mar 2003 13:45:23 -0800.
This is Icecast 2.x packaged for Debian.
It was retrieved from http://www.xiph.org/~brendan/snapshots/icecast/
Upstream source: http://downloads.us.xiph.org/releases/icecast/
Note: Tarball distributed with Debian is currently not pristine: the
subdir "debian" has been stripped to not clash with the official Debian
packaging files.
Upstream Authors: the icecast team <team@icecast.org>
Copyright (c) 1999, 2000 the icecast team
Copyright and license; src/httpd/:
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any latfer version.
licensed under the lgpl
created by jack moffitt <jack@icecast.org>
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright and license; src/avl/:
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Copyright (C) 1995-1997 by Sam Rushing <rushing@nightmare.com>
*
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of Sam
* Rushing not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission.
*
* SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
On Debian systems, the complete text of the GNU General Public License
can be found in `/usr/share/common-licenses/GPL'.
Copyright and license; src/thread/:
* Copyright (c) 1999, 2000 the icecast team <team@icecast.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Copyright and license; src/net/:
* Copyright (C) 1999 the icecast team <team@icecast.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Copyright and license; src/httpd/:
lgpl
by jack moffitt <jack@icecast.org>
Copyright and license; other files (found in src/client.c):
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
On Debian systems, the complete text of both the GNU General Public
License (GPL) and the GNU Library General Public License (LGPL) can be
found below `/usr/share/common-licenses/'.

View File

@ -10,7 +10,7 @@
CONFIGFILE="/etc/icecast2/icecast.xml"
# Name or ID of the user and group the daemon should run under
USERID=icecast
USERID=icecast2
GROUPID=icecast
# Edit /etc/icecast2/icecast.xml and change at least the passwords.

View File

@ -20,7 +20,7 @@ test -x $DAEMON || exit 0
# Defaults
CONFIGFILE="/etc/icecast2/icecast.xml"
CONFIGDEFAULTFILE="/etc/default/icecast2"
USERID=icecast
USERID=icecast2
GROUPID=icecast
ENABLE="false"
@ -38,7 +38,7 @@ case "$1" in
start)
echo -n "Starting $DESC: "
start-stop-daemon --start --quiet --chuid $USERID:$GROUPID \
--background --exec $DAEMON -- -c $CONFIGFILE
--exec $DAEMON -- -b -c $CONFIGFILE
echo "$NAME."
;;
stop)
@ -53,9 +53,9 @@ case "$1" in
restart)
echo -n "Restarting $DESC: "
start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
sleep 1
sleep 2
start-stop-daemon --start --quiet --chuid $USERID:$GROUPID \
--background --exec $DAEMON -- -c $CONFIGFILE
--exec $DAEMON -- -b -c $CONFIGFILE
echo "$NAME."
;;
*)

View File

@ -24,18 +24,30 @@ if [ -f /etc/icecast.xml ] && grep -q /etc/icecast2/ /etc/default/icecast2; then
echo "It seems you have an old configuration lying around at"
echo "/etc/icecast.xml. You will need to manually merge with"
echo "the current configuration at /etc/icecast2/icecast.xml."
echo
echo "See /usr/share/doc/icecast2/examples for new configuration options."
fi
# Check for an account named 'icecast'
if ! id icecast >/dev/null 2>&1; then
# Create the new system account
adduser --system --disabled-password --disabled-login \
--home /usr/share/icecast2 --no-create-home --group icecast
if [ -f /etc/icecast2/icecast.xml ] ; then
echo "* Found existing /etc/icecast2/icecast.xml"
echo "* Moving /etc/icecast2/icecast.xml to /usr/share/icecast2/etc/icecast2"
mv /etc/icecast2/icecast.xml /usr/share/icecast2/etc/icecast2
ln -s /usr/share/icecast2/etc/icecast2/icecast.xml /etc/icecast2/icecast.xml
fi
chown -R icecast:icecast /var/log/icecast2
if ! getent group icecast >/dev/null 2>&1; then
addgroup --system icecast
fi
# Check for an account named 'icecast2'
if ! id icecast2 >/dev/null 2>&1; then
# Create the new system account
adduser --system --disabled-password --disabled-login \
--home /usr/share/icecast2 --no-create-home --ingroup icecast icecast2
fi
chown -R icecast2: /var/log/icecast2 /etc/icecast2 /usr/share/icecast2
chmod -R ug=rw,o=,ug+X /etc/icecast2
#DEBHELPER#

View File

@ -6,14 +6,15 @@ set -e
case "$1" in
purge)
rm -rf /var/log/icecast2
rm -rf /usr/share/icecast2/log
if id icecast >/dev/null 2>&1; then
deluser icecast
if id icecast2 >/dev/null 2>&1; then
deluser icecast2
fi
# Remove group only if empty
if getent group icecast | awk -F: ' { print $4 } ' | egrep -cq '^$'; then
groupdel icecast
groupdel icecast || echo "Error occured removing group icecast, please do it manually."
fi
;;
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)

41
debian/rules vendored
View File

@ -1,34 +1,27 @@
#!/usr/bin/make -f
# -*- mode: makefile; coding: utf-8 -*-
# Copyright © 2004 Jonas Smedegaard <dr@jones.dk>
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/autotools.mk
include debian/cdbs/1/rules/buildinfo.mk
DEB_INSTALL_CHANGELOGS_ALL = NEWS
DEB_CONFIGURE_SYSCONFDIR = /etc/icecast2
DEB_CONFIGURE_EXTRA_FLAGS = --program-transform-name="s/icecast$$/icecast2/"
DEB_MAKE_INVOKE += PACKAGE=icecast2 docdir=/usr/share/doc/icecast2 pkgdatadir=/usr/share/icecast2
DEB_INSTALL_DIRS_icecast2 = var/log/icecast2
DEB_INSTALL_MANPAGES_icecast2 = debian/icecast2.1
binary-post-install/icecast2::
# Debian has a central copy of the GPL, no need to distribute again
# Debian has a central copy of the GPL, no need to distribute again
common-binary-post-install-arch::
rm -f $(DEB_DESTDIR)/usr/share/doc/icecast2/COPYING
# Live peacefully with icecast 1
mv $(DEB_DESTDIR)/usr/bin/icecast $(DEB_DESTDIR)/usr/bin/icecast2
# Move XSLT templates to /etc and replace with symlinks
for file in `cd $(DEB_DESTDIR)/usr/share && find icecast2 -type f -name *.xsl`; do \
mkdir -p $(DEB_DESTDIR)/etc/`dirname $$file`; \
mv $(DEB_DESTDIR)/usr/share/$$file $(DEB_DESTDIR)/etc/$$file; \
ln -s /etc/$$file $(DEB_DESTDIR)/usr/share/$$file; \
# Move XSLT templates and CSS files to /etc and replace with symlinks
common-binary-post-install-arch::
for file in `cd $(DEB_DESTDIR)/usr/share && find icecast2 -type d \( -name admin -or -name web \)`; do \
ln -s /usr/share/$$file $(DEB_DESTDIR)/etc/$$file; \
done
# NEWS is ChangeLog - avoid original name
rm -f $(DEB_DESTDIR)/usr/share/doc/icecast2/NEWS
mkdir -p $(CURDIR)/debian/icecast2/var/log/icecast2
# Store build information
dh_buildinfo
clean::
# Upstream forgot to clean this one it seems...
rm -f conf/icecast.xml.dist
mkdir $(DEB_DESTDIR)/usr/share/icecast2/log
chown icecast2:icecast $(DEB_DESTDIR)/usr/share/icecast2/log
mkdir -p $(DEB_DESTDIR)/usr/share/icecast2/etc/icecast2
#mv $(DEB_DESTDIR)/etc/icecast2/icecast.xml $(DEB_DESTDIR)/usr/share/icecast2/etc/icecast2
#ln -s /usr/share/icecast2/etc/icecast2/icecast.xml $(DEB_DESTDIR)/etc/icecast2/icecast.xml

4
debian/watch vendored
View File

@ -1,3 +1,3 @@
# Run the "uscan" command to check for upstream updates and more.
version=2
http://www.icecast.org/download.html files/icecast/icecast-(.*)\.tar\.gz debian uupdate
http://downloads.us.xiph.org/releases/icecast/icecast-([\d+\.]+|\d+)(\.tar|\.tgz)(\.gz|\.bz2|) 2.2.0 uupdate

View File

@ -11,7 +11,8 @@ noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
compat.h fserve.h xslt.h yp.h event.h md5.h \
auth.h auth_htpasswd.h auth_cmd.h auth_url.h \
format.h format_ogg.h format_mp3.h \
format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h
format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \
fnmatch_loop.c
icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
util.c slave.c source.c stats.c refbuf.c client.c \
xslt.c fserve.c event.c admin.c md5.c \
@ -19,8 +20,8 @@ icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c
auth.c auth_htpasswd.c
EXTRA_icecast_SOURCES = yp.c \
auth_url.c auth_cmd.c \
format_vorbis.c format_theora.c format_speex.c
format_vorbis.c format_theora.c format_speex.c fnmatch.c
icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \
httpp/libicehttpp.la log/libicelog.la avl/libiceavl.la timing/libicetiming.la
icecast_LDADD = $(icecast_DEPENDENCIES) @XIPH_LIBS@

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,23 @@
#ifndef __ADMIN_H__
#define __ADMIN_H__
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "source.h"
#include "client.h"
typedef enum {
NONE,
RAW,
XSLT,
TEXT
} admin_response_type;
int admin_handle_request (client_t *client, const char *uri);
void admin_mount_request (client_t *client, const char *uri);
void admin_source_listeners (source_t *source, xmlNodePtr node);
void admin_send_response(xmlDocPtr doc, client_t *client,
admin_response_type response, const char *xslt_template);
#endif /* __ADMIN_H__ */

View File

@ -39,67 +39,57 @@
#define CATMODULE "auth"
static volatile auth_client *clients_to_auth;
static volatile unsigned int auth_pending_count;
static volatile int auth_running;
static mutex_t auth_lock;
static thread_type *auth_thread;
static spin_t auth_lock;
static volatile int thread_id;
static void *auth_run_thread (void *arg);
static auth_client *auth_client_setup (const char *mount, mount_proxy *mountinfo, client_t *client)
void auth_check_http (client_t *client)
{
const char *header;
char *username, *password;
/* process any auth headers if any available */
header = httpp_getvar (client->parser, "authorization");
if (header == NULL)
return;
if (strncmp(header, "Basic ", 6) == 0)
{
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
char *tmp, *userpass = util_base64_decode (header+6);
if (userpass == NULL)
{
WARN1("Base64 decode of Authorization header \"%s\" failed",
header+6);
return;
}
tmp = strchr(userpass, ':');
if (tmp == NULL)
{
free (userpass);
return;
}
*tmp = 0;
username = userpass;
password = tmp+1;
client->username = strdup (username);
client->password = strdup (password);
free (userpass);
return;
}
WARN1 ("unhandled authorization header: %s", header);
}
static auth_client *auth_client_setup (const char *mount, client_t *client)
{
ice_config_t *config = config_get_config_unlocked();
auth_client *auth_user = calloc (1, sizeof(auth_client));
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
char *header;
char *userpass, *tmp;
char *username, *password;
do
{
if (client == NULL)
break;
/* process any auth headers if any available */
header = httpp_getvar(client->parser, "authorization");
if (header == NULL)
break;
if (strncmp(header, "Basic ", 6) == 0)
{
userpass = util_base64_decode (header+6);
if (userpass == NULL)
{
WARN1("Base64 decode of Authorization header \"%s\" failed",
header+6);
break;
}
tmp = strchr(userpass, ':');
if (tmp == NULL)
{
free (userpass);
break;
}
*tmp = 0;
username = userpass;
password = tmp+1;
client->username = strdup (username);
client->password = strdup (password);
free (userpass);
break;
}
WARN1 ("unhandled authorization header: %s", header);
} while (0);
thread_mutex_lock (&mountinfo->auth->lock);
if (client)
client->auth = mountinfo->auth;
mountinfo->auth->refcount++;
thread_mutex_unlock (&mountinfo->auth->lock);
auth_user->mount = strdup (mount);
auth_user->hostname = strdup (config->hostname);
auth_user->port = config->port;
@ -108,13 +98,35 @@ static auth_client *auth_client_setup (const char *mount, mount_proxy *mountinfo
}
static void queue_auth_client (auth_client *auth_user)
static void queue_auth_client (auth_client *auth_user, mount_proxy *mountinfo)
{
thread_mutex_lock (&auth_lock);
auth_user->next = (auth_client *)clients_to_auth;
clients_to_auth = auth_user;
auth_pending_count++;
thread_mutex_unlock (&auth_lock);
auth_t *auth;
int i;
if (auth_user == NULL || mountinfo == NULL)
return;
auth = mountinfo->auth;
thread_mutex_lock (&auth->lock);
auth_user->next = NULL;
auth_user->auth = auth;
auth->refcount++;
*auth->tailp = auth_user;
auth->tailp = &auth_user->next;
auth->pending_count++;
for (i=0; i<auth->handlers; i++)
{
if (auth->handles [i].thread == NULL)
{
DEBUG1 ("starting auth thread %d", i);
auth->handles [i].thread = thread_create ("auth thread", auth_run_thread,
&auth->handles [i], THREAD_DETACHED);
break;
}
}
if (i == auth->handlers)
DEBUG0 ("max authentication handlers allocated");
INFO2 ("auth on %s has %d pending", auth->mount, auth->pending_count);
thread_mutex_unlock (&auth->lock);
}
@ -128,18 +140,33 @@ void auth_release (auth_t *authenticator)
thread_mutex_lock (&authenticator->lock);
authenticator->refcount--;
DEBUG2 ("...refcount on auth_t %s is now %d", authenticator->mount, authenticator->refcount);
if (authenticator->refcount)
{
thread_mutex_unlock (&authenticator->lock);
return;
}
/* cleanup auth threads attached to this auth */
authenticator->running = 0;
while (authenticator->handlers)
{
while (authenticator->handles [authenticator->handlers-1].thread)
thread_sleep (5000);
if (authenticator->release_thread_data)
authenticator->release_thread_data (authenticator,
authenticator->handles [authenticator->handlers-1].data);
authenticator->handlers--;
}
free (authenticator->handles);
if (authenticator->free)
authenticator->free (authenticator);
xmlFree (authenticator->type);
xmlFree (authenticator->realm);
thread_mutex_unlock (&authenticator->lock);
thread_mutex_destroy (&authenticator->lock);
free (authenticator->mount);
free (authenticator);
}
@ -155,15 +182,29 @@ static void auth_client_free (auth_client *auth_user)
if (client->respcode)
client_destroy (client);
else
client_send_401 (client);
client_send_401 (client, auth_user->auth->realm);
auth_user->client = NULL;
}
auth_release (auth_user->auth);
free (auth_user->hostname);
free (auth_user->mount);
free (auth_user);
}
/* verify that the listener is still connected. */
static int is_listener_connected (client_t *client)
{
int ret = 1;
if (client)
{
if (sock_active (client->con->sock) == 0)
ret = 0;
}
return ret;
}
/* wrapper function for auth thread to authenticate new listener
* connection details
*/
@ -171,9 +212,17 @@ static void auth_new_listener (auth_client *auth_user)
{
client_t *client = auth_user->client;
if (client->auth->authenticate)
/* make sure there is still a client at this point, a slow backend request
* can be avoided if client has disconnected */
if (is_listener_connected (client) == 0)
{
if (client->auth->authenticate (auth_user) != AUTH_OK)
DEBUG0 ("listener is no longer connected");
client->respcode = 400;
return;
}
if (auth_user->auth->authenticate)
{
if (auth_user->auth->authenticate (auth_user) != AUTH_OK)
return;
}
if (auth_postprocess_listener (auth_user) < 0)
@ -185,12 +234,15 @@ static void auth_new_listener (auth_client *auth_user)
*/
static void auth_remove_listener (auth_client *auth_user)
{
client_t *client = auth_user->client;
if (client->auth->release_client)
client->auth->release_client (auth_user);
auth_release (client->auth);
client->auth = NULL;
DEBUG0 ("...queue listener");
if (auth_user->auth->release_listener)
auth_user->auth->release_listener (auth_user);
auth_release (auth_user->auth);
auth_user->auth = NULL;
/* client is going, so auth is not an issue at this point */
auth_user->client->authenticated = 0;
client_send_404 (auth_user->client, "Failed relay");
auth_user->client = NULL;
}
@ -201,15 +253,11 @@ static void stream_auth_callback (auth_client *auth_user)
{
client_t *client = auth_user->client;
if (client->auth->stream_auth)
client->auth->stream_auth (auth_user);
if (auth_user->auth->stream_auth)
auth_user->auth->stream_auth (auth_user);
if (client->authenticated)
{
auth_release (client->auth);
client->auth = NULL;
auth_postprocess_source (auth_user);
DEBUG0 ("started");
}
else
WARN1 ("Failed auth for source \"%s\"", auth_user->mount);
}
@ -220,15 +268,10 @@ static void stream_auth_callback (auth_client *auth_user)
*/
static void stream_start_callback (auth_client *auth_user)
{
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
auth_t *auth = mountinfo->auth;
config_release_config();
auth_t *auth = auth_user->auth;
if (auth->stream_start)
auth->stream_start (auth_user, auth);
auth_release (auth);
auth->stream_start (auth_user);
}
@ -237,35 +280,48 @@ static void stream_start_callback (auth_client *auth_user)
*/
static void stream_end_callback (auth_client *auth_user)
{
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
auth_t *auth = mountinfo->auth;
config_release_config();
auth_t *auth = auth_user->auth;
if (auth->stream_end)
auth->stream_end (auth_user, auth);
auth_release (auth);
auth->stream_end (auth_user);
}
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
INFO0 ("Authentication thread started");
while (1)
auth_thread_t *handler = arg;
auth_t *auth = handler->auth;
INFO2 ("Authentication thread %d started for %s", handler->id, auth->mount);
while (auth->running)
{
if (clients_to_auth)
/* usually no clients are waiting, so don't bother taking locks */
if (auth->head)
{
auth_client *auth_user;
thread_mutex_lock (&auth_lock);
auth_user = (auth_client*)clients_to_auth;
clients_to_auth = auth_user->next;
auth_pending_count--;
thread_mutex_unlock (&auth_lock);
/* may become NULL before lock taken */
thread_mutex_lock (&auth->lock);
auth_user = (auth_client*)auth->head;
if (auth_user == NULL)
{
thread_mutex_unlock (&auth->lock);
continue;
}
DEBUG2 ("%d client(s) pending on %s", auth->pending_count, auth->mount);
auth->head = auth_user->next;
if (auth->head == NULL)
auth->tailp = &auth->head;
auth->pending_count--;
thread_mutex_unlock (&auth->lock);
auth_user->next = NULL;
/* associate per-thread data with auth_user here */
auth_user->thread_data = handler->data;
auth_user->handler = handler->id;
if (auth_user->process)
auth_user->process (auth_user);
else
@ -275,90 +331,165 @@ static void *auth_run_thread (void *arg)
continue;
}
/* is there a request to shutdown */
if (auth_running == 0)
break;
thread_sleep (150000);
break;
}
INFO0 ("Authenication thread shutting down");
INFO1 ("Authenication thread %d shutting down", handler->id);
handler->thread = NULL;
return NULL;
}
/* Check whether this client is currently on this mount, the client may be
* on either the active or pending lists.
/* Check whether this listener is on this source. This is only called when
* there is auth. This may flag an existing listener to terminate.
* return 1 if ok to add or 0 to prevent
*/
static int check_duplicate_logins (source_t *source, client_t *client)
static int check_duplicate_logins (source_t *source, client_t *client, auth_t *auth)
{
auth_t *auth = client->auth;
client_t *existing;
if (auth == NULL || auth->allow_duplicate_users)
return 1;
/* allow multiple authenticated relays */
if (client->username == NULL || client->is_slave)
return 1;
if (auth && auth->allow_duplicate_users == 0)
existing = source->active_clients;
while (existing)
{
client_t *existing;
existing = source->active_clients;
while (existing)
if (existing->con->error == 0 && existing->username &&
strcmp (existing->username, client->username) == 0)
{
if (existing->con->error == 0 && existing->username &&
strcmp (existing->username, client->username) == 0)
if (auth->drop_existing_listener)
{
if (auth->drop_existing_listener)
{
existing->con->error = 1;
return 1;
}
else
return 0;
existing->con->error = 1;
return 1;
}
existing = existing->next;
else
return 0;
}
existing = existing->next;
}
return 1;
}
/* The actual add client routine, this requires the source to be locked.
* if 0 is returned then the client should not be touched, however if -1
* is returned then the caller is responsible for handling the client
/* Add client to source if it finds one. If a 0 is returned then the client should not be
* touched, if the return value is -1 then the it failed to add and should not be touched.
* If it's a -2 value then the client is still around for any further processing.
*/
static int add_client_to_source (source_t *source, client_t *client)
static int add_listener_to_source (const char *mount, mount_proxy *mountinfo, client_t *client)
{
int loop = 10;
int within_limits;
source_t *source;
mount_proxy *minfo = mountinfo;
const char *passed_mount = mount;
ice_config_t *config = config_get_config_unlocked();
do
{
thread_mutex_lock (&source->lock);
DEBUG3 ("max on %s is %ld (cur %lu)", source->mount,
source->max_listeners, source->listeners);
if (source->max_listeners == -1)
break;
if (client->is_slave)
break;
if (source->listeners < (unsigned long)source->max_listeners)
break;
int64_t stream_bitrate = 0;
if (loop && source->fallback_when_full && source->fallback_mount)
do
{
source_t *next = source_find_mount (source->fallback_mount);
thread_mutex_unlock (&source->lock);
if (!next)
source = source_find_mount_raw (mount);
if (loop == 0)
{
ERROR2("Fallback '%s' for full source '%s' not found",
source->mount, source->fallback_mount);
WARN0 ("preventing a fallback loop");
client_send_403 (client, "Fallback through too many mountpoints");
return -1;
}
if (source)
{
thread_mutex_lock (&source->lock);
if (source->running || source->on_demand)
break;
thread_mutex_unlock (&source->lock);
}
if (minfo == NULL || minfo->fallback_mount == NULL)
return -2;
mount = minfo->fallback_mount;
minfo = config_find_mount (config_get_config_unlocked(), mount);
loop--;
} while (1);
INFO1 ("stream full trying %s", next->mount);
source = next;
/* ok, we found a source and it is locked */
if (client->is_slave)
{
INFO0 ("client is from a slave, bypassing limits");
break;
}
if (source->format)
{
stream_bitrate = 8 * rate_avg (source->format->in_bitrate);
if (config->max_bandwidth)
{
int64_t global_rate = (int64_t)8 * global_getrate_avg (global.out_bitrate);
DEBUG1 ("server outgoing bitrate is %" PRId64, global_rate);
if (global_rate + stream_bitrate > config->max_bandwidth)
{
thread_mutex_unlock (&source->lock);
INFO0 ("server-wide outgoing bandwidth limit reached");
client_send_403redirect (client, passed_mount, "server bandwidth reached");
return -1;
}
}
}
if (mountinfo == NULL)
break; /* allow adding listeners, no mount limits imposed */
if (check_duplicate_logins (source, client, mountinfo->auth) == 0)
{
thread_mutex_unlock (&source->lock);
client_send_403 (client, "Account already in use");
return -1;
}
/* set a per-mount disconnect time if auth hasn't set one already */
if (mountinfo->max_listener_duration && client->con->discon_time == 0)
client->con->discon_time = global.time + mountinfo->max_listener_duration;
INFO3 ("max on %s is %ld (cur %lu)", source->mount,
mountinfo->max_listeners, source->listeners);
within_limits = 1;
if (mountinfo->max_bandwidth > -1 && stream_bitrate)
{
DEBUG3 ("checking bandwidth limits for %s (%" PRId64 ", %" PRId64 ")",
mountinfo->mountname, stream_bitrate, mountinfo->max_bandwidth);
if ((source->listeners+1) * stream_bitrate > mountinfo->max_bandwidth)
{
INFO1 ("bandwidth limit reached on %s", source->mount);
within_limits = 0;
}
}
if (within_limits)
{
if (mountinfo->max_listeners == -1)
break;
if (source->listeners < (unsigned long)mountinfo->max_listeners)
break;
INFO1 ("max listener count reached on %s", source->mount);
}
/* minfo starts off as mountinfo put cascades through fallbacks */
if (minfo && minfo->fallback_when_full && minfo->fallback_mount)
{
thread_mutex_unlock (&source->lock);
mount = minfo->fallback_mount;
INFO1 ("stream full trying %s", mount);
loop--;
continue;
}
/* now we fail the client */
thread_mutex_unlock (&source->lock);
client_send_403redirect (client, passed_mount, "max listeners reached");
return -1;
} while (1);
@ -386,45 +517,40 @@ static int add_client_to_source (source_t *source, client_t *client)
}
/* Add listener to the pending lists of either the source or fserve thread.
* This can be run from the connection or auth thread context
/* Add listener to the pending lists of either the source or fserve thread. This can be run
* from the connection or auth thread context. return -1 to indicate that client has been
* terminated, 0 for receiving content.
*/
static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo, client_t *client)
{
int ret = 0;
source_t *source = NULL;
client->authenticated = 1;
/* Here we are parsing the URI request to see if the extension is .xsl, if
* so, then process this request as an XSLT request
*/
if (util_check_valid_extension (mount) == XSLT_CONTENT)
{
/* If the file exists, then transform it, otherwise, write a 404 */
DEBUG0("Stats request, sending XSL transformed stats");
stats_transform_xslt (client, mount);
return 0;
}
avl_tree_rlock (global.source_tree);
source = source_find_mount (mount);
ret = add_listener_to_source (mount, mountinfo, client);
avl_tree_unlock (global.source_tree);
if (source)
if (ret == -2)
{
if (client->auth && check_duplicate_logins (source, client) == 0)
if (mountinfo && mountinfo->file_seekable == 0)
{
avl_tree_unlock (global.source_tree);
return -1;
DEBUG1 ("disable seek on file matching %s", mountinfo->mountname);
httpp_deletevar (client->parser, "range");
httpp_setvar (client->parser, HTTPP_VAR_NO_CONTENT_LENGTH, "yes");
}
if (mountinfo)
{
/* set a per-mount disconnect time if auth hasn't set one already */
if (mountinfo->max_listener_duration && client->con->discon_time == 0)
client->con->discon_time = time(NULL) + mountinfo->max_listener_duration;
}
ret = add_client_to_source (source, client);
avl_tree_unlock (global.source_tree);
if (ret == 0)
DEBUG0 ("client authenticated, passed to source");
else
{
if (redirect_client (mount, client))
ret = 0;
}
}
else
{
avl_tree_unlock (global.source_tree);
fserve_client_create (client, mount);
ret = fserve_client_create (client, mount);
}
return ret;
}
@ -433,16 +559,14 @@ static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo
int auth_postprocess_listener (auth_client *auth_user)
{
int ret;
client_t *client = auth_user->client;
ice_config_t *config = config_get_config();
mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
auth_user->client->authenticated = 1;
ret = add_authenticated_listener (auth_user->mount, mountinfo, auth_user->client);
ret = add_authenticated_listener (auth_user->mount, mountinfo, client);
config_release_config();
if (ret < 0)
client_send_401 (auth_user->client);
auth_user->client = NULL;
return ret;
@ -469,7 +593,7 @@ void auth_postprocess_source (auth_client *auth_user)
/* Add a listener. Check for any mount information that states any
* authentication to be used.
*/
void auth_add_client (const char *mount, client_t *client)
void auth_add_listener (const char *mount, client_t *client)
{
mount_proxy *mountinfo;
ice_config_t *config;
@ -490,59 +614,62 @@ void auth_add_client (const char *mount, client_t *client)
client_send_403 (client, "mountpoint unavailable");
return;
}
if (mountinfo && mountinfo->auth)
if (mountinfo && mountinfo->auth && mountinfo->auth->authenticate)
{
auth_client *auth_user;
if (auth_pending_count > 30)
if (mountinfo->auth->pending_count > 1000)
{
config_release_config ();
WARN0 ("too many clients awaiting authentication");
client_send_403 (client, "busy, please try again later");
return;
}
auth_user = auth_client_setup (mount, mountinfo, client);
config_release_config ();
auth_user = auth_client_setup (mount, client);
auth_user->process = auth_new_listener;
INFO0 ("adding client for authentication");
queue_auth_client (auth_user);
queue_auth_client (auth_user, mountinfo);
}
else
{
int ret = add_authenticated_listener (mount, mountinfo, client);
config_release_config ();
if (ret < 0)
client_send_403 (client, "max listeners reached");
add_authenticated_listener (mount, mountinfo, client);
}
config_release_config ();
}
/* determine whether we need to process this client further. This
* involves any auth exit, typically for external auth servers.
/* General listener client shutdown function. Here we free up the passed client but
* if the client is authenticated and there's auth available then queue it.
*/
int auth_client_release (client_t *client)
int auth_release_listener (client_t *client, const char *mount, mount_proxy *mountinfo)
{
if (client->auth && client->authenticated)
if (client->authenticated)
{
auth_client *auth_user = calloc (1, sizeof (auth_client));
if (auth_user == NULL)
return 0;
/* drop any queue reference here, we do not want a race between the source thread
* and the auth/fserve thread */
client_set_queue (client, NULL);
auth_user->mount = strdup (httpp_getvar (client->parser, HTTPP_VAR_URI));
auth_user->process = auth_remove_listener;
auth_user->client = client;
queue_auth_client (auth_user);
return 1;
if (mount && mountinfo && mountinfo->auth && mountinfo->auth->release_listener)
{
auth_client *auth_user = auth_client_setup (mount, client);
auth_user->process = auth_remove_listener;
queue_auth_client (auth_user, mountinfo);
return 1;
}
client->authenticated = 0;
}
client_send_404 (client, NULL);
return 0;
}
static void get_authenticator (auth_t *auth, config_options_t *options)
static int get_authenticator (auth_t *auth, config_options_t *options)
{
if (auth->type == NULL)
{
WARN0 ("no authentication type defined");
return -1;
}
do
{
DEBUG1 ("type is %s", auth->type);
@ -550,43 +677,51 @@ static void get_authenticator (auth_t *auth, config_options_t *options)
if (strcmp (auth->type, "url") == 0)
{
#ifdef HAVE_AUTH_URL
auth_get_url_auth (auth, options);
if (auth_get_url_auth (auth, options) < 0)
return -1;
break;
#else
ERROR0 ("Auth URL disabled");
return -1;
#endif
break;
}
if (strcmp (auth->type, "command") == 0)
{
#ifdef WIN32
ERROR1("Authenticator type: \"%s\" not supported on win32 platform", auth->type);
return;
return -1;
#else
auth_get_cmd_auth (auth, options);
if (auth_get_cmd_auth (auth, options) < 0)
return -1;
break;
#endif
}
if (strcmp (auth->type, "htpasswd") == 0)
{
auth_get_htpasswd_auth (auth, options);
if (auth_get_htpasswd_auth (auth, options) < 0)
return -1;
break;
}
ERROR1("Unrecognised authenticator type: \"%s\"", auth->type);
return;
return -1;
} while (0);
auth->refcount = 1;
while (options)
{
if (strcmp(options->name, "allow_duplicate_users") == 0)
if (strcmp (options->name, "allow_duplicate_users") == 0)
auth->allow_duplicate_users = atoi (options->value);
else if (strcmp(options->name, "realm") == 0)
auth->realm = (char*)xmlStrdup (XMLSTR(options->value));
else if (strcmp(options->name, "drop_existing_listener") == 0)
auth->drop_existing_listener = atoi (options->value);
else if (strcmp(options->name, "handlers") == 0)
auth->handlers = atoi (options->value);
options = options->next;
}
if (auth->handlers < 1) auth->handlers = 1;
if (auth->handlers > 20) auth->handlers = 20;
return 0;
}
@ -595,11 +730,11 @@ int auth_get_authenticator (xmlNodePtr node, void *x)
auth_t *auth = calloc (1, sizeof (auth_t));
config_options_t *options = NULL, **next_option = &options;
xmlNodePtr option;
int i;
if (auth == NULL)
return -1;
*(auth_t**)x = auth;
option = node->xmlChildrenNode;
while (option)
{
@ -629,8 +764,31 @@ int auth_get_authenticator (xmlNodePtr node, void *x)
WARN1 ("unknown auth setting (%s)", current->name);
}
auth->type = (char *)xmlGetProp (node, XMLSTR("type"));
get_authenticator (auth, options);
thread_mutex_create ("auth_t", &auth->lock);
if (get_authenticator (auth, options) < 0)
{
xmlFree (auth->type);
free (auth);
auth = NULL;
}
else
{
auth->tailp = &auth->head;
thread_mutex_create ("auth_t", &auth->lock);
/* allocate N threads */
auth->handles = calloc (auth->handlers, sizeof (auth_thread_t));
auth->refcount = 1;
auth->running = 1;
for (i=0; i<auth->handlers; i++)
{
if (auth->alloc_thread_data)
auth->handles[i].data = auth->alloc_thread_data (auth);
auth->handles[i].id = thread_id++;
auth->handles[i].auth = auth;
}
*(auth_t**)x = auth;
}
while (options)
{
config_options_t *opt = options;
@ -651,16 +809,17 @@ int auth_stream_authenticate (client_t *client, const char *mount, mount_proxy *
{
if (mountinfo && mountinfo->auth && mountinfo->auth->stream_auth)
{
auth_client *auth_user = auth_client_setup (mount, mountinfo, client);
auth_client *auth_user = auth_client_setup (mount, client);
auth_user->process = stream_auth_callback;
INFO1 ("request source auth for \"%s\"", mount);
queue_auth_client (auth_user);
queue_auth_client (auth_user, mountinfo);
return 1;
}
return 0;
}
/* called when the stream starts, so that authentication engine can do any
* cleanup/initialisation.
*/
@ -668,11 +827,11 @@ void auth_stream_start (mount_proxy *mountinfo, const char *mount)
{
if (mountinfo && mountinfo->auth && mountinfo->auth->stream_start)
{
auth_client *auth_user = auth_client_setup (mount, mountinfo, NULL);
auth_client *auth_user = auth_client_setup (mount, NULL);
auth_user->process = stream_start_callback;
INFO1 ("request source start for \"%s\"", mount);
queue_auth_client (auth_user);
queue_auth_client (auth_user, mountinfo);
}
}
@ -684,11 +843,11 @@ void auth_stream_end (mount_proxy *mountinfo, const char *mount)
{
if (mountinfo && mountinfo->auth && mountinfo->auth->stream_end)
{
auth_client *auth_user = auth_client_setup (mount, mountinfo, NULL);
auth_client *auth_user = auth_client_setup (mount, NULL);
auth_user->process = stream_end_callback;
INFO1 ("request source end for \"%s\"", mount);
queue_auth_client (auth_user);
queue_auth_client (auth_user, mountinfo);
}
}
@ -697,20 +856,13 @@ void auth_stream_end (mount_proxy *mountinfo, const char *mount)
void auth_initialise (void)
{
clients_to_auth = NULL;
auth_pending_count = 0;
auth_running = 1;
thread_mutex_create ("auth lock", &auth_lock);
auth_thread = thread_create ("auth thread", auth_run_thread, NULL, THREAD_ATTACHED);
thread_spin_create ("auth lock", &auth_lock);
thread_id = 0;
}
void auth_shutdown (void)
{
if (auth_thread)
{
auth_running = 0;
thread_join (auth_thread);
INFO0 ("Auth thread has terminated");
}
thread_spin_destroy (&auth_lock);
INFO0 ("Auth shutdown");
}

View File

@ -43,45 +43,73 @@ typedef struct auth_client_tag
char *hostname;
int port;
client_t *client;
struct auth_tag *auth;
void *thread_data;
int handler;
void (*process)(struct auth_client_tag *auth_user);
struct auth_client_tag *next;
} auth_client;
typedef struct _auth_thread_t
{
thread_type *thread;
void *data;
unsigned int id;
struct auth_tag *auth;
} auth_thread_t;
typedef struct auth_tag
{
char *mount;
/* Authenticate using the given username and password */
auth_result (*authenticate)(auth_client *aclient);
auth_result (*release_client)(auth_client *auth_user);
auth_result (*release_listener)(auth_client *auth_user);
/* auth handler for authenicating a connecting source client */
void (*stream_auth)(auth_client *auth_user);
/* auth handler for source startup, no client passed as it may disappear */
void (*stream_start)(auth_client *auth_user, struct auth_tag *auth);
void (*stream_start)(auth_client *auth_user);
/* auth handler for source exit, no client passed as it may disappear */
void (*stream_end)(auth_client *auth_user, struct auth_tag *auth);
void (*stream_end)(auth_client *auth_user);
/* auth state-specific free call */
void (*free)(struct auth_tag *self);
/* call to allocate any per auth thread data */
void *(*alloc_thread_data)(struct auth_tag *self);
/* call to freeup any per auth thread data */
void (*release_thread_data)(struct auth_tag *self, void *data);
auth_result (*adduser)(struct auth_tag *auth, const char *username, const char *password);
auth_result (*deleteuser)(struct auth_tag *auth, const char *username);
auth_result (*listuser)(struct auth_tag *auth, xmlNodePtr srcnode);
mutex_t lock;
int running;
int refcount;
int allow_duplicate_users;
int drop_existing_listener;
int handlers;
/* runtime allocated array of thread handlers for this auth */
auth_thread_t *handles;
/* per-auth queue for clients */
auth_client *head, **tailp;
int pending_count;
void *state;
char *type;
char *realm;
} auth_t;
void auth_add_client (const char *mount, client_t *client);
int auth_client_release (client_t *client);
void auth_add_listener (const char *mount, client_t *client);
int auth_release_listener (client_t *client, const char *mount, struct _mount_proxy *mountinfo);
void auth_initialise (void);
void auth_shutdown (void);
@ -106,6 +134,8 @@ int auth_postprocess_listener (auth_client *auth_user);
/* called from auth thread */
void auth_postprocess_source (auth_client *auth_user);
void auth_check_http (client_t *client);
#endif

View File

@ -58,7 +58,7 @@ static auth_result auth_cmd_client (auth_client *auth_user)
int fd[2];
pid_t pid;
client_t *client = auth_user->client;
auth_t *auth = client->auth;
auth_t *auth = auth_user->auth;
auth_cmd *cmd = auth->state;
int status, len;
char str[512];
@ -118,7 +118,7 @@ static auth_result auth_cmd_listuser (auth_t *auth, xmlNodePtr srcnode)
return AUTH_FAILED;
}
void auth_get_cmd_auth (auth_t *authenticator, config_options_t *options)
int auth_get_cmd_auth (auth_t *authenticator, config_options_t *options)
{
auth_cmd *state;
@ -135,7 +135,13 @@ void auth_get_cmd_auth (auth_t *authenticator, config_options_t *options)
state->filename = strdup(options->value);
options = options->next;
}
if (state->filename == NULL)
{
ERROR0 ("No command specified for authentication");
return -1;
}
authenticator->state = state;
INFO0("external command based authentication setup");
return 0;
}

View File

@ -17,7 +17,7 @@
#include <config.h>
#endif
void auth_get_cmd_auth (auth_t *, config_options_t *options);
int auth_get_cmd_auth (auth_t *, config_options_t *options);
#endif

View File

@ -34,10 +34,6 @@
#include "logging.h"
#define CATMODULE "auth_htpasswd"
#ifdef WIN32
#define snprintf _snprintf
#endif
static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password);
static auth_result htpasswd_deleteuser(auth_t *auth, const char *username);
@ -66,19 +62,6 @@ static void htpasswd_clear(auth_t *self) {
free(state);
}
static int get_line(FILE *file, char *buf, size_t siz)
{
if(fgets(buf, (int)siz, file)) {
size_t len = strlen(buf);
if(len > 0 && buf[len-1] == '\n') {
buf[--len] = 0;
if(len > 0 && buf[len-1] == '\r')
buf[--len] = 0;
}
return 1;
}
return 0;
}
/* md5 hash */
static char *get_hash(const char *data, size_t len)
@ -95,8 +78,6 @@ static char *get_hash(const char *data, size_t len)
return util_bin_to_hex(digest, 16);
}
#define MAX_LINE_LEN 512
static int compare_users (void *arg, void *a, void *b)
{
@ -191,7 +172,7 @@ static void htpasswd_recheckfile (htpasswd_auth_state *htpasswd)
static auth_result htpasswd_auth (auth_client *auth_user)
{
auth_t *auth = auth_user->client->auth;
auth_t *auth = auth_user->auth;
htpasswd_auth_state *htpasswd = auth->state;
client_t *client = auth_user->client;
htpasswd_user entry;

View File

@ -17,7 +17,7 @@
#include <config.h>
#endif
void auth_get_htpasswd_auth (auth_t *auth, config_options_t *options);
int auth_get_htpasswd_auth (auth_t *auth, config_options_t *options);
#endif

View File

@ -33,7 +33,7 @@
* On client disconnection another request can be sent to a URL with the POST
* information of
*
* action=listener_remove&server=host&port=8000&client=1&mount=/live&user=fred&pass=mypass&duration=3600
* action=listener_remove&server=host&port=8000&client=1&mount=/live&user=fred&pass=mypass&ip=127.0.0.1&duration=3600
*
* client refers to the icecast client identification number. mount refers
* to the mountpoint (beginning with / and may contain query parameters eg ?&
@ -46,6 +46,15 @@
*
* action=mount_add&mount=/live&server=myserver.com&port=8000
* action=mount_remove&mount=/live&server=myserver.com&port=8000
*
* On source client connection, a request can be made to trigger a URL request
* to verify the details externally. Post info is
*
* action=stream_auth&mount=/stream&ip=IP&server=SERVER&port=8000&user=fred&pass=pass
*
* As admin requests can come in for a stream (eg metadata update) these requests
* can be issued while stream is active. For these &admin=1 is added to the POST
* details.
*/
#ifdef HAVE_CONFIG_H
@ -58,10 +67,9 @@
#include <stdio.h>
#ifndef _WIN32
#include <sys/wait.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#else
#define snprintf _snprintf
#define strncasecmp strnicmp
#endif
#include <curl/curl.h>
@ -75,6 +83,15 @@
#include "logging.h"
#define CATMODULE "auth_url"
typedef struct
{
int id;
CURL *curl;
char *server_id;
char *location;
char errormsg [CURL_ERROR_SIZE];
} auth_thread_data;
typedef struct {
char *addurl;
char *removeurl;
@ -88,8 +105,6 @@ typedef struct {
char *timelimit_header;
int timelimit_header_len;
char *userpwd;
CURL *handle;
char errormsg [CURL_ERROR_SIZE];
} auth_url;
@ -99,7 +114,7 @@ static void auth_url_clear(auth_t *self)
INFO0 ("Doing auth URL cleanup");
url = self->state;
curl_easy_cleanup (url->handle);
self->state = NULL;
free (url->username);
free (url->password);
free (url->removeurl);
@ -114,12 +129,14 @@ static void auth_url_clear(auth_t *self)
}
#ifdef CURLOPT_PASSWDFUNCTION
/* make sure that prompting at the console does not occur */
static int my_getpass(void *client, char *prompt, char *buffer, int buflen)
{
buffer[0] = '\0';
return 0;
}
#endif
static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
@ -127,10 +144,11 @@ static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *s
auth_client *auth_user = stream;
unsigned bytes = size * nmemb;
client_t *client = auth_user->client;
auth_thread_data *atd = auth_user->thread_data;
if (client)
{
auth_t *auth = client->auth;
auth_t *auth = auth_user->auth;
auth_url *url = auth->state;
int retcode = 0;
@ -139,8 +157,8 @@ static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *s
if (retcode == 403)
{
char *p = strchr (ptr, ' ') + 1;
snprintf (url->errormsg, sizeof(url->errormsg), p);
p = strchr (url->errormsg, '\r');
snprintf (atd->errormsg, sizeof(atd->errormsg), p);
p = strchr (atd->errormsg, '\r');
if (p) *p='\0';
}
}
@ -155,13 +173,19 @@ static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *s
if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0)
{
char *eol;
snprintf (url->errormsg, sizeof (url->errormsg), "%s", (char*)ptr+22);
eol = strchr (url->errormsg, '\r');
snprintf (atd->errormsg, sizeof (atd->errormsg), "%s", (char*)ptr+22);
eol = strchr (atd->errormsg, '\r');
if (eol == NULL)
eol = strchr (url->errormsg, '\n');
eol = strchr (atd->errormsg, '\n');
if (eol)
*eol = '\0';
}
if (strncasecmp (ptr, "Location: ", 10) == 0)
{
int len = strcspn ((char*)ptr+10, "\r\n");
atd->location = malloc (len+1);
snprintf (atd->location, len+1, "%s", (char *)ptr+10);
}
}
return (int)bytes;
@ -174,23 +198,19 @@ static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *str
}
static auth_result url_remove_client (auth_client *auth_user)
static auth_result url_remove_listener (auth_client *auth_user)
{
client_t *client = auth_user->client;
auth_t *auth = client->auth;
auth_url *url = auth->state;
auth_url *url = auth_user->auth->state;
auth_thread_data *atd = auth_user->thread_data;
time_t duration = global.time - client->con->con_time;
char *username, *password, *mount, *server;
ice_config_t *config;
int port;
char *username, *password, *mount, *server, *ipaddr;
const char *qargs;
char *userpwd = NULL, post [4096];
if (url->removeurl == NULL)
return AUTH_OK;
config = config_get_config ();
server = util_url_escape (config->hostname);
port = config->port;
config_release_config ();
server = util_url_escape (auth_user->hostname);
if (client->username)
username = util_url_escape (client->username);
@ -203,16 +223,17 @@ static auth_result url_remove_client (auth_client *auth_user)
password = strdup ("");
/* get the full uri (with query params if available) */
mount = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
if (mount == NULL)
mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
mount = util_url_escape (mount);
qargs = httpp_getvar (client->parser, HTTPP_VAR_QUERYARGS);
snprintf (post, sizeof post, "%s%s", auth_user->mount, qargs ? qargs : "");
mount = util_url_escape (post);
ipaddr = util_url_escape (client->con->ip);
snprintf (post, sizeof (post),
"action=listener_remove&server=%s&port=%d&client=%lu&mount=%s"
"&user=%s&pass=%s&duration=%lu",
server, port, client->con->id, mount, username,
password, (long unsigned)duration);
"&user=%s&pass=%s&ip=%s&duration=%lu",
server, auth_user->port, client->con->id, mount, username,
password, ipaddr, (long unsigned)duration);
free (ipaddr);
free (server);
free (mount);
free (username);
@ -221,7 +242,7 @@ static auth_result url_remove_client (auth_client *auth_user)
if (strchr (url->removeurl, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd);
else
{
/* auth'd requests may not have a user/pass, but may use query args */
@ -230,23 +251,26 @@ static auth_result url_remove_client (auth_client *auth_user)
int len = strlen (client->username) + strlen (client->password) + 2;
userpwd = malloc (len);
snprintf (userpwd, len, "%s:%s", client->username, client->password);
curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, userpwd);
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
}
}
else
{
/* url has user/pass but libcurl may need to clear any existing settings */
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
}
curl_easy_setopt (url->handle, CURLOPT_URL, url->removeurl);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
curl_easy_setopt (atd->curl, CURLOPT_URL, url->removeurl);
curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user);
if (curl_easy_perform (url->handle))
WARN2 ("auth to server %s failed with %s", url->removeurl, url->errormsg);
DEBUG1 ("...handler %d sending request", auth_user->handler);
if (curl_easy_perform (atd->curl))
WARN2 ("auth to server %s failed with %s", url->removeurl, atd->errormsg);
else
DEBUG1 ("...handler %d request complete", auth_user->handler);
free (userpwd);
@ -254,13 +278,15 @@ static auth_result url_remove_client (auth_client *auth_user)
}
static auth_result url_add_client (auth_client *auth_user)
static auth_result url_add_listener (auth_client *auth_user)
{
client_t *client = auth_user->client;
auth_t *auth = client->auth;
auth_t *auth = auth_user->auth;
auth_url *url = auth->state;
auth_thread_data *atd = auth_user->thread_data;
int res = 0, port;
char *agent, *user_agent, *username, *password;
const char *agent, *qargs;
char *user_agent, *username, *password;
char *mount, *ipaddr, *server;
ice_config_t *config;
char *userpwd = NULL, post [4096];
@ -286,10 +312,9 @@ static auth_result url_add_client (auth_client *auth_user)
password = strdup ("");
/* get the full uri (with query params if available) */
mount = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
if (mount == NULL)
mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
mount = util_url_escape (mount);
qargs = httpp_getvar (client->parser, HTTPP_VAR_QUERYARGS);
snprintf (post, sizeof post, "%s%s", auth_user->mount, qargs ? qargs : "");
mount = util_url_escape (post);
ipaddr = util_url_escape (client->con->ip);
snprintf (post, sizeof (post),
@ -307,7 +332,7 @@ static auth_result url_add_client (auth_client *auth_user)
if (strchr (url->addurl, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd);
else
{
/* auth'd requests may not have a user/pass, but may use query args */
@ -316,40 +341,52 @@ static auth_result url_add_client (auth_client *auth_user)
int len = strlen (client->username) + strlen (client->password) + 2;
userpwd = malloc (len);
snprintf (userpwd, len, "%s:%s", client->username, client->password);
curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, userpwd);
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
}
}
else
{
/* url has user/pass but libcurl may need to clear any existing settings */
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
}
curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
url->errormsg[0] = '\0';
curl_easy_setopt (atd->curl, CURLOPT_URL, url->addurl);
curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user);
atd->errormsg[0] = '\0';
res = curl_easy_perform (url->handle);
DEBUG1 ("handler %d sending request", auth_user->handler);
res = curl_easy_perform (atd->curl);
DEBUG1 ("handler %d request finished", auth_user->handler);
free (userpwd);
if (res)
{
WARN2 ("auth to server %s failed with %s", url->addurl, url->errormsg);
WARN2 ("auth to server %s failed with %s", url->addurl, atd->errormsg);
client_send_403 (client, "Unable to contact auth server");
auth_user->client = NULL;
return AUTH_FAILED;
}
if (atd->location)
{
client_send_302 (client, atd->location+10);
auth_user->client = NULL;
free (atd->location);
atd->location = NULL;
return AUTH_FAILED;
}
/* we received a response, lets see what it is */
if (client->authenticated)
return AUTH_OK;
if (atoi (url->errormsg) == 403)
if (atoi (atd->errormsg) == 403)
{
client_send_403 (client, url->errormsg+4);
client_send_403 (client, atd->errormsg+4);
auth_user->client = NULL;
}
INFO2 ("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg);
INFO2 ("client auth (%s) failed with \"%s\"", url->addurl, atd->errormsg);
return AUTH_FAILED;
}
@ -357,89 +394,108 @@ static auth_result url_add_client (auth_client *auth_user)
/* called by auth thread when a source starts, there is no client_t in
* this case
*/
static void url_stream_start (auth_client *auth_user, auth_t *auth)
static void url_stream_start (auth_client *auth_user)
{
char *mount, *server;
auth_url *url = auth->state;
char *mount, *server, *ipaddr;
client_t *client = auth_user->client;
auth_url *url = auth_user->auth->state;
auth_thread_data *atd = auth_user->thread_data;
char post [4096];
server = util_url_escape (auth_user->hostname);
mount = util_url_escape (auth_user->mount);
if (client && client->con)
ipaddr = util_url_escape (client->con->ip);
else
ipaddr = strdup("");
snprintf (post, sizeof (post),
"action=mount_add&mount=%s&server=%s&port=%d", mount, server, auth_user->port);
"action=mount_add&mount=%s&server=%s&port=%d&ip=%s", mount, server, auth_user->port, ipaddr);
free (ipaddr);
free (server);
free (mount);
if (strchr (url->stream_start, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd);
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_start);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_URL, url->stream_start);
curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user);
if (curl_easy_perform (url->handle))
WARN2 ("auth to server %s failed with %s", url->stream_start, url->errormsg);
DEBUG1 ("handler %d sending request", auth_user->handler);
if (curl_easy_perform (atd->curl))
WARN2 ("auth to server %s failed with %s", url->stream_start, atd->errormsg);
DEBUG1 ("handler %d request finished", auth_user->handler);
}
static void url_stream_end (auth_client *auth_user, auth_t *auth)
static void url_stream_end (auth_client *auth_user)
{
char *mount, *server;
auth_url *url = auth->state;
char *mount, *server, *ipaddr;
client_t *client = auth_user->client;
auth_url *url = auth_user->auth->state;
auth_thread_data *atd = auth_user->thread_data;
char post [4096];
server = util_url_escape (auth_user->hostname);
mount = util_url_escape (auth_user->mount);
if (client && client->con)
ipaddr = util_url_escape (client->con->ip);
else
ipaddr = strdup("");
snprintf (post, sizeof (post),
"action=mount_remove&mount=%s&server=%s&port=%d", mount, server, auth_user->port);
"action=mount_remove&mount=%s&server=%s&port=%d&ip=%s", mount, server, auth_user->port, ipaddr);
free (ipaddr);
free (server);
free (mount);
if (strchr (url->stream_end, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd);
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_end);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_URL, url->stream_end);
curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user);
if (curl_easy_perform (url->handle))
WARN2 ("auth to server %s failed with %s", url->stream_end, url->errormsg);
DEBUG1 ("handler %d sending request", auth_user->handler);
if (curl_easy_perform (atd->curl))
WARN2 ("auth to server %s failed with %s", url->stream_end, atd->errormsg);
DEBUG1 ("handler %d request finished", auth_user->handler);
}
static void url_stream_auth (auth_client *auth_user)
{
client_t *client = auth_user->client;
auth_url *url = client->auth->state;
auth_url *url = auth_user->auth->state;
auth_thread_data *atd = auth_user->thread_data;
char *mount, *host, *user, *pass, *ipaddr, *admin="";
char post [4096];
if (strchr (url->stream_auth, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, url->userpwd);
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_auth);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
curl_easy_setopt (atd->curl, CURLOPT_USERPWD, "");
curl_easy_setopt (atd->curl, CURLOPT_URL, url->stream_auth);
curl_easy_setopt (atd->curl, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (atd->curl, CURLOPT_WRITEHEADER, auth_user);
if (strncmp (auth_user->mount, "/admin/", 7) == 0)
{
mount = util_url_escape (httpp_get_query_param (client->parser, "mount"));
@ -462,8 +518,8 @@ static void url_stream_auth (auth_client *auth_user)
free (host);
client->authenticated = 0;
if (curl_easy_perform (url->handle))
WARN2 ("auth to server %s failed with %s", url->stream_auth, url->errormsg);
if (curl_easy_perform (atd->curl))
WARN2 ("auth to server %s failed with %s", url->stream_auth, atd->errormsg);
}
@ -482,6 +538,39 @@ static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode)
return AUTH_FAILED;
}
/* This is called with the config lock held */
static void *alloc_thread_data (auth_t *auth)
{
auth_thread_data *atd = calloc (1, sizeof (auth_thread_data));
ice_config_t *config = config_get_config_unlocked();
atd->server_id = strdup (config->server_id);
atd->curl = curl_easy_init ();
curl_easy_setopt (atd->curl, CURLOPT_USERAGENT, atd->server_id);
curl_easy_setopt (atd->curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
curl_easy_setopt (atd->curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
curl_easy_setopt (atd->curl, CURLOPT_WRITEDATA, atd);
curl_easy_setopt (atd->curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (atd->curl, CURLOPT_TIMEOUT, 6L);
#ifdef CURLOPT_PASSWDFUNCTION
curl_easy_setopt (atd->curl, CURLOPT_PASSWDFUNCTION, my_getpass);
#endif
curl_easy_setopt (atd->curl, CURLOPT_ERRORBUFFER, &atd->errormsg[0]);
INFO0 ("...handler data initialized");
return atd;
}
static void release_thread_data (auth_t *auth, void *thread_data)
{
auth_thread_data *atd = thread_data;
curl_easy_cleanup (atd->curl);
free (atd->server_id);
free (atd);
INFO1 ("...handler destroyed for %s", auth->mount);
}
int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
{
auth_url *url_info;
@ -490,6 +579,8 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
authenticator->adduser = auth_url_adduser;
authenticator->deleteuser = auth_url_deleteuser;
authenticator->listuser = auth_url_listuser;
authenticator->alloc_thread_data = alloc_thread_data;
authenticator->release_thread_data = release_thread_data;
url_info = calloc(1, sizeof(auth_url));
url_info->auth_header = strdup ("icecast-auth-user: 1\r\n");
@ -497,32 +588,43 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
while(options) {
if(!strcmp(options->name, "username"))
{
free (url_info->username);
url_info->username = strdup (options->value);
}
if(!strcmp(options->name, "password"))
{
free (url_info->password);
url_info->password = strdup (options->value);
}
if(!strcmp(options->name, "listener_add"))
{
authenticator->authenticate = url_add_client;
authenticator->authenticate = url_add_listener;
free (url_info->addurl);
url_info->addurl = strdup (options->value);
}
if(!strcmp(options->name, "listener_remove"))
{
authenticator->release_client = url_remove_client;
authenticator->release_listener = url_remove_listener;
free (url_info->removeurl);
url_info->removeurl = strdup (options->value);
}
if(!strcmp(options->name, "mount_add"))
{
authenticator->stream_start = url_stream_start;
free (url_info->stream_start);
url_info->stream_start = strdup (options->value);
}
if(!strcmp(options->name, "mount_remove"))
{
authenticator->stream_end = url_stream_end;
free (url_info->stream_end);
url_info->stream_end = strdup (options->value);
}
if(!strcmp(options->name, "stream_auth"))
{
authenticator->stream_auth = url_stream_auth;
free (url_info->stream_auth);
url_info->stream_auth = strdup (options->value);
}
if(!strcmp(options->name, "auth_header"))
@ -537,27 +639,11 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
}
options = options->next;
}
url_info->handle = curl_easy_init ();
if (url_info->handle == NULL)
{
free (url_info);
return -1;
}
if (url_info->auth_header)
url_info->auth_header_len = strlen (url_info->auth_header);
if (url_info->timelimit_header)
url_info->timelimit_header_len = strlen (url_info->timelimit_header);
curl_easy_setopt (url_info->handle, CURLOPT_USERAGENT,
config_get_config_unlocked()->server_id);
curl_easy_setopt (url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
curl_easy_setopt (url_info->handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
curl_easy_setopt (url_info->handle, CURLOPT_WRITEDATA, url_info->handle);
curl_easy_setopt (url_info->handle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (url_info->handle, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt (url_info->handle, CURLOPT_PASSWDFUNCTION, my_getpass);
curl_easy_setopt (url_info->handle, CURLOPT_ERRORBUFFER, &url_info->errormsg[0]);
if (url_info->username && url_info->password)
{
int len = strlen (url_info->username) + strlen (url_info->password) + 2;
@ -566,7 +652,6 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
}
authenticator->state = url_info;
INFO0("URL based authentication setup");
return 0;
}

View File

@ -17,7 +17,7 @@
#include <config.h>
#endif
int *auth_get_url_auth (auth_t *authenticator, config_options_t *options);
int auth_get_url_auth (auth_t *authenticator, config_options_t *options);
#endif

View File

@ -20,6 +20,9 @@
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#ifdef HAVE_FNMATCH_H
#include <fnmatch.h>
#endif
#include "thread/thread.h"
#include "cfgfile.h"
#include "refbuf.h"
@ -48,7 +51,7 @@
#define CONFIG_DEFAULT_PLAYLIST_LOG NULL
#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
#define CONFIG_DEFAULT_ERROR_LOG "error.log"
#define CONFIG_DEFAULT_LOG_LEVEL 4
#define CONFIG_DEFAULT_LOG_LEVEL 3
#define CONFIG_DEFAULT_CHROOT 0
#define CONFIG_DEFAULT_CHUID 0
#define CONFIG_MASTER_UPDATE_INTERVAL 120
@ -73,7 +76,7 @@ static void _set_defaults(ice_config_t *c);
static int _parse_root (xmlNodePtr node, ice_config_t *config);
static void create_locks(void) {
thread_mutex_create("relay lock", &_locks.relay_lock);
thread_mutex_create("relay", &_locks.relay_lock);
thread_rwlock_create(&_locks.config_lock);
}
@ -82,6 +85,7 @@ static void release_locks(void) {
thread_rwlock_destroy(&_locks.config_lock);
}
/*
*/
struct cfg_tag
@ -98,7 +102,7 @@ int config_get_bool (xmlNodePtr node, void *x)
{
char *str = (char *)xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
if (str == NULL)
return -1;
return 1;
if (strcasecmp (str, "true") == 0)
*(int*)x = 1;
else
@ -115,7 +119,7 @@ int config_get_str (xmlNodePtr node, void *x)
xmlChar *str = xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
xmlChar *p = *(xmlChar**)x;
if (str == NULL)
return -1;
return 1;
if (p)
xmlFree (p);
*(xmlChar **)x = str;
@ -126,12 +130,29 @@ int config_get_int (xmlNodePtr node, void *x)
{
xmlChar *str = xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
if (str == NULL)
return -1;
return 1;
*(int*)x = strtol ((char*)str, NULL, 0);
xmlFree (str);
return 0;
}
int config_get_bitrate (xmlNodePtr node, void *x)
{
xmlChar *str = xmlNodeListGetString (node->doc, node->xmlChildrenNode, 1);
int64_t *p = (int64_t*)x;
char metric = '\0';
if (str == NULL)
return 1;
sscanf ((char*)str, "%"SCNd64 "%c", p, &metric);
if (metric == 'k' || metric == 'K')
(*p) *= 1024;
if (metric == 'm' || metric == 'M')
(*p) *= 1024*1024;
xmlFree (str);
return 0;
}
int parse_xml_tags (xmlNodePtr parent, const struct cfg_tag *args)
{
@ -151,12 +172,17 @@ int parse_xml_tags (xmlNodePtr parent, const struct cfg_tag *args)
if (strcmp ((const char*)node->name, argp->name) == 0)
{
ret = argp->retrieve (node, argp->storage);
if (ret > 0)
{
xmlParserWarning(stderr, "skipping element \"%s\" parsing \"%s\"\n", node->name, parent->name);
ret = 0;
}
break;
}
argp++;
}
if (argp->name == NULL)
WARN2 ("unknown element \"%s\" parsing \"%s\"\n", node->name, parent->name);
WARN2 ("unknown element \"%s\" parsing \"%s\"", node->name, parent->name);
}
return ret;
}
@ -182,9 +208,16 @@ void config_init_configuration(ice_config_t *configuration)
static void config_clear_relay (relay_server *relay)
{
xmlFree (relay->server);
xmlFree (relay->mount);
xmlFree (relay->localmount);
while (relay->masters)
{
relay_server_master *master = relay->masters;
relay->masters = master->next;
if (master->ip) xmlFree (master->ip);
if (master->bind) xmlFree (master->bind);
if (master->mount) xmlFree (master->mount);
free (master);
}
if (relay->localmount) xmlFree (relay->localmount);
free (relay);
}
@ -193,31 +226,31 @@ static void config_clear_mount (mount_proxy *mount)
{
config_options_t *option;
xmlFree (mount->mountname);
xmlFree (mount->username);
xmlFree (mount->password);
xmlFree (mount->dumpfile);
xmlFree (mount->intro_filename);
xmlFree (mount->on_connect);
xmlFree (mount->on_disconnect);
xmlFree (mount->fallback_mount);
xmlFree (mount->stream_name);
xmlFree (mount->stream_description);
xmlFree (mount->stream_url);
xmlFree (mount->stream_genre);
xmlFree (mount->bitrate);
xmlFree (mount->type);
xmlFree (mount->subtype);
xmlFree (mount->charset);
xmlFree (mount->cluster_password);
if (mount->mountname) xmlFree (mount->mountname);
if (mount->username) xmlFree (mount->username);
if (mount->password) xmlFree (mount->password);
if (mount->dumpfile) xmlFree (mount->dumpfile);
if (mount->intro_filename) xmlFree (mount->intro_filename);
if (mount->on_connect) xmlFree (mount->on_connect);
if (mount->on_disconnect) xmlFree (mount->on_disconnect);
if (mount->fallback_mount) xmlFree (mount->fallback_mount);
if (mount->stream_name) xmlFree (mount->stream_name);
if (mount->stream_description) xmlFree (mount->stream_description);
if (mount->stream_url) xmlFree (mount->stream_url);
if (mount->stream_genre) xmlFree (mount->stream_genre);
if (mount->bitrate) xmlFree (mount->bitrate);
if (mount->type) xmlFree (mount->type);
if (mount->subtype) xmlFree (mount->subtype);
if (mount->charset) xmlFree (mount->charset);
if (mount->cluster_password) xmlFree (mount->cluster_password);
xmlFree (mount->auth_type);
if (mount->auth_type) xmlFree (mount->auth_type);
option = mount->auth_options;
while (option)
{
config_options_t *nextopt = option->next;
xmlFree (option->name);
xmlFree (option->value);
if (option->name) xmlFree (option->name);
if (option->value) xmlFree (option->value);
free (option);
option = nextopt;
}
@ -225,6 +258,22 @@ static void config_clear_mount (mount_proxy *mount)
free (mount);
}
listener_t *config_clear_listener (listener_t *listener)
{
listener_t *next = NULL;
if (listener)
{
next = listener->next;
listener->refcount--;
if (listener->refcount == 0)
{
if (listener->bind_address) xmlFree (listener->bind_address);
if (listener->shoutcast_mount) xmlFree (listener->shoutcast_mount);
free (listener);
}
}
return next;
}
void config_clear(ice_config_t *c)
{
@ -232,65 +281,49 @@ void config_clear(ice_config_t *c)
aliases *alias, *nextalias;
int i;
if (c->config_filename)
free(c->config_filename);
free(c->config_filename);
xmlFree (c->server_id);
if (c->location && c->location != CONFIG_DEFAULT_LOCATION)
xmlFree(c->location);
if (c->admin && c->admin != CONFIG_DEFAULT_ADMIN)
xmlFree(c->admin);
if (c->source_password && c->source_password != CONFIG_DEFAULT_SOURCE_PASSWORD)
xmlFree(c->source_password);
if (c->admin_username)
xmlFree(c->admin_username);
if (c->admin_password)
xmlFree(c->admin_password);
if (c->relay_username)
xmlFree(c->relay_username);
if (c->relay_password)
xmlFree(c->relay_password);
if (c->hostname && c->hostname != CONFIG_DEFAULT_HOSTNAME)
xmlFree(c->hostname);
if (c->base_dir && c->base_dir != CONFIG_DEFAULT_BASE_DIR)
xmlFree(c->base_dir);
if (c->log_dir && c->log_dir != CONFIG_DEFAULT_LOG_DIR)
xmlFree(c->log_dir);
if (c->webroot_dir && c->webroot_dir != CONFIG_DEFAULT_WEBROOT_DIR)
xmlFree(c->webroot_dir);
if (c->adminroot_dir && c->adminroot_dir != CONFIG_DEFAULT_ADMINROOT_DIR)
xmlFree(c->adminroot_dir);
if (c->cert_file)
xmlFree(c->cert_file);
if (c->pidfile)
xmlFree(c->pidfile);
if (c->playlist_log && c->playlist_log != CONFIG_DEFAULT_PLAYLIST_LOG)
xmlFree(c->playlist_log);
if (c->access_log && c->access_log != CONFIG_DEFAULT_ACCESS_LOG)
xmlFree(c->access_log);
if (c->error_log && c->error_log != CONFIG_DEFAULT_ERROR_LOG)
xmlFree(c->error_log);
xmlFree (c->access_log_exclude_ext);
if (c->shoutcast_mount && c->shoutcast_mount != CONFIG_DEFAULT_SHOUTCAST_MOUNT)
xmlFree(c->shoutcast_mount);
for(i=0; i < MAX_LISTEN_SOCKETS; i++) {
if (c->listeners[i].bind_address) xmlFree(c->listeners[i].bind_address);
xmlFree (c->listeners[i].shoutcast_mount);
}
if (c->location) xmlFree(c->location);
if (c->admin) xmlFree(c->admin);
if (c->source_password) xmlFree(c->source_password);
if (c->admin_username) xmlFree(c->admin_username);
if (c->admin_password) xmlFree(c->admin_password);
if (c->relay_username) xmlFree(c->relay_username);
if (c->relay_password) xmlFree(c->relay_password);
if (c->hostname) xmlFree(c->hostname);
if (c->base_dir) xmlFree(c->base_dir);
if (c->log_dir) xmlFree(c->log_dir);
if (c->webroot_dir) xmlFree(c->webroot_dir);
if (c->adminroot_dir) xmlFree(c->adminroot_dir);
if (c->cert_file) xmlFree(c->cert_file);
if (c->pidfile) xmlFree(c->pidfile);
if (c->banfile) xmlFree(c->banfile);
if (c->allowfile) xmlFree (c->allowfile);
if (c->agentfile) xmlFree (c->agentfile);
if (c->playlist_log) xmlFree(c->playlist_log);
if (c->access_log) xmlFree(c->access_log);
if (c->error_log) xmlFree(c->error_log);
if (c->access_log_exclude_ext) xmlFree (c->access_log_exclude_ext);
if (c->shoutcast_mount) xmlFree(c->shoutcast_mount);
while ((c->listen_sock = config_clear_listener (c->listen_sock)))
;
if (c->master_server) xmlFree(c->master_server);
if (c->master_username) xmlFree(c->master_username);
if (c->master_password) xmlFree(c->master_password);
if (c->master_bind) xmlFree(c->master_bind);
if (c->user) xmlFree(c->user);
if (c->group) xmlFree(c->group);
if (c->mimetypes_fn) xmlFree (c->mimetypes_fn);
thread_mutex_lock(&(_locks.relay_lock));
while (c->relay)
{
relay_server *to_go = c->relay;
c->relay = to_go->next;
config_clear_relay (to_go);
}
thread_mutex_unlock(&(_locks.relay_lock));
while (c->mounts)
{
@ -301,9 +334,9 @@ void config_clear(ice_config_t *c)
alias = c->aliases;
while(alias) {
nextalias = alias->next;
xmlFree(alias->source);
xmlFree(alias->destination);
xmlFree(alias->bind_address);
if (alias->source) xmlFree(alias->source);
if (alias->destination) xmlFree(alias->destination);
if (alias->bind_address) xmlFree(alias->bind_address);
free(alias);
alias = nextalias;
}
@ -311,7 +344,7 @@ void config_clear(ice_config_t *c)
dirnode = c->dir_list;
while(dirnode) {
nextdirnode = dirnode->next;
xmlFree(dirnode->host);
if (dirnode->host) xmlFree(dirnode->host);
free(dirnode);
dirnode = nextdirnode;
}
@ -319,7 +352,7 @@ void config_clear(ice_config_t *c)
i = 0;
while (i < c->num_yp_directories)
{
xmlFree (c->yp_url[i]);
if (c->yp_url[i]) xmlFree (c->yp_url[i]);
i++;
}
#endif
@ -427,14 +460,12 @@ static void _set_defaults(ice_config_t *configuration)
configuration->dir_list = NULL;
configuration->hostname = (char *)xmlCharStrdup (CONFIG_DEFAULT_HOSTNAME);
configuration->port = 0;
configuration->listeners[0].port = 0;
configuration->listeners[0].bind_address = NULL;
configuration->listeners[0].shoutcast_compat = 0;
configuration->master_server = NULL;
configuration->master_server_port = 0;
configuration->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
configuration->master_username = (char*)xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
configuration->master_password = NULL;
configuration->master_bind = NULL;
configuration->master_relay_auth = 0;
configuration->base_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_BASE_DIR);
configuration->log_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_LOG_DIR);
@ -442,6 +473,7 @@ static void _set_defaults(ice_config_t *configuration)
configuration->adminroot_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_ADMINROOT_DIR);
configuration->playlist_log = (char *)xmlCharStrdup (CONFIG_DEFAULT_PLAYLIST_LOG);
configuration->access_log = (char *)xmlCharStrdup (CONFIG_DEFAULT_ACCESS_LOG);
configuration->access_log_ip = 1;
configuration->error_log = (char *)xmlCharStrdup (CONFIG_DEFAULT_ERROR_LOG);
configuration->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
configuration->chroot = CONFIG_DEFAULT_CHROOT;
@ -467,8 +499,8 @@ static int _parse_alias (xmlNodePtr node, void *arg)
alias->destination = (char *)xmlGetProp (node, XMLSTR ("dest"));
if (alias->source == NULL || alias->destination == NULL)
{
if (alias->source) xmlFree (XMLSTR (alias->source));
if (alias->destination) xmlFree (XMLSTR (alias->destination));
if (alias->source) xmlFree (alias->source);
if (alias->destination) xmlFree (alias->destination);
free (alias);
WARN0 ("incomplete alias definition");
return -1;
@ -542,6 +574,7 @@ static int _parse_logging (xmlNodePtr node, void *arg)
struct cfg_tag icecast_tags[] =
{
{ "accesslog", config_get_str, &config->access_log },
{ "accesslog_ip", config_get_bool, &config->access_log_ip },
{ "accesslog_exclude_ext",
config_get_str, &config->access_log_exclude_ext },
{ "accesslog_lines",
@ -571,17 +604,24 @@ static int _parse_paths (xmlNodePtr node, void *arg)
ice_config_t *config = arg;
struct cfg_tag icecast_tags[] =
{
{ "basedir", config_get_str, &config->base_dir },
{ "logdir", config_get_str, &config->log_dir },
{ "pidfile", config_get_str, &config->pidfile },
{ "ssl_certificate",
config_get_str, &config->cert_file },
{ "webroot", config_get_str, &config->webroot_dir },
{ "adminroot", config_get_str, &config->adminroot_dir },
{ "alias", _parse_alias, config },
{ "basedir", config_get_str, &config->base_dir },
{ "logdir", config_get_str, &config->log_dir },
{ "mime-types", config_get_str, &config->mimetypes_fn },
{ "pidfile", config_get_str, &config->pidfile },
{ "banfile", config_get_str, &config->banfile },
{ "ban-file", config_get_str, &config->banfile },
{ "deny-ip", config_get_str, &config->banfile },
{ "allow-ip", config_get_str, &config->allowfile },
{ "deny-agents", config_get_str, &config->agentfile },
{ "ssl-certificate",config_get_str, &config->cert_file },
{ "ssl_certificate",config_get_str, &config->cert_file },
{ "webroot", config_get_str, &config->webroot_dir },
{ "adminroot", config_get_str, &config->adminroot_dir },
{ "alias", _parse_alias, config },
{ NULL, NULL, NULL }
};
config->mimetypes_fn = (char *)xmlCharStrdup (MIMETYPESFILE);
if (parse_xml_tags (node, icecast_tags))
return -1;
return 0;
@ -619,62 +659,63 @@ static int _parse_mount (xmlNodePtr node, void *arg)
struct cfg_tag icecast_tags[] =
{
{ "mount-name", config_get_str, &mount->mountname },
{ "source-timeout", config_get_int, &mount->source_timeout },
{ "queue-size", config_get_int, &mount->queue_size_limit },
{ "burst-size", config_get_int, &mount->burst_size},
{ "username", config_get_str, &mount->username },
{ "password", config_get_str, &mount->password },
{ "dump-file", config_get_str, &mount->dumpfile },
{ "intro", config_get_str, &mount->intro_filename },
{ "fallback-mount", config_get_str, &mount->fallback_mount },
{ "fallback-override",
config_get_bool, &mount->fallback_override },
{ "fallback-when-full",
config_get_bool, &mount->fallback_when_full },
{ "max-listeners", config_get_int, &mount->max_listeners },
{ "wait-time", config_get_int, &mount->wait_time },
{ "filter-theora", config_get_bool, &mount->filter_theora },
{ "limit-rate", config_get_int, &mount->limit_rate },
{ "mount-name", config_get_str, &mount->mountname },
{ "source-timeout", config_get_int, &mount->source_timeout },
{ "queue-size", config_get_int, &mount->queue_size_limit },
{ "burst-size", config_get_int, &mount->burst_size},
{ "username", config_get_str, &mount->username },
{ "password", config_get_str, &mount->password },
{ "dump-file", config_get_str, &mount->dumpfile },
{ "intro", config_get_str, &mount->intro_filename },
{ "file-seekable", config_get_bool, &mount->file_seekable },
{ "fallback-mount", config_get_str, &mount->fallback_mount },
{ "fallback-override", config_get_bool, &mount->fallback_override },
{ "fallback-when-full", config_get_bool, &mount->fallback_when_full },
{ "max-listeners", config_get_int, &mount->max_listeners },
{ "max-bandwidth", config_get_bitrate, &mount->max_bandwidth },
{ "wait-time", config_get_int, &mount->wait_time },
{ "filter-theora", config_get_bool, &mount->filter_theora },
{ "limit-rate", config_get_bitrate, &mount->limit_rate },
{ "avg-bitrate-duration",
config_get_int, &mount->avg_bitrate_duration },
{ "charset", config_get_str, &mount->charset },
config_get_int, &mount->avg_bitrate_duration },
{ "charset", config_get_str, &mount->charset },
{ "qblock-size", config_get_int, &mount->queue_block_size },
{ "mp3-metadata-interval",
config_get_int, &mount->mp3_meta_interval },
{ "ogg-passthrough",
config_get_bool, &mount->ogg_passthrough },
config_get_int, &mount->mp3_meta_interval },
{ "ogg-passthrough", config_get_bool, &mount->ogg_passthrough },
{ "admin_comments_only",config_get_bool, &mount->admin_comments_only },
{ "allow-url-ogg-metadata",
config_get_bool, &mount->url_ogg_meta },
{ "no-mount", config_get_bool, &mount->no_mount },
{ "hidden", config_get_bool, &mount->hidden },
{ "authentication", auth_get_authenticator,
&mount->auth },
{ "on-connect", config_get_str, &mount->on_connect },
{ "on-disconnect", config_get_str, &mount->on_disconnect },
config_get_bool, &mount->url_ogg_meta },
{ "no-mount", config_get_bool, &mount->no_mount },
{ "hidden", config_get_bool, &mount->hidden },
{ "authentication", auth_get_authenticator, &mount->auth },
{ "on-connect", config_get_str, &mount->on_connect },
{ "on-disconnect", config_get_str, &mount->on_disconnect },
{ "max-listener-duration",
config_get_int, &mount->max_listener_duration },
config_get_int, &mount->max_listener_duration },
/* YP settings */
{ "cluster-password",
config_get_str, &mount->cluster_password },
{ "stream-name", config_get_str, &mount->stream_name },
{ "stream-description",
config_get_str, &mount->stream_description },
{ "stream-url", config_get_str, &mount->stream_url },
{ "genre", config_get_str, &mount->stream_genre },
{ "bitrate", config_get_str, &mount->bitrate },
{ "public", config_get_bool, &mount->yp_public },
{ "type", config_get_str, &mount->type },
{ "subtype", config_get_str, &mount->subtype },
{ "cluster-password", config_get_str, &mount->cluster_password },
{ "stream-name", config_get_str, &mount->stream_name },
{ "stream-description", config_get_str, &mount->stream_description },
{ "stream-url", config_get_str, &mount->stream_url },
{ "genre", config_get_str, &mount->stream_genre },
{ "bitrate", config_get_str, &mount->bitrate },
{ "public", config_get_bool, &mount->yp_public },
{ "type", config_get_str, &mount->type },
{ "subtype", config_get_str, &mount->subtype },
{ NULL, NULL, NULL },
};
/* default <mount> settings */
mount->max_listeners = -1;
mount->max_bandwidth = -1;
mount->burst_size = -1;
mount->mp3_meta_interval = -1;
mount->yp_public = -1;
mount->url_ogg_meta = 0;
mount->avg_bitrate_duration = 60;
mount->source_timeout = config->source_timeout;
mount->file_seekable = 1;
if (parse_xml_tags (node, icecast_tags))
return -1;
@ -684,8 +725,16 @@ static int _parse_mount (xmlNodePtr node, void *arg)
config_clear_mount (mount);
return -1;
}
if (mount->auth)
mount->auth->mount = strdup (mount->mountname);
if (mount->avg_bitrate_duration < 2)
mount->avg_bitrate_duration = 2;
if (mount->admin_comments_only)
mount->url_ogg_meta = 1;
if (mount->url_ogg_meta)
mount->ogg_passthrough = 0;
if (mount->queue_block_size < 100)
mount->queue_block_size = 1400;
mount->next = config->mounts;
config->mounts = mount;
@ -694,18 +743,58 @@ static int _parse_mount (xmlNodePtr node, void *arg)
}
static int _relay_master (xmlNodePtr node, void *arg)
{
relay_server *relay = arg;
relay_server_master *last, *master = calloc (1, sizeof (relay_server_master));
struct cfg_tag icecast_tags[] =
{
{ "ip", config_get_str, &master->ip },
{ "server", config_get_str, &master->ip },
{ "port", config_get_int, &master->port },
{ "mount", config_get_str, &master->mount },
{ "bind", config_get_str, &master->bind },
{ NULL, NULL, NULL },
};
/* default master details taken from the default relay settings */
master->ip = (char *)xmlCharStrdup (relay->masters->ip);
master->mount = (char *)xmlCharStrdup (relay->masters->mount);
if (relay->masters->bind)
master->bind = (char *)xmlCharStrdup (relay->masters->bind);
master->port = relay->masters->port;
if (parse_xml_tags (node, icecast_tags))
return -1;
/* place new details at the end of the list */
last = relay->masters;
while (last->next)
last = last->next;
last->next = master;
return 0;
}
static int _parse_relay (xmlNodePtr node, void *arg)
{
ice_config_t *config = arg;
relay_server *relay = calloc(1, sizeof(relay_server));
relay_server_master *master = calloc (1, sizeof (relay_server_master));
struct cfg_tag icecast_tags[] =
{
{ "server", config_get_str, &relay->server },
{ "port", config_get_int, &relay->port },
{ "mount", config_get_str, &relay->mount },
{ "master", _relay_master, relay },
{ "server", config_get_str, &master->ip },
{ "ip", config_get_str, &master->ip },
{ "bind", config_get_str, &master->bind },
{ "port", config_get_int, &master->port },
{ "mount", config_get_str, &master->mount },
{ "local-mount", config_get_str, &relay->localmount },
{ "on-demand", config_get_bool, &relay->on_demand },
{ "retry-delay", config_get_int, &relay->interval },
{ "relay-shoutcast-metadata",
config_get_bool, &relay->mp3metadata },
{ "username", config_get_str, &relay->username },
@ -716,19 +805,30 @@ static int _parse_relay (xmlNodePtr node, void *arg)
relay->mp3metadata = 1;
relay->enable = 1;
relay->interval = config->master_update_interval;
relay->on_demand = config->on_demand;
relay->port = config->port;
relay->masters = master;
/* default settings */
master->port = config->port;
master->ip = (char *)xmlCharStrdup ("127.0.0.1");
master->mount = (char*)xmlCharStrdup ("/");
if (parse_xml_tags (node, icecast_tags))
return -1;
/* check for undefined entries */
if (relay->server == NULL)
relay->server = (char *)xmlCharStrdup ("127.0.0.1");
if (relay->mount == NULL)
relay->mount = (char*)xmlCharStrdup ("/");
/* check for unspecified entries */
if (relay->localmount == NULL)
relay->localmount = (char*)xmlCharStrdup (relay->mount);
relay->localmount = (char*)xmlCharStrdup (master->mount);
/* if master is set then remove the default entry at the head of the list */
if (relay->masters->next)
{
relay->masters = relay->masters->next;
if (master->mount) xmlFree (master->mount);
if (master->ip) xmlFree (master->ip);
if (master->bind) xmlFree (master->bind);
free (master);
}
relay->next = config->relay;
config->relay = relay;
@ -742,6 +842,7 @@ static int _parse_limits (xmlNodePtr node, void *arg)
struct cfg_tag icecast_tags[] =
{
{ "max-bandwidth", config_get_bitrate,&config->max_bandwidth },
{ "clients", config_get_int, &config->client_limit },
{ "sources", config_get_int, &config->source_limit },
{ "queue-size", config_get_int, &config->queue_size_limit },
@ -756,45 +857,53 @@ static int _parse_limits (xmlNodePtr node, void *arg)
return 0;
}
static int _parse_listen_sock (xmlNodePtr node, void *arg)
{
ice_config_t *config = arg;
listener_t *listener = &config->listeners[0];
int i;
listener_t *listener = calloc (1, sizeof(listener_t));
for (i=0; i < MAX_LISTEN_SOCKETS; i++)
struct cfg_tag icecast_tags[] =
{
if (listener->port <= 0)
{
struct cfg_tag icecast_tags[] =
{
{ "port", config_get_int, &listener->port },
{ "shoutcast-compat", config_get_bool, &listener->shoutcast_compat },
{ "bind-address", config_get_str, &listener->bind_address },
{ "shoutcast-mount", config_get_str, &listener->shoutcast_mount },
{ NULL, NULL, NULL },
};
if (parse_xml_tags (node, icecast_tags))
break;
if (listener->shoutcast_mount)
{
listener_t *sc_port = listener+1;
if (i+1 < MAX_LISTEN_SOCKETS && sc_port->port <= 0)
{
sc_port->port = listener->port+1;
sc_port->shoutcast_compat = 1;
sc_port->bind_address = (char*)xmlStrdup (XMLSTR(listener->bind_address));
sc_port->shoutcast_mount= (char*)xmlStrdup (XMLSTR(listener->shoutcast_mount));
}
}
if (config->port == 0)
config->port = listener->port;
return 0;
}
listener++;
{ "port", config_get_int, &listener->port },
{ "shoutcast-compat", config_get_bool, &listener->shoutcast_compat },
{ "bind-address", config_get_str, &listener->bind_address },
{ "ssl", config_get_bool, &listener->ssl },
{ "shoutcast-mount", config_get_str, &listener->shoutcast_mount },
{ NULL, NULL, NULL },
};
listener->refcount = 1;
listener->port = 8000;
if (parse_xml_tags (node, icecast_tags))
{
config_clear_listener (listener);
return -1;
}
return -1;
listener->next = config->listen_sock;
config->listen_sock = listener;
config->listen_sock_count++;
if (listener->shoutcast_mount)
{
listener_t *sc_port = calloc (1, sizeof (listener_t));
sc_port->refcount = 1;
sc_port->port = listener->port+1;
sc_port->shoutcast_compat = 1;
sc_port->shoutcast_mount = (char*)xmlStrdup (XMLSTR(listener->shoutcast_mount));
if (listener->bind_address)
sc_port->bind_address = (char*)xmlStrdup (XMLSTR(listener->bind_address));
sc_port->next = config->listen_sock;
config->listen_sock = sc_port;
config->listen_sock_count++;
}
else
listener->shoutcast_mount = (char*)xmlStrdup (XMLSTR(config->shoutcast_mount));
if (config->port == 0)
config->port = listener->port;
return 0;
}
@ -805,6 +914,7 @@ static int _parse_root (xmlNodePtr node, ice_config_t *config)
{ "location", config_get_str, &config->location },
{ "admin", config_get_str, &config->admin },
{ "server_id", config_get_str, &config->server_id },
{ "server-id", config_get_str, &config->server_id },
{ "source-password", config_get_str, &config->source_password },
{ "hostname", config_get_str, &config->hostname },
{ "port", config_get_int, &config->port },
@ -813,14 +923,14 @@ static int _parse_root (xmlNodePtr node, ice_config_t *config)
{ "master-server", config_get_str, &config->master_server },
{ "master-username", config_get_str, &config->master_username },
{ "master-password", config_get_str, &config->master_password },
{ "master-bind", config_get_str, &config->master_bind },
{ "master-server-port", config_get_int, &config->master_server_port },
{ "master-update-interval",
config_get_int, &config->master_update_interval },
{ "master-relay-auth", config_get_bool, &config->master_relay_auth },
{ "master-ssl-port", config_get_int, &config->master_ssl_port },
{ "master-redirect", config_get_bool, &config->master_redirect },
{ "max-redirect-slaves",
config_get_int, &config->max_redirects },
{ "max-redirect-slaves",config_get_int, &config->max_redirects },
{ "shoutcast-mount", config_get_str, &config->shoutcast_mount },
{ "listen-socket", _parse_listen_sock, config },
{ "limits", _parse_limits, config },
@ -833,12 +943,13 @@ static int _parse_root (xmlNodePtr node, ice_config_t *config)
{ "authentication", _parse_authentication, config},
{ NULL, NULL, NULL }
};
if (parse_xml_tags (node, icecast_tags))
return -1;
if (config->max_redirects == 0 && config->master_redirect)
config->max_redirects = 1;
if (config->listeners[0].port <= 0)
if (config->listen_sock_count == 0)
{
WARN0 ("No listen-socket defintions");
return -1;
@ -850,20 +961,18 @@ static int _parse_root (xmlNodePtr node, ice_config_t *config)
/* return the mount details that match the supplied mountpoint */
mount_proxy *config_find_mount (ice_config_t *config, const char *mount)
{
mount_proxy *mountinfo = config->mounts, *global = NULL;
mount_proxy *mountinfo = config->mounts, *to_return = NULL;
if (mount == NULL)
return NULL;
while (mountinfo)
{
if (xmlStrcmp (XMLSTR(mountinfo->mountname), XMLSTR ("all")) == 0)
global = mountinfo;
if (xmlStrcmp (XMLSTR(mountinfo->mountname), XMLSTR(mount)) == 0)
break;
if (fnmatch (mountinfo->mountname, mount, FNM_PATHNAME) == 0)
to_return = mountinfo;
mountinfo = mountinfo->next;
}
if (mountinfo == NULL)
mountinfo = global;
mountinfo = to_return;
return mountinfo;
}

View File

@ -23,11 +23,14 @@
#define XMLSTR (xmlChar *)
struct _mount_proxy;
struct ice_config_tag;
typedef struct _listener_t listener_t;
#include "thread/thread.h"
#include "avl/avl.h"
#include "auth.h"
#include "global.h"
#include "connection.h"
typedef struct ice_config_dir_tag
{
@ -51,8 +54,15 @@ typedef struct _mount_proxy {
char *dumpfile; /* Filename to dump this stream to (will be appended). NULL
to not dump. */
char *intro_filename; /* Send contents of file to client before the stream */
/* whether to allow matching files to work with http ranges */
int file_seekable;
int fallback_when_full; /* switch new listener to fallback source
when max listeners reached */
/* Max bandwidth (kbps) for this mountpoint only. -1 (default) is not specified */
int64_t max_bandwidth;
int max_listeners; /* Max listeners for this mountpoint only. -1 to not
limit here (i.e. only use the global limit) */
char *fallback_mount; /* Fallback mountname */
@ -68,15 +78,17 @@ typedef struct _mount_proxy {
unsigned int source_timeout; /* source timeout in seconds */
char *charset; /* character set if not utf8 */
int mp3_meta_interval; /* outgoing per-stream metadata interval */
int queue_block_size; /* for non-ogg streams, try to create blocks of this size */
int filter_theora; /* prevent theora pages getting queued */
int url_ogg_meta; /* enable to allow updates via url requests for ogg */
int ogg_passthrough; /* enable to prevent the ogg stream being rebuilt */
int admin_comments_only; /* enable to only show comments set from the admin page */
/* duration in seconds for sampling the bandwidth */
int avg_bitrate_duration;
/* trigger level at which a timer is used to prevent excessive incoming bandwidth */
int limit_rate;
int64_t limit_rate;
/* duration (secs) for mountpoint to be kept reserved after source client exits */
int wait_time;
@ -109,13 +121,16 @@ typedef struct _aliases {
struct _aliases *next;
}aliases;
typedef struct {
struct _listener_t
{
struct _listener_t *next;
int refcount;
int port;
char *bind_address;
int shoutcast_compat;
char *shoutcast_mount;
int ssl;
} listener_t;
};
typedef struct ice_config_tag
{
@ -133,6 +148,7 @@ typedef struct ice_config_tag
int header_timeout;
int source_timeout;
int ice_login;
int64_t max_bandwidth;
int fileserve;
int on_demand; /* global setting for all relays */
@ -148,12 +164,15 @@ typedef struct ice_config_tag
char *hostname;
int port;
char *mimetypes_fn;
listener_t listeners[MAX_LISTEN_SOCKETS];
listener_t *listen_sock;
unsigned int listen_sock_count;
char *master_server;
int master_server_port;
int master_update_interval;
char *master_bind;
char *master_username;
char *master_password;
int master_relay_auth;
@ -169,6 +188,9 @@ typedef struct ice_config_tag
char *base_dir;
char *log_dir;
char *pidfile;
char *banfile;
char *allowfile;
char *agentfile;
char *cert_file;
char *webroot_dir;
char *adminroot_dir;
@ -176,6 +198,7 @@ typedef struct ice_config_tag
unsigned slaves_count;
char *access_log;
int access_log_ip;
int access_log_lines;
char *access_log_exclude_ext;
char *error_log;
@ -208,8 +231,10 @@ 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);
listener_t *config_clear_listener (listener_t *listener);
void config_clear(ice_config_t *config);
mount_proxy *config_find_mount (ice_config_t *config, const char *mount);
listener_t *config_get_listen_sock (ice_config_t *config, sock_t serversock);
int config_rehash(void);

View File

@ -40,10 +40,6 @@
#ifdef HAVE_AIO
#include <errno.h>
#endif
#ifdef _WIN32
#define snprintf _snprintf
#endif
#undef CATMODULE
#define CATMODULE "client"
@ -63,6 +59,19 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
global.clients++;
if (con->serversock != SOCK_ERROR)
{
int i;
for (i=0; i < global.server_sockets; i++)
{
if (global.serversock[i] == con->serversock)
{
client->server_conn = global.server_conn[i];
client->server_conn->refcount++;
}
}
}
/* don't do client limit check if on an SSL socket, as that will be an admin request */
if (not_ssl_connection (con))
{
@ -84,10 +93,10 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
client->pos = 0;
client->write_to_client = format_generic_write_to_client;
*c_ptr = client;
return ret;
}
void client_destroy(client_t *client)
{
if (client == NULL)
@ -101,8 +110,8 @@ void client_destroy(client_t *client)
client->refbuf = NULL;
}
if (auth_client_release (client))
return;
if (client->authenticated)
DEBUG1 ("client still in auth \"%s\"", httpp_getvar (client->parser, HTTPP_VAR_URI));
/* write log entry if ip is set (some things don't set it, like outgoing
* slave requests
@ -128,6 +137,7 @@ void client_destroy(client_t *client)
global_lock ();
global.clients--;
stats_event_args (NULL, "clients", "%d", global.clients);
config_clear_listener (client->server_conn);
global_unlock ();
/* we need to free client specific format data (if any) */
@ -163,13 +173,14 @@ int client_read_bytes (client_t *client, void *buf, unsigned len)
bytes = client->con->read (client->con, buf, len);
if (bytes == -1 && client->con->error)
WARN0 ("reading from connection has failed");
DEBUG0 ("reading from connection has failed");
return bytes;
}
void client_send_302(client_t *client, const char *location) {
void client_send_302(client_t *client, const char *location)
{
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
"HTTP/1.0 302 Temporarily Moved\r\n"
"Content-Type: text/html\r\n"
@ -192,24 +203,20 @@ void client_send_400(client_t *client, char *message) {
}
void client_send_401 (client_t *client)
void client_send_401 (client_t *client, const char *realm)
{
ice_config_t *config = config_get_config ();
const char *send_realm;
if (client->auth && client->auth->realm)
send_realm = client->auth->realm;
else
send_realm = config->server_id;
if (realm == NULL)
realm = config->server_id;
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
"HTTP/1.0 401 Authentication Required\r\n"
"WWW-Authenticate: Basic realm=\"%s\"\r\n"
"\r\n"
"You need to authenticate\r\n", send_realm);
"You need to authenticate\r\n", realm);
config_release_config();
client->respcode = 401;
auth_release (client->auth);
client->auth = NULL;
client->refbuf->len = strlen (client->refbuf->data);
fserve_add_client (client, NULL);
}
@ -227,10 +234,25 @@ void client_send_403(client_t *client, const char *reason)
fserve_add_client (client, NULL);
}
void client_send_404(client_t *client, char *message) {
void client_send_403redirect (client_t *client, const char *mount, const char *reason)
{
if (redirect_client (mount, client))
return;
DEBUG0 ("dropping client");
client_send_403 (client, reason);
}
void client_send_404(client_t *client, const char *message)
{
if (client->respcode)
{
client_destroy (client);
return;
}
if (message == NULL)
message = "Not Available";
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
"HTTP/1.0 404 File Not Found\r\n"
"HTTP/1.0 404 Not Available\r\n"
"Content-Type: text/html\r\n\r\n"
"<b>%s</b>\r\n", message);
client->respcode = 404;

View File

@ -36,6 +36,9 @@ struct _client_tag
/* the client's http headers */
http_parser_t *parser;
/* reference to incoming connection details */
listener_t *server_conn;
/* http response code for this client */
int respcode;
@ -49,14 +52,14 @@ struct _client_tag
refbuf_t *refbuf;
/* position in first buffer */
unsigned long pos;
unsigned int pos;
/* byte count in queue */
unsigned int lag;
/* client is a slave server */
int is_slave;
/* auth used for this client */
struct auth_tag *auth;
/* Client username, if authenticated */
char *username;
@ -88,9 +91,10 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser);
void client_destroy(client_t *client);
void client_send_504(client_t *client, char *message);
void client_send_416(client_t *client);
void client_send_404(client_t *client, char *message);
void client_send_401(client_t *client);
void client_send_404(client_t *client, const char *message);
void client_send_401(client_t *client, const char *realm);
void client_send_403(client_t *client, const char *reason);
void client_send_403redirect (client_t *client, const char *mount, const char *reason);
void client_send_400(client_t *client, char *message);
void client_send_302(client_t *client, const char *location);
int client_send_bytes (client_t *client, const void *buf, unsigned len);

View File

@ -20,48 +20,35 @@
* Solaris.
*/
#ifdef _WIN32
#include <windows.h>
#include <time.h>
#else
#ifdef HAVE_UNISTD_H
#include <unistd.h>
# ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
# else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
# endif
#endif
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
/* Make sure we define 64 bit types */
#ifdef _WIN32
# define PATH_SEPARATOR "\\"
# define size_t unsigned int
# define ssize_t int
# define int64_t __int64
# define uint64_t unsigned __int64
# define uint32_t unsigned int
#else
# define PATH_SEPARATOR "/"
# if defined(HAVE_STDINT_H)
# include <stdint.h>
# elif defined(HAVE_INTTYPES_H)
# if defined(HAVE_INTTYPES_H)
# include <inttypes.h>
# elif defined(HAVE_STDINT_H)
# include <stdint.h>
# endif
#endif
#ifdef _WIN32
#define FORMAT_INT64 "%I64d"
#define FORMAT_UINT64 "%I64u"
#else
#define FORMAT_INT64 "%lld"
#define FORMAT_UINT64 "%llu"
#endif
#endif /* __COMPAT_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -17,13 +17,13 @@
#include <time.h>
#ifdef HAVE_OPENSSL
#include <openssl/ssl.h>
#else
#define SSL void
#include <openssl/err.h>
#endif
struct source_tag;
typedef struct connection_tag connection_t;
#include "cfgfile.h"
#include "client.h"
#include "compat.h"
#include "httpp/httpp.h"
@ -38,8 +38,8 @@ struct connection_tag
time_t discon_time;
uint64_t sent_bytes;
int sock;
int serversock;
sock_t sock;
sock_t serversock;
int error;
#ifdef HAVE_OPENSSL
@ -50,7 +50,6 @@ struct connection_tag
char *ip;
char *host;
};
#ifdef HAVE_OPENSSL
@ -60,15 +59,20 @@ struct connection_tag
#endif
void connection_initialize(void);
void connection_shutdown(void);
void connection_accept_loop(void);
void connection_thread_startup();
void connection_thread_shutdown();
int connection_setup_sockets (struct ice_config_tag *config);
void connection_close(connection_t *con);
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip);
int connection_complete_source (struct source_tag *source, int response);
void connection_uses_ssl (connection_t *con);
void connection_thread_shutdown_req (void);
int connection_check_source_pass (client_t *client, const char *mount);
int connection_check_relay_pass(http_parser_t *parser);
int connection_check_admin_pass(http_parser_t *parser);
extern rwlock_t _source_shutdown_rwlock;
extern int connection_running;
#endif /* __CONNECTION_H__ */

View File

@ -23,10 +23,12 @@
#include "client.h"
#include "logging.h"
#include "slave.h"
#include "fserve.h"
#include "stats.h"
#define CATMODULE "event"
void event_config_read(void *arg)
void event_config_read (void)
{
int ret;
ice_config_t *config;
@ -36,7 +38,7 @@ void event_config_read(void *arg)
INFO0("Re-reading XML");
config = config_grab_config(); /* Both to get the lock, and to be able
to find out the config filename */
xmlSetGenericErrorFunc ("config", log_parse_failure);
xmlSetGenericErrorFunc (config->config_filename, log_parse_failure);
ret = config_parse_file(config->config_filename, &new_config);
if(ret < 0) {
ERROR0("Error parsing config, not replacing existing config");
@ -60,11 +62,14 @@ void event_config_read(void *arg)
else {
config_clear(config);
config_set_config(&new_config);
restart_logging (config_get_config_unlocked());
yp_recheck_config (config_get_config_unlocked());
config = config_get_config_unlocked();
restart_logging (config);
yp_recheck_config (config);
fserve_recheck_mime_types (config);
stats_global (config);
config_release_config();
slave_recheck_mounts();
connection_thread_shutdown();
slave_restart();
}
}

View File

@ -16,6 +16,6 @@
#define EVENT_NO_EVENT 0
#define EVENT_CONFIG_READ 1
void event_config_read(void *nothing);
void event_config_read (void);
#endif /* __EVENT_H__ */

371
src/fnmatch.c Normal file
View File

@ -0,0 +1,371 @@
/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
#ifndef _LIBC
# include <config.h>
#endif
/* Enable GNU extensions in fnmatch.h. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#if ! defined __builtin_expect && __GNUC__ < 3
# define __builtin_expect(expr, expected) (expr)
#endif
#include <fnmatch.h>
#if 0
#ifdef HAVE_FNMATCH_H
#include <fnmatch.h>
#endif
#ifdef HAVE_FNMATCH__H
#include "fnmatch_.h"
#endif
#ifdef WIN32
int __mb_cur_max;
unsigned short* _pctype;
#endif
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif
#include <stdlib.h>
#include <string.h>
#define WIDE_CHAR_SUPPORT \
(HAVE_WCTYPE_H && HAVE_BTOWC && HAVE_ISWCTYPE \
&& HAVE_WMEMCHR && (HAVE_WMEMCPY || HAVE_WMEMPCPY))
/* For platform which support the ISO C amendement 1 functionality we
support user defined character classes. */
#if defined _LIBC || WIDE_CHAR_SUPPORT
# include <wctype.h>
# include <wchar.h>
#endif
/* We need some of the locale data (the collation sequence information)
but there is no interface to get this information in general. Therefore
we support a correct implementation only in glibc. */
#ifdef _LIBC
# include "../locale/localeinfo.h"
# include "../locale/elem-hash.h"
# include "../locale/coll-lookup.h"
# include <shlib-compat.h>
# define CONCAT(a,b) __CONCAT(a,b)
# define mbsrtowcs __mbsrtowcs
# define fnmatch __fnmatch
extern int fnmatch (const char *pattern, const char *string, int flags);
#endif
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif
/* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set. */
#define NO_LEADING_PERIOD(flags) \
((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD))
/* Comment out all this code if we are using the GNU C Library, and are not
actually compiling the library itself, and have not detected a bug
in the library. This code is part of the GNU C
Library, but also included in many other GNU distributions. Compiling
and linking in this code is a waste when using the GNU C library
(especially if it is a shared library). Rather than having every GNU
program understand `configure --with-gnu-libc' and omit the object files,
it is simpler to just do this in the source for each such file. */
#if defined _LIBC || !defined __GNU_LIBRARY__ || !HAVE_FNMATCH_GNU
# if ! (defined isblank || HAVE_DECL_ISBLANK)
# define isblank(c) ((c) == ' ' || (c) == '\t')
# endif
# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
# if defined _LIBC || WIDE_CHAR_SUPPORT
/* The GNU C library provides support for user-defined character classes
and the functions from ISO C amendement 1. */
# ifdef CHARCLASS_NAME_MAX
# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
# else
/* This shouldn't happen but some implementation might still have this
problem. Use a reasonable default value. */
# define CHAR_CLASS_MAX_LENGTH 256
# endif
# ifdef _LIBC
# define IS_CHAR_CLASS(string) __wctype (string)
# else
# define IS_CHAR_CLASS(string) wctype (string)
# endif
# ifdef _LIBC
# define ISWCTYPE(WC, WT) __iswctype (WC, WT)
# else
# define ISWCTYPE(WC, WT) iswctype (WC, WT)
# endif
# if (HAVE_MBSTATE_T && HAVE_MBSRTOWCS) || _LIBC
/* In this case we are implementing the multibyte character handling. */
# define HANDLE_MULTIBYTE 1
# endif
# else
# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
# define IS_CHAR_CLASS(string) \
(STREQ (string, "alpha") || STREQ (string, "upper") \
|| STREQ (string, "lower") || STREQ (string, "digit") \
|| STREQ (string, "alnum") || STREQ (string, "xdigit") \
|| STREQ (string, "space") || STREQ (string, "print") \
|| STREQ (string, "punct") || STREQ (string, "graph") \
|| STREQ (string, "cntrl") || STREQ (string, "blank"))
# endif
/* Avoid depending on library functions or files
whose names are inconsistent. */
/* Global variable. */
static int posixly_correct;
# ifndef internal_function
/* Inside GNU libc we mark some function in a special way. In other
environments simply ignore the marking. */
# define internal_function
# endif
/* Note that this evaluates C many times. */
# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
# define CHAR char
# define UCHAR unsigned char
# define INT int
# define FCT internal_fnmatch
# define EXT ext_match
# define END end_pattern
# define L_(CS) CS
# ifdef _LIBC
# define BTOWC(C) __btowc (C)
# else
# define BTOWC(C) btowc (C)
# endif
# define STRLEN(S) strlen (S)
# define STRCAT(D, S) strcat (D, S)
# ifdef _LIBC
# define MEMPCPY(D, S, N) __mempcpy (D, S, N)
# else
# if HAVE_MEMPCPY
# define MEMPCPY(D, S, N) mempcpy (D, S, N)
# else
# define MEMPCPY(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
# endif
# endif
# define MEMCHR(S, C, N) memchr (S, C, N)
# define STRCOLL(S1, S2) strcoll (S1, S2)
# include "fnmatch_loop.c"
# if HANDLE_MULTIBYTE
# define FOLD(c) ((flags & FNM_CASEFOLD) ? towlower (c) : (c))
# define CHAR wchar_t
# define UCHAR wint_t
# define INT wint_t
# define FCT internal_fnwmatch
# define EXT ext_wmatch
# define END end_wpattern
# define L_(CS) L##CS
# define BTOWC(C) (C)
# ifdef _LIBC
# define STRLEN(S) __wcslen (S)
# define STRCAT(D, S) __wcscat (D, S)
# define MEMPCPY(D, S, N) __wmempcpy (D, S, N)
# else
# define STRLEN(S) wcslen (S)
# define STRCAT(D, S) wcscat (D, S)
# if HAVE_WMEMPCPY
# define MEMPCPY(D, S, N) wmempcpy (D, S, N)
# else
# define MEMPCPY(D, S, N) (wmemcpy (D, S, N) + (N))
# endif
# endif
# define MEMCHR(S, C, N) wmemchr (S, C, N)
# define STRCOLL(S1, S2) wcscoll (S1, S2)
# define WIDE_CHAR_VERSION 1
# undef IS_CHAR_CLASS
/* We have to convert the wide character string in a multibyte string. But
we know that the character class names consist of alphanumeric characters
from the portable character set, and since the wide character encoding
for a member of the portable character set is the same code point as
its single-byte encoding, we can use a simplified method to convert the
string to a multibyte character string. */
static wctype_t
is_char_class (const wchar_t *wcs)
{
char s[CHAR_CLASS_MAX_LENGTH + 1];
char *cp = s;
do
{
/* Test for a printable character from the portable character set. */
# ifdef _LIBC
if (*wcs < 0x20 || *wcs > 0x7e
|| *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60)
return (wctype_t) 0;
# else
switch (*wcs)
{
case L' ': case L'!': case L'"': case L'#': case L'%':
case L'&': case L'\'': case L'(': case L')': case L'*':
case L'+': case L',': case L'-': case L'.': case L'/':
case L'0': case L'1': case L'2': case L'3': case L'4':
case L'5': case L'6': case L'7': case L'8': case L'9':
case L':': case L';': case L'<': case L'=': case L'>':
case L'?':
case L'A': case L'B': case L'C': case L'D': case L'E':
case L'F': case L'G': case L'H': case L'I': case L'J':
case L'K': case L'L': case L'M': case L'N': case L'O':
case L'P': case L'Q': case L'R': case L'S': case L'T':
case L'U': case L'V': case L'W': case L'X': case L'Y':
case L'Z':
case L'[': case L'\\': case L']': case L'^': case L'_':
case L'a': case L'b': case L'c': case L'd': case L'e':
case L'f': case L'g': case L'h': case L'i': case L'j':
case L'k': case L'l': case L'm': case L'n': case L'o':
case L'p': case L'q': case L'r': case L's': case L't':
case L'u': case L'v': case L'w': case L'x': case L'y':
case L'z': case L'{': case L'|': case L'}': case L'~':
break;
default:
return (wctype_t) 0;
}
# endif
/* Avoid overrunning the buffer. */
if (cp == s + CHAR_CLASS_MAX_LENGTH)
return (wctype_t) 0;
*cp++ = (char) *wcs++;
}
while (*wcs != L'\0');
*cp = '\0';
# ifdef _LIBC
return __wctype (s);
# else
return wctype (s);
# endif
}
# define IS_CHAR_CLASS(string) is_char_class (string)
# include "fnmatch_loop.c"
# endif
int
fnmatch (const char *pattern, const char *string, int flags)
{
# if HANDLE_MULTIBYTE
# define ALLOCA_LIMIT 2000
if (__builtin_expect (MB_CUR_MAX, 1) != 1)
{
mbstate_t ps;
size_t patsize;
size_t strsize;
size_t totsize;
wchar_t *wpattern;
wchar_t *wstring;
int res;
/* Calculate the size needed to convert the strings to
wide characters. */
memset (&ps, '\0', sizeof (ps));
patsize = mbsrtowcs (NULL, &pattern, 0, &ps) + 1;
if (__builtin_expect (patsize != 0, 1))
{
assert (mbsinit (&ps));
strsize = mbsrtowcs (NULL, &string, 0, &ps) + 1;
if (__builtin_expect (strsize != 0, 1))
{
assert (mbsinit (&ps));
totsize = patsize + strsize;
if (__builtin_expect (! (patsize <= totsize
&& totsize <= SIZE_MAX / sizeof (wchar_t)),
0))
{
errno = ENOMEM;
return -1;
}
/* Allocate room for the wide characters. */
if (__builtin_expect (totsize < ALLOCA_LIMIT, 1))
wpattern = (wchar_t *) alloca (totsize * sizeof (wchar_t));
else
{
wpattern = malloc (totsize * sizeof (wchar_t));
if (__builtin_expect (! wpattern, 0))
{
errno = ENOMEM;
return -1;
}
}
wstring = wpattern + patsize;
/* Convert the strings into wide characters. */
mbsrtowcs (wpattern, &pattern, patsize, &ps);
assert (mbsinit (&ps));
mbsrtowcs (wstring, &string, strsize, &ps);
res = internal_fnwmatch (wpattern, wstring, wstring + strsize - 1,
flags & FNM_PERIOD, flags);
if (__builtin_expect (! (totsize < ALLOCA_LIMIT), 0))
free (wpattern);
return res;
}
}
}
# endif /* HANDLE_MULTIBYTE */
return internal_fnmatch (pattern, string, string + strlen (string),
flags & FNM_PERIOD, flags);
}
# ifdef _LIBC
# undef fnmatch
versioned_symbol (libc, __fnmatch, fnmatch, GLIBC_2_2_3);
# if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_2_3)
strong_alias (__fnmatch, __fnmatch_old)
compat_symbol (libc, __fnmatch_old, fnmatch, GLIBC_2_0);
# endif
libc_hidden_ver (__fnmatch, fnmatch)
# endif
#endif /* _LIBC or not __GNU_LIBRARY__. */

65
src/fnmatch.h Normal file
View File

@ -0,0 +1,65 @@
/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2001, 2002, 2003,
2005, 2007 Free Software Foundation, Inc.
This file is part of the GNU C Library.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
#ifndef _FNMATCH_H
#define _FNMATCH_H 1
#ifdef __cplusplus
extern "C" {
#endif
/* We #undef these before defining them because some losing systems
(HP-UX A.08.07 for example) define these in <unistd.h>. */
#undef FNM_PATHNAME
#undef FNM_NOESCAPE
#undef FNM_PERIOD
/* Bits set in the FLAGS argument to `fnmatch'. */
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
# define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */
#endif
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
#define FNM_NOMATCH 1
/* This value is returned if the implementation does not support
`fnmatch'. Since this is not the case here it will never be
returned but the conformance test suites still require the symbol
to be defined. */
#ifdef _XOPEN_SOURCE
# define FNM_NOSYS (-1)
#endif
/* Match NAME against the file name pattern PATTERN,
returning zero if it matches, FNM_NOMATCH if not. */
extern int fnmatch (const char *__pattern, const char *__name,
int __flags);
#ifdef __cplusplus
}
#endif
#endif /* fnmatch.h */

1210
src/fnmatch_loop.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -27,8 +27,8 @@
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <time.h>
#include "compat.h"
#include "connection.h"
#include "refbuf.h"
@ -44,16 +44,10 @@
#include "stats.h"
#define CATMODULE "format"
#ifdef WIN32
#define strcasecmp stricmp
#define strncasecmp strnicmp
#define snprintf _snprintf
#endif
static int format_prepare_headers (source_t *source, client_t *client);
format_type_t format_get_type(char *contenttype)
format_type_t format_get_type(const char *contenttype)
{
if(strcmp(contenttype, "application/x-ogg") == 0)
return FORMAT_TYPE_OGG; /* Backwards compatibility */
@ -71,6 +65,7 @@ void format_free_plugin (format_plugin_t *format)
return;
rate_free (format->in_bitrate);
rate_free (format->out_bitrate);
free (format->charset);
if (format->free_plugin)
format->free_plugin (format);
}
@ -107,14 +102,19 @@ static void find_client_start (source_t *source, client_t *client)
* a starting point, so look for one from the burst point */
if (client->intro_offset == -1 && source->stream_data_tail
&& source->stream_data_tail->sync_point)
{
refbuf = source->stream_data_tail;
client->lag = refbuf->len;
}
else
{
size_t size = client->intro_offset;
refbuf = source->burst_point;
client->lag = source->burst_offset;
while (size > 0 && refbuf && refbuf->next)
{
size -= refbuf->len;
client->lag -= refbuf->len;
refbuf = refbuf->next;
}
}
@ -171,6 +171,7 @@ int format_check_file_buffer (source_t *source, client_t *client)
client->refbuf = refbuf;
client->pos = refbuf->len;
client->intro_offset = 0;
client->lag = 0;
}
if (client->pos == refbuf->len)
{
@ -210,13 +211,13 @@ int format_check_http_buffer (source_t *source, client_t *client)
{
DEBUG0("processing pending client headers");
client->respcode = 200;
if (format_prepare_headers (source, client) < 0)
{
ERROR0 ("internal problem, dropping client");
client->con->error = 1;
return -1;
}
client->respcode = 200;
stats_event_inc (NULL, "listeners");
stats_event_inc (NULL, "listener_connections");
stats_event_inc (source->mount, "listener_connections");
@ -357,6 +358,11 @@ static int format_prepare_headers (source_t *source, client_t *client)
remaining -= bytes;
ptr += bytes;
/* prevent proxy servers from caching */
bytes = snprintf (ptr, remaining, "Cache-Control: no-cache\r\n");
remaining -= bytes;
ptr += bytes;
bytes = snprintf (ptr, remaining, "\r\n");
remaining -= bytes;
ptr += bytes;

View File

@ -39,7 +39,8 @@ typedef struct _format_plugin_tag
/* we need to know the mount to report statistics */
char *mount;
char *contenttype;
const char *contenttype;
char *charset;
uint64_t read_bytes;
uint64_t sent_bytes;
struct rate_calc *in_bitrate;
@ -47,9 +48,9 @@ typedef struct _format_plugin_tag
refbuf_t *(*get_buffer)(struct source_tag *);
int (*write_buf_to_client)(client_t *client);
void (*write_buf_to_file)(struct source_tag *source, refbuf_t *refbuf);
void (*write_buf_to_file)(struct source_tag *source, refbuf_t *refbuf);
int (*create_client_data)(struct source_tag *source, client_t *client);
void (*set_tag)(struct _format_plugin_tag *plugin, char *tag, char *value);
void (*set_tag)(struct _format_plugin_tag *plugin, const char *tag, const char *value, const char *charset);
void (*free_plugin)(struct _format_plugin_tag *self);
void (*apply_settings)(client_t *client, struct _format_plugin_tag *format, struct _mount_proxy *mount);
@ -57,7 +58,7 @@ typedef struct _format_plugin_tag
void *_state;
} format_plugin_t;
format_type_t format_get_type(char *contenttype);
format_type_t format_get_type(const char *contenttype);
int format_get_plugin(format_type_t type, struct source_tag *source);
int format_generic_write_to_client (client_t *client);

View File

@ -40,12 +40,6 @@
#include "format_mp3.h"
#ifdef WIN32
#define strcasecmp stricmp
#define strncasecmp strnicmp
#define snprintf _snprintf
#endif
#define CATMODULE "format-mp3"
/* Note that this seems to be 8192 in shoutcast - perhaps we want to be the
@ -61,7 +55,7 @@ static int format_mp3_create_client_data (source_t *source, client_t *client);
static void free_mp3_client_data (client_t *client);
static int format_mp3_write_buf_to_client(client_t *client);
static void write_mp3_to_file (struct source_tag *source, refbuf_t *refbuf);
static void mp3_set_tag (format_plugin_t *plugin, char *tag, char *value);
static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset);
static void format_mp3_apply_settings(client_t *client, format_plugin_t *format, mount_proxy *mount);
@ -75,7 +69,7 @@ typedef struct {
int format_mp3_get_plugin (source_t *source)
{
char *metadata;
const char *metadata;
format_plugin_t *plugin;
mp3_state *state = calloc(1, sizeof(mp3_state));
refbuf_t *meta;
@ -124,42 +118,43 @@ int format_mp3_get_plugin (source_t *source)
}
static void mp3_set_tag (format_plugin_t *plugin, char *tag, char *value)
static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset)
{
mp3_state *source_mp3 = plugin->_state;
unsigned int len;
const char meta[] = "StreamTitle='";
int size = sizeof (meta) + 1;
char *value;
if (tag==NULL || value == NULL)
if (tag==NULL || in_value == NULL)
return;
/* protect against multiple updaters */
thread_mutex_lock (&source_mp3->url_lock);
value = util_conv_string (in_value, charset, plugin->charset);
if (value == NULL)
value = strdup (in_value);
len = strlen (value)+1;
size += len;
/* protect against multiple updaters */
thread_mutex_lock (&source_mp3->url_lock);
if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0)
{
char *p = strdup (value);
if (p)
{
free (source_mp3->url_title);
free (source_mp3->url_artist);
source_mp3->url_artist = NULL;
source_mp3->url_title = p;
source_mp3->update_metadata = 1;
}
free (source_mp3->url_title);
free (source_mp3->url_artist);
source_mp3->url_artist = NULL;
source_mp3->url_title = value;
source_mp3->update_metadata = 1;
}
else if (strcmp (tag, "artist") == 0)
{
char *p = strdup (value);
if (p)
{
free (source_mp3->url_artist);
source_mp3->url_artist = p;
source_mp3->update_metadata = 1;
}
free (source_mp3->url_artist);
source_mp3->url_artist = value;
source_mp3->update_metadata = 1;
}
else
free (value);
thread_mutex_unlock (&source_mp3->url_lock);
}
@ -176,7 +171,7 @@ static void filter_shoutcast_metadata (source_t *source, char *metadata, size_t
metadata++;
if (strncmp (metadata, "StreamTitle='", 13))
break;
if ((end = strstr (metadata, "\';")) == NULL)
if ((end = strstr (metadata+13, "\';")) == NULL)
break;
len = (end - metadata) - 13;
p = calloc (1, len+1);
@ -184,7 +179,7 @@ static void filter_shoutcast_metadata (source_t *source, char *metadata, size_t
{
memcpy (p, metadata+13, len);
stats_event_conv (source->mount, "title", p, source->charset);
stats_event_conv (source->mount, "title", p, source->format->charset);
yp_touch (source->mount);
free (p);
@ -198,10 +193,24 @@ static void format_mp3_apply_settings (client_t *client, format_plugin_t *format
{
mp3_state *source_mp3 = format->_state;
if (mount == NULL || mount->mp3_meta_interval < 0)
source_mp3->interval = -1;
free (format->charset);
format->charset = NULL;
source_mp3->queue_block_size = 1400;
if (mount)
{
char *metadata = httpp_getvar (client->parser, "icy-metaint");
source_mp3->interval = -1;
if (mount->mp3_meta_interval > 0)
source_mp3->interval = mount->mp3_meta_interval;
if (mount->charset)
format->charset = strdup (mount->charset);
if (mount->queue_block_size)
source_mp3->queue_block_size = mount->queue_block_size;
}
if (source_mp3->interval <= 0)
{
const char *metadata = httpp_getvar (client->parser, "icy-metaint");
source_mp3->interval = ICY_METADATA_INTERVAL;
if (metadata)
{
int interval = atoi (metadata);
@ -209,9 +218,11 @@ static void format_mp3_apply_settings (client_t *client, format_plugin_t *format
source_mp3->interval = interval;
}
}
else
source_mp3->interval = mount->mp3_meta_interval;
DEBUG1 ("mp3 interval %d", source_mp3->interval);
if (format->charset == NULL)
format->charset = strdup ("ISO8859-1");
DEBUG1 ("sending metadata interval %d", source_mp3->interval);
DEBUG1 ("charset %s", format->charset);
}
@ -262,6 +273,7 @@ static void mp3_set_title (source_t *source)
else
snprintf (p->data, size, "%c%s%s';", len_byte, meta,
source_mp3->url_title);
DEBUG1 ("shoutcast metadata block setup with %s", p->data+1);
filter_shoutcast_metadata (source, p->data, size);
refbuf_release (source_mp3->metadata);
@ -275,11 +287,12 @@ static void mp3_set_title (source_t *source)
* which is 0 or greater. Check the client in_metadata value afterwards
* to see if all metadata has been sent
*/
static int send_mp3_metadata (client_t *client, refbuf_t *associated)
static int send_stream_metadata (client_t *client, refbuf_t *refbuf, unsigned int remaining)
{
int ret = 0;
char *metadata;
int meta_len;
refbuf_t *associated = refbuf->associated;
mp3_client_data *client_mp3 = client->format_data;
/* If there is a change in metadata then send it else
@ -304,6 +317,41 @@ static int send_mp3_metadata (client_t *client, refbuf_t *associated)
meta_len = 17 - client_mp3->metadata_offset;
}
}
/* if there is normal stream data to send as well as metadata then try
* to merge them into 1 write call */
if (remaining)
{
char *merge = malloc (remaining + meta_len);
memcpy (merge, refbuf->data + client->pos, remaining);
memcpy (merge+remaining, metadata, meta_len);
ret = client_send_bytes (client, merge, remaining+meta_len);
free (merge);
if (ret >= (int)remaining)
{
/* did we write all of it? */
if (ret < (int)remaining + meta_len)
{
client_mp3->metadata_offset += (ret - remaining);
client_mp3->in_metadata = 1;
}
else
client_mp3->associated = associated;
client_mp3->since_meta_block = 0;
client->pos += remaining;
client->lag -= remaining;
return ret;
}
if (ret > 0)
{
client_mp3->since_meta_block += ret;
client->pos += ret;
client->lag -= ret;
return ret;
}
return 0;
}
ret = client_send_bytes (client, metadata, meta_len);
if (ret == meta_len)
@ -340,8 +388,7 @@ static int format_mp3_write_buf_to_client (client_t *client)
/* send any unwritten metadata to the client */
if (client_mp3->in_metadata)
{
refbuf_t *associated = refbuf->associated;
ret = send_mp3_metadata (client, associated);
ret = send_stream_metadata (client, refbuf, 0);
if (client_mp3->in_metadata)
break;
@ -353,28 +400,13 @@ static int format_mp3_write_buf_to_client (client_t *client)
size_t remaining = client_mp3->interval -
client_mp3->since_meta_block;
/* sending the metadata block */
/* leading up to sending the metadata block */
if (remaining <= len)
{
/* send any mp3 before the metadata block */
if (remaining)
{
ret = client_send_bytes (client, buf, remaining);
if (ret > 0)
{
client_mp3->since_meta_block += ret;
client->pos += ret;
}
if (ret < (int)remaining)
break;
written += ret;
}
ret = send_mp3_metadata (client, refbuf->associated);
ret = send_stream_metadata (client, refbuf, remaining);
if (client_mp3->in_metadata)
break;
written += ret;
/* change buf and len */
buf += remaining;
len -= remaining;
/* limit how much mp3 we send if using small intervals */
@ -391,6 +423,7 @@ static int format_mp3_write_buf_to_client (client_t *client)
{
client_mp3->since_meta_block += ret;
client->pos += ret;
client->lag -= ret;
}
if (ret < (int)len)
break;
@ -426,30 +459,31 @@ static void format_mp3_free_plugin (format_plugin_t *plugin)
*/
static int complete_read (source_t *source)
{
int bytes;
int bytes = 0;
format_plugin_t *format = source->format;
mp3_state *source_mp3 = format->_state;
char *buf;
refbuf_t *refbuf;
#define REFBUF_SIZE 1400
if (source_mp3->read_data == NULL)
{
source_mp3->read_data = refbuf_new (REFBUF_SIZE);
source_mp3->read_data = refbuf_new (source_mp3->queue_block_size);
source_mp3->read_count = 0;
}
buf = source_mp3->read_data->data + source_mp3->read_count;
bytes = client_read_bytes (source->client, buf, REFBUF_SIZE-source_mp3->read_count);
if (bytes < 0)
if (source_mp3->read_count < source_mp3->queue_block_size)
{
if (source->client->con->error)
bytes = client_read_bytes (source->client, buf, source_mp3->queue_block_size-source_mp3->read_count);
if (bytes < 0)
{
refbuf_release (source_mp3->read_data);
source_mp3->read_data = NULL;
if (source->client->con->error)
{
refbuf_release (source_mp3->read_data);
source_mp3->read_data = NULL;
}
return 0;
}
return 0;
}
source_mp3->read_count += bytes;
refbuf = source_mp3->read_data;
@ -457,7 +491,7 @@ static int complete_read (source_t *source)
format->read_bytes += bytes;
rate_add (format->in_bitrate, bytes, global.time);
if (source_mp3->read_count < REFBUF_SIZE)
if (source_mp3->read_count < source_mp3->queue_block_size)
{
if (source_mp3->read_count == 0)
{
@ -552,6 +586,7 @@ static refbuf_t *mp3_get_filter_meta (source_t *source)
sizeof (source_mp3->build_metadata));
source_mp3->build_metadata_offset = 0;
source_mp3->build_metadata_len = 1 + (*src * 16);
rate_add (plugin->in_bitrate, source_mp3->build_metadata_len, global.time);
}
/* do we have all of the metatdata block */
@ -575,7 +610,7 @@ static refbuf_t *mp3_get_filter_meta (source_t *source)
/* assign metadata if it's greater than 1 byte, and the text has changed */
if (source_mp3->build_metadata_len > 1 &&
strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0)
strcmp (source_mp3->build_metadata+1, source_mp3->metadata->data+1) != 0)
{
refbuf_t *meta = refbuf_new (source_mp3->build_metadata_len);
memcpy (meta->data, source_mp3->build_metadata,
@ -624,19 +659,24 @@ static int format_mp3_create_client_data(source_t *source, client_t *client)
size_t remaining = 4096 - client->refbuf->len + 2;
char *ptr = client->refbuf->data + client->refbuf->len - 2;
int bytes;
const char *useragent;
if (client_mp3 == NULL)
{
ERROR0 ("malloc failed");
return -1;
}
/* hack for flash player, it wants a length */
if (httpp_getvar(client->parser, "x-flash-version"))
/* hack for flash player, it wants a length. It has also been reported that the useragent
* appears as MSIE if run in internet explorer */
useragent = httpp_getvar (client->parser, "user-agent");
if (httpp_getvar(client->parser, "x-flash-version") ||
(useragent && strstr(useragent, "MSIE")))
{
bytes = snprintf (ptr, remaining, "Content-Length: 347122319\r\n");
bytes = snprintf (ptr, remaining, "Content-Length: 221183499\r\n");
remaining -= bytes;
ptr += bytes;
/* avoid browser caching, reported via forum */
bytes = snprintf (ptr, remaining, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n");
remaining -= bytes;
ptr += bytes;
}
client->format_data = client_mp3;

View File

@ -26,10 +26,11 @@ typedef struct {
char *url_artist;
char *url_title;
int update_metadata;
int queue_block_size;
refbuf_t *metadata;
refbuf_t *read_data;
unsigned int read_count;
int read_count;
mutex_t url_lock;
unsigned build_metadata_len;

View File

@ -42,10 +42,6 @@
#endif
#include "format_midi.h"
#include "format_flac.h"
#ifdef _WIN32
#define snprintf _snprintf
#endif
#define CATMODULE "format-ogg"
#include "logging.h"
@ -226,6 +222,9 @@ static void apply_ogg_settings (client_t *client,
ogg_info->passthrough = mount->ogg_passthrough;
DEBUG1 ("oggpassthrough is %d", ogg_info->passthrough);
ogg_info->admin_comments_only = mount->admin_comments_only;
DEBUG1 ("admin_comments_only is %d", ogg_info->admin_comments_only);
}
@ -563,7 +562,10 @@ static int write_buf_to_client (client_t *client)
ret = client_send_bytes (client, buf, len);
if (ret > 0)
{
client->pos += ret;
client->lag -= ret;
}
if (ret < (int)len)
break;

View File

@ -35,6 +35,7 @@ typedef struct ogg_state_tag
int log_metadata;
int use_url_metadata;
int passthrough;
int admin_comments_only;
refbuf_t *file_headers;
refbuf_t *header_pages;
refbuf_t *header_pages_tail;

View File

@ -134,11 +134,8 @@ static refbuf_t *process_theora_page (ogg_state_t *ogg_info, ogg_codec_t *codec,
{
if (codec->possible_start)
refbuf_release (codec->possible_start);
if (refbuf)
{
refbuf_addref (refbuf);
codec->possible_start = refbuf;
}
refbuf_addref (refbuf);
codec->possible_start = refbuf;
}
theora->prev_granulepos = granulepos;

View File

@ -67,7 +67,7 @@ static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec);
static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
ogg_codec_t *codec, ogg_page *page);
static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec);
static void vorbis_set_tag (format_plugin_t *plugin, char *tag, char *value);
static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char *value, const char *charset);
static void free_ogg_packet (ogg_packet *packet)
@ -324,13 +324,16 @@ static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec)
{
vorbis_comment vc;
ogg_packet header;
ice_config_t *config;
vorbis_comment_init (&vc);
if (ogg_info->artist)
vorbis_comment_add_tag (&vc, "artist", ogg_info->artist);
if (ogg_info->title)
vorbis_comment_add_tag (&vc, "title", ogg_info->title);
vorbis_comment_add_tag (&vc, "server", ICECAST_VERSION_STRING);
config = config_get_config();
vorbis_comment_add_tag (&vc, "server", config->server_id);
config_release_config();
vorbis_commentheader_out (&vc, &header);
ogg_stream_packetin (&source_vorbis->new_os, &header);
@ -414,12 +417,13 @@ ogg_codec_t *initial_vorbis_page (format_plugin_t *plugin, ogg_page *page)
/* called from the admin interface, here we update the artist/title info
* and schedule a new set of header pages
*/
static void vorbis_set_tag (format_plugin_t *plugin, char *tag, char *value)
static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char *in_value, const char *charset)
{
ogg_state_t *ogg_info = plugin->_state;
ogg_codec_t *codec = ogg_info->codecs;
vorbis_codec_t *source_vorbis;
int change = 0;
char *value;
/* avoid url updates unless allowed to */
if (ogg_info->use_url_metadata == 0)
@ -431,43 +435,37 @@ static void vorbis_set_tag (format_plugin_t *plugin, char *tag, char *value)
else
return;
value = util_conv_string (in_value, charset, "UTF-8");
if (value == NULL)
value = strdup (in_value);
if (strcmp (tag, "artist") == 0)
{
char *p = strdup (value);
if (p)
{
free (ogg_info->artist);
ogg_info->artist = p;
change = 1;
}
free (ogg_info->artist);
ogg_info->artist = value;
change = 1;
}
if (strcmp (tag, "title") == 0)
else if (strcmp (tag, "title") == 0)
{
char *p = strdup (value);
if (p)
{
free (ogg_info->title);
ogg_info->title = p;
change = 1;
}
free (ogg_info->title);
ogg_info->title = value;
change = 1;
}
if (strcmp (tag, "song") == 0)
else if (strcmp (tag, "song") == 0)
{
char *p = strdup (value);
if (p)
{
free (ogg_info->artist);
free (ogg_info->title);
ogg_info->artist = NULL;
ogg_info->title = p;
change = 1;
}
free (ogg_info->artist);
free (ogg_info->title);
ogg_info->artist = NULL;
ogg_info->title = value;
change = 1;
}
if (change)
{
source_vorbis->stream_notify = 1;
source_vorbis->rebuild_comment = 1;
}
else
free (value);
}
@ -561,6 +559,8 @@ static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
{
/* set queued vorbis pages to contain about 1 second worth of samples */
source_vorbis->page_samples_trigger = (ogg_int64_t)(source_vorbis->vi.rate);
if (ogg_info->admin_comments_only)
source_vorbis->rebuild_comment = 1;
source_vorbis->process_packet = process_vorbis_headers;
source_vorbis->initial_audio_page = 1;
}
@ -571,19 +571,22 @@ static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
codec->process_page = process_vorbis_passthru_page;
}
free (ogg_info->title);
comment = vorbis_comment_query (&source_vorbis->vc, "TITLE", 0);
if (comment)
ogg_info->title = strdup (comment);
else
ogg_info->title = NULL;
if (ogg_info->admin_comments_only == 0)
{
free (ogg_info->title);
comment = vorbis_comment_query (&source_vorbis->vc, "TITLE", 0);
if (comment)
ogg_info->title = strdup (comment);
else
ogg_info->title = NULL;
free (ogg_info->artist);
comment = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0);
if (comment)
ogg_info->artist = strdup (comment);
else
ogg_info->artist = NULL;
free (ogg_info->artist);
comment = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0);
if (comment)
ogg_info->artist = strdup (comment);
else
ogg_info->artist = NULL;
}
ogg_info->log_metadata = 1;
stats_event_args (ogg_info->mount, "audio_samplerate", "%ld", (long)source_vorbis->vi.rate);

View File

@ -34,8 +34,6 @@
#else
#include <winsock2.h>
#include <windows.h>
#define snprintf _snprintf
#define S_ISREG(mode) ((mode) & _S_IFREG)
#endif
#include "thread/thread.h"
@ -52,6 +50,7 @@
#include "logging.h"
#include "cfgfile.h"
#include "util.h"
#include "admin.h"
#include "compat.h"
#include "fserve.h"
@ -61,20 +60,13 @@
#define BUFSIZE 4096
#ifdef _WIN32
#define MIMETYPESFILE ".\\mime.types"
#else
#define MIMETYPESFILE "/etc/mime.types"
#endif
static fserve_t *active_list = NULL;
static volatile fserve_t *pending_list = NULL;
static mutex_t pending_lock;
static spin_t pending_lock;
static avl_tree *mimetypes = NULL;
static thread_type *fserv_thread;
static int run_fserv = 0;
static volatile int run_fserv = 0;
static unsigned int fserve_clients;
static int client_tree_changed=0;
@ -82,7 +74,7 @@ static int client_tree_changed=0;
static struct pollfd *ufds = NULL;
#else
static fd_set fds;
static int fd_max = -1;
static sock_t fd_max = SOCK_ERROR;
#endif
typedef struct {
@ -93,30 +85,46 @@ typedef struct {
static void fserve_client_destroy(fserve_t *fclient);
static int _delete_mapping(void *mapping);
static void *fserv_thread_function(void *arg);
static void create_mime_mappings(const char *fn);
static int fserve_add_client_mount (client_t *client, const char *mount, FILE *file);
void fserve_initialize(void)
{
create_mime_mappings(MIMETYPESFILE);
ice_config_t *config = config_get_config();
thread_mutex_create ("fserve pending", &pending_lock);
mimetypes = NULL;
thread_spin_create ("fserve pending", &pending_lock);
fserve_recheck_mime_types (config);
config_release_config();
run_fserv = 1;
stats_event (NULL, "file_connections", "0");
fserv_thread = thread_create("File Serving Thread",
fserv_thread_function, NULL, THREAD_ATTACHED);
INFO0("file serving started");
}
void fserve_shutdown(void)
{
if(!run_fserv)
return;
thread_spin_lock (&pending_lock);
run_fserv = 0;
thread_join(fserv_thread);
INFO0("file serving thread stopped");
avl_tree_free(mimetypes, _delete_mapping);
while (pending_list)
{
fserve_t *to_go = (fserve_t *)pending_list;
pending_list = to_go->next;
fserve_client_destroy (to_go);
}
while (active_list)
{
fserve_t *to_go = active_list;
active_list = to_go->next;
fserve_client_destroy (to_go);
}
if (mimetypes)
avl_tree_free (mimetypes, _delete_mapping);
thread_spin_unlock (&pending_lock);
thread_spin_destroy (&pending_lock);
INFO0("file serving stopped");
}
#ifdef HAVE_POLL
@ -141,7 +149,12 @@ int fserve_client_waiting (void)
}
}
if (!ufds)
thread_sleep(200000);
{
thread_spin_lock (&pending_lock);
run_fserv = 0;
thread_spin_unlock (&pending_lock);
return -1;
}
else if (poll(ufds, fserve_clients, 200) > 0)
{
/* mark any clients that are ready */
@ -166,18 +179,23 @@ int fserve_client_waiting (void)
if(client_tree_changed) {
client_tree_changed = 0;
FD_ZERO(&fds);
fd_max = -1;
fd_max = SOCK_ERROR;
fclient = active_list;
while (fclient) {
FD_SET (fclient->client->con->sock, &fds);
if (fclient->client->con->sock > fd_max)
if (fclient->client->con->sock > fd_max || fd_max == SOCK_ERROR)
fd_max = fclient->client->con->sock;
fclient = fclient->next;
}
}
/* hack for windows, select needs at least 1 descriptor */
if (fd_max == -1)
thread_sleep (200000);
if (fd_max == SOCK_ERROR)
{
thread_spin_lock (&pending_lock);
run_fserv = 0;
thread_spin_unlock (&pending_lock);
return -1;
}
else
{
struct timeval tv;
@ -203,15 +221,17 @@ int fserve_client_waiting (void)
}
#endif
static void wait_for_fds(void) {
static int wait_for_fds(void)
{
fserve_t *fclient;
int ret;
while (run_fserv)
{
/* add any new clients here */
if (pending_list)
{
thread_mutex_lock (&pending_lock);
thread_spin_lock (&pending_lock);
fclient = (fserve_t*)pending_list;
while (fclient)
@ -224,12 +244,14 @@ static void wait_for_fds(void) {
fserve_clients++;
}
pending_list = NULL;
thread_mutex_unlock (&pending_lock);
thread_spin_unlock (&pending_lock);
}
/* drop out of here if someone is ready */
if (fserve_client_waiting())
break;
ret = fserve_client_waiting();
if (ret)
return ret;
}
return -1;
}
static void *fserv_thread_function(void *arg)
@ -237,10 +259,10 @@ static void *fserv_thread_function(void *arg)
fserve_t *fclient, **trail;
size_t bytes;
INFO0("file serving thread started");
while (run_fserv)
while (1)
{
wait_for_fds();
if (wait_for_fds() < 0)
break;
fclient = active_list;
trail = &active_list;
@ -299,66 +321,55 @@ static void *fserv_thread_function(void *arg)
}
}
/* Shutdown path */
thread_mutex_lock (&pending_lock);
while (pending_list)
{
fserve_t *to_go = (fserve_t *)pending_list;
pending_list = to_go->next;
fserve_client_destroy (to_go);
}
thread_mutex_unlock (&pending_lock);
while (active_list)
{
fserve_t *to_go = active_list;
active_list = to_go->next;
fserve_client_destroy (to_go);
}
DEBUG0 ("fserve handler exit");
return NULL;
}
const char *fserve_content_type (const char *path)
/* string returned needs to be free'd */
char *fserve_content_type (const char *path)
{
char *ext = util_get_extension(path);
mime_type exttype = { NULL, NULL };
void *result;
char *type;
if (ext == NULL)
return "text/html";
return strdup ("text/html");
exttype.ext = strdup (ext);
if (!avl_get_by_key (mimetypes, &exttype, &result))
thread_spin_lock (&pending_lock);
if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result))
{
mime_type *mime = result;
free (exttype.ext);
return mime->type;
type = strdup (mime->type);
}
else {
free (exttype.ext);
/* Fallbacks for a few basic ones */
if(!strcmp(ext, "ogg"))
return "application/ogg";
type = strdup ("application/ogg");
else if(!strcmp(ext, "mp3"))
return "audio/mpeg";
type = strdup ("audio/mpeg");
else if(!strcmp(ext, "html"))
return "text/html";
type = strdup ("text/html");
else if(!strcmp(ext, "css"))
return "text/css";
type = strdup ("text/css");
else if(!strcmp(ext, "txt"))
return "text/plain";
type = strdup ("text/plain");
else if(!strcmp(ext, "jpg"))
return "image/jpeg";
type = strdup ("image/jpeg");
else if(!strcmp(ext, "png"))
return "image/png";
type = strdup ("image/png");
else if(!strcmp(ext, "m3u"))
return "audio/x-mpegurl";
type = strdup ("audio/x-mpegurl");
else if(!strcmp(ext, "aac"))
return "audio/aac";
type = strdup ("audio/aac");
else
return "application/octet-stream";
type = strdup ("application/octet-stream");
}
thread_spin_unlock (&pending_lock);
return type;
}
static void fserve_client_destroy(fserve_t *fclient)
@ -372,7 +383,15 @@ static void fserve_client_destroy(fserve_t *fclient)
fclient->callback (fclient->client, fclient->arg);
else
if (fclient->client)
client_destroy (fclient->client);
{
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, fclient->mount);
fclient->client->authenticated = 0;
auth_release_listener (fclient->client, fclient->mount, mountinfo);
config_release_config();
}
free (fclient->mount);
free (fclient);
}
}
@ -384,12 +403,13 @@ static void fserve_client_destroy(fserve_t *fclient)
int fserve_client_create (client_t *httpclient, const char *path)
{
struct stat file_buf;
char *range = NULL;
int64_t new_content_len = 0;
int64_t rangenumber = 0, content_length;
const char *range = NULL;
off_t new_content_len = 0;
off_t rangenumber = 0, content_length;
int ret = 0;
char *fullpath;
int m3u_requested = 0, m3u_file_available = 1;
int xspf_requested = 0, xspf_file_available = 1;
ice_config_t *config;
FILE *file;
@ -399,11 +419,14 @@ int fserve_client_create (client_t *httpclient, const char *path)
if (strcmp (util_get_extension (fullpath), "m3u") == 0)
m3u_requested = 1;
if (strcmp (util_get_extension (fullpath), "xspf") == 0)
xspf_requested = 1;
/* check for the actual file */
if (stat (fullpath, &file_buf) != 0)
{
/* the m3u can be generated, but send an m3u file if available */
if (m3u_requested == 0)
if (m3u_requested == 0 && xspf_requested == 0)
{
WARN2 ("req for file \"%s\" %s", fullpath, strerror (errno));
client_send_404 (httpclient, "The file you requested could not be found");
@ -411,17 +434,18 @@ int fserve_client_create (client_t *httpclient, const char *path)
return -1;
}
m3u_file_available = 0;
xspf_file_available = 0;
}
httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE;
if (m3u_requested && m3u_file_available == 0)
{
char *host = httpp_getvar (httpclient->parser, "host");
const char *host = httpp_getvar (httpclient->parser, "host");
char *sourceuri = strdup (path);
char *dot = strrchr (sourceuri, '.');
char *protocol = "http";
char *agent = httpp_getvar (httpclient->parser, "user-agent");
const char *agent = httpp_getvar (httpclient->parser, "user-agent");
if (agent)
{
@ -466,6 +490,19 @@ int fserve_client_create (client_t *httpclient, const char *path)
free (fullpath);
return 0;
}
if (xspf_requested && xspf_file_available == 0)
{
xmlDocPtr doc;
char *reference = strdup (path);
char *eol = strrchr (reference, '.');
if (eol)
*eol = '\0';
stats_get_xml (&doc, 0, reference);
free (reference);
admin_send_response (doc, httpclient, XSLT, "xspf.xsl");
xmlFreeDoc(doc);
return 0;
}
/* on demand file serving check */
config = config_get_config();
@ -488,24 +525,29 @@ int fserve_client_create (client_t *httpclient, const char *path)
}
file = fopen (fullpath, "rb");
free (fullpath);
if (file == NULL)
{
WARN1 ("Problem accessing file \"%s\"", fullpath);
client_send_404 (httpclient, "File not readable");
free (fullpath);
return -1;
}
free (fullpath);
content_length = (int64_t)file_buf.st_size;
content_length = file_buf.st_size;
range = httpp_getvar (httpclient->parser, "range");
do
{
int bytes;
/* full http range handling is currently not done but we deal with the common case */
if (range)
{
ret = sscanf(range, "bytes=" FORMAT_INT64 "-", &rangenumber);
ret = 0;
if (strncasecmp (range, "bytes=", 6) == 0)
ret = sscanf (range+6, "%" SCNdMAX "-", &rangenumber);
if (ret == 1 && rangenumber>=0 && rangenumber < content_length)
{
/* Date: is required on all HTTP1.1 responses */
@ -513,9 +555,10 @@ int fserve_client_create (client_t *httpclient, const char *path)
time_t now;
int strflen;
struct tm result;
int64_t endpos;
off_t endpos;
char *type;
ret = fseek (file, rangenumber, SEEK_SET);
ret = fseeko (file, rangenumber, SEEK_SET);
if (ret == -1)
break;
@ -528,37 +571,53 @@ int fserve_client_create (client_t *httpclient, const char *path)
strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT",
gmtime_r (&now, &result));
httpclient->respcode = 206;
type = fserve_content_type (path);
bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
"HTTP/1.1 206 Partial Content\r\n"
"Date: %s\r\n"
"Content-Length: " FORMAT_INT64 "\r\n"
"Content-Range: bytes " FORMAT_INT64 \
"-" FORMAT_INT64 "/" FORMAT_INT64 "\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %" PRIdMAX "\r\n"
"Content-Range: bytes %" PRIdMAX
"-%" PRIdMAX
"/%" PRIdMAX "\r\n"
"Content-Type: %s\r\n\r\n",
currenttime,
new_content_len,
rangenumber,
endpos,
content_length,
fserve_content_type(path));
type);
free (type);
}
else
break;
}
else {
else
{
char *type = fserve_content_type (path);
httpclient->respcode = 200;
bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
"HTTP/1.0 200 OK\r\n"
"Content-Length: " FORMAT_INT64 "\r\n"
"Content-Type: %s\r\n\r\n",
content_length,
fserve_content_type(path));
if (httpp_getvar (httpclient->parser, HTTPP_VAR_NO_CONTENT_LENGTH))
bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
"HTTP/1.0 200 OK\r\n"
"Content-Type: %s\r\n"
"\r\n",
type);
else
bytes = snprintf (httpclient->refbuf->data, BUFSIZE,
"HTTP/1.0 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Type: %s\r\n"
"Content-Length: %" PRIdMAX "\r\n"
"\r\n",
type,
content_length);
free (type);
}
httpclient->refbuf->len = bytes;
httpclient->pos = 0;
stats_event_inc (NULL, "file_connections");
fserve_add_client (httpclient, file);
fserve_add_client_mount (httpclient, path, file);
return 0;
} while (0);
/* If we run into any issues with the ranges
@ -568,11 +627,24 @@ int fserve_client_create (client_t *httpclient, const char *path)
return -1;
}
static void fserve_add_pending (fserve_t *fclient)
{
thread_spin_lock (&pending_lock);
fclient->next = (fserve_t *)pending_list;
pending_list = fclient;
if (run_fserv == 0)
{
run_fserv = 1;
DEBUG0 ("fserve handler waking up");
thread_create("File Serving Thread", fserv_thread_function, NULL, THREAD_DETACHED);
}
thread_spin_unlock (&pending_lock);
}
/* Add client to fserve thread, client needs to have refbuf set and filled
* but may provide a NULL file if no data needs to be read
*/
int fserve_add_client (client_t *client, FILE *file)
static int fserve_add_client_mount (client_t *client, const char *mount, FILE *file)
{
fserve_t *fclient = calloc (1, sizeof(fserve_t));
@ -584,17 +656,21 @@ int fserve_add_client (client_t *client, FILE *file)
}
fclient->file = file;
fclient->client = client;
if (mount)
fclient->mount = strdup (mount);
fclient->ready = 0;
thread_mutex_lock (&pending_lock);
fclient->next = (fserve_t *)pending_list;
pending_list = fclient;
thread_mutex_unlock (&pending_lock);
fserve_add_pending (fclient);
return 0;
}
int fserve_add_client (client_t *client, FILE *file)
{
return fserve_add_client_mount (client, NULL, file);
}
/* add client to file serving engine, but just write out the buffer contents,
* then pass the client to the callback with the provided arg
*/
@ -614,10 +690,7 @@ void fserve_add_client_callback (client_t *client, fserve_callback_t callback, v
fclient->callback = callback;
fclient->arg = arg;
thread_mutex_lock (&pending_lock);
fclient->next = (fserve_t *)pending_list;
pending_list = fclient;
thread_mutex_unlock (&pending_lock);
fserve_add_pending (fclient);
}
@ -637,20 +710,25 @@ static int _compare_mappings(void *arg, void *a, void *b)
((mime_type *)b)->ext);
}
static void create_mime_mappings(const char *fn) {
FILE *mimefile = fopen(fn, "r");
void fserve_recheck_mime_types (ice_config_t *config)
{
FILE *mimefile;
char line[4096];
char *type, *ext, *cur;
mime_type *mapping;
avl_tree *new_mimetypes;
mimetypes = avl_tree_new(_compare_mappings, NULL);
if (config->mimetypes_fn == NULL)
return;
mimefile = fopen (config->mimetypes_fn, "r");
if (mimefile == NULL)
{
WARN1 ("Cannot open mime type file %s", fn);
WARN1 ("Cannot open mime types file %s", config->mimetypes_fn);
return;
}
new_mimetypes = avl_tree_new(_compare_mappings, NULL);
while(fgets(line, 4096, mimefile))
{
line[4095] = 0;
@ -686,13 +764,55 @@ static void create_mime_mappings(const char *fn) {
mapping = malloc(sizeof(mime_type));
mapping->ext = strdup(ext);
mapping->type = strdup(type);
if(!avl_get_by_key(mimetypes, mapping, &tmp))
avl_delete(mimetypes, mapping, _delete_mapping);
avl_insert(mimetypes, mapping);
if (!avl_get_by_key (new_mimetypes, mapping, &tmp))
avl_delete (new_mimetypes, mapping, _delete_mapping);
if (avl_insert (new_mimetypes, mapping) != 0)
_delete_mapping (mapping);
}
}
}
fclose(mimefile);
thread_spin_lock (&pending_lock);
if (mimetypes)
avl_tree_free (mimetypes, _delete_mapping);
mimetypes = new_mimetypes;
thread_spin_unlock (&pending_lock);
}
#if 0
/* generate xml list containing listener details for clients attached via the file
* serving engine.
*/
xmlDocPtr command_show_listeners(client_t *client, source_t *source,
int response)
{
xmlDocPtr doc;
xmlNodePtr node, srcnode;
unsigned long id = -1;
char *ID_str = NULL;
char buf[22];
doc = xmlNewDoc(XMLSTR("1.0"));
node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
xmlDocSetRootElement(doc, node);
{
client_t *listener;
thread_mutex_lock (&source->lock);
listener = source->active_clients;
while (listener)
{
add_listener_node (srcnode, listener);
listener = listener->next;
}
thread_mutex_unlock (&source->lock);
}
admin_send_response(doc, client, response, "listclients.xsl");
xmlFreeDoc(doc);
}
#endif

View File

@ -14,6 +14,7 @@
#define __FSERVE_H__
#include <stdio.h>
#include "cfgfile.h"
typedef void (*fserve_callback_t)(client_t *, void *);
@ -25,6 +26,7 @@ typedef struct _fserve_t
int ready;
void (*callback)(client_t *, void *);
void *arg;
char *mount;
struct _fserve_t *next;
} fserve_t;
@ -33,7 +35,8 @@ void fserve_shutdown(void);
int fserve_client_create(client_t *httpclient, const char *path);
int fserve_add_client (client_t *client, FILE *file);
void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg);
const char *fserve_content_type (const char *path);
char *fserve_content_type (const char *path);
void fserve_recheck_mime_types (ice_config_t *config);
#endif

View File

@ -35,7 +35,6 @@ static mutex_t _global_mutex;
void global_initialize(void)
{
memset(global.serversock, 0, sizeof(int)*MAX_LISTEN_SOCKETS);
global.server_sockets = 0;
global.relays = NULL;
global.master_relays = NULL;
@ -45,12 +44,18 @@ void global_initialize(void)
global.time = time(NULL);
global.source_tree = avl_tree_new(source_compare_sources, NULL);
thread_mutex_create("global", &_global_mutex);
thread_spin_create ("xyz", &global.spinlock);
/* do a sampling based on 1/10ths of a second */
global.out_bitrate = rate_setup (60);
}
void global_shutdown(void)
{
thread_mutex_destroy(&_global_mutex);
thread_spin_destroy (&global.spinlock);
avl_tree_free(global.source_tree, NULL);
rate_free (global.out_bitrate);
global.out_bitrate = NULL;
}
void global_lock(void)
@ -62,3 +67,29 @@ void global_unlock(void)
{
thread_mutex_unlock(&_global_mutex);
}
void global_add_bitrates (struct rate_calc *rate, unsigned long value)
{
time_t t = (time_t)(global.time_ms/100);
thread_spin_lock (&global.spinlock);
rate_add (rate, value, t);
thread_spin_unlock (&global.spinlock);
}
void global_reduce_bitrate_sampling (struct rate_calc *rate)
{
thread_spin_lock (&global.spinlock);
rate_reduce (rate, 5);
thread_spin_unlock (&global.spinlock);
}
unsigned long global_getrate_avg (struct rate_calc *rate)
{
unsigned long v;
thread_spin_lock (&global.spinlock);
v = rate_avg (rate)*10L;
thread_spin_unlock (&global.spinlock);
return v;
}

View File

@ -13,6 +13,8 @@
#ifndef __GLOBAL_H__
#define __GLOBAL_H__
#include "config.h"
#define ICE_LISTEN_QUEUE 5
#define ICE_RUNNING 1
@ -20,15 +22,16 @@
#define ICECAST_VERSION_STRING "Icecast " PACKAGE_VERSION
#define MAX_LISTEN_SOCKETS 50
#include "thread/thread.h"
#include "slave.h"
#include "timing/timing.h"
#include "net/sock.h"
typedef struct ice_global_tag
{
int serversock[MAX_LISTEN_SOCKETS];
int server_sockets;
sock_t *serversock;
struct _listener_t **server_conn;
int running;
@ -37,6 +40,7 @@ typedef struct ice_global_tag
int schedule_config_reread;
time_t time;
uint64_t time_ms;
avl_tree *source_tree;
/* for locally defined relays */
@ -48,6 +52,9 @@ typedef struct ice_global_tag
unsigned int redirect_count;
struct _redirect_host *redirectors;
spin_t spinlock;
struct rate_calc *out_bitrate;
cond_t shutdown_cond;
} ice_global_t;
@ -57,5 +64,8 @@ void global_initialize(void);
void global_shutdown(void);
void global_lock(void);
void global_unlock(void);
void global_add_bitrates (struct rate_calc *rate, unsigned long value);
void global_reduce_bitrate_sampling (struct rate_calc *rate);
unsigned long global_getrate_avg (struct rate_calc *rate);
#endif /* __GLOBAL_H__ */

View File

@ -30,10 +30,6 @@
#include "logging.h"
#include "util.h"
#ifdef _WIN32
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif
/* the global log descriptors */
int errorlog = 0;
@ -115,8 +111,11 @@ void logging_access(client_t *client)
struct tm thetime;
time_t now;
time_t stayed;
char *referrer, *user_agent, *username, *ip = "-";
const char *referrer, *user_agent, *username, *ip = "-";
#ifdef HAVE_LOG_DIRECT_KEEP
int keep = 0;
#endif
ice_config_t *config;
now = global.time;
@ -150,18 +149,19 @@ void logging_access(client_t *client)
if (user_agent == NULL)
user_agent = "-";
#ifdef HAVE_LOGGING_IP
ip = client->con->ip;
#endif
config = config_get_config();
if (config->access_log_ip)
ip = client->con->ip;
config_release_config ();
#ifdef HAVE_LOG_DIRECT_KEEP
if (httpp_getvar (client->parser, "__avoid_access_log") == NULL)
keep = 1;
#ifdef HAVE_LOG_DIRECT_KEEP
log_write_direct_keep (accesslog, keep,
#else
log_write_direct (accesslog,
#endif
"%s - %s [%s] \"%s\" %d " FORMAT_UINT64 " \"%s\" \"%s\" %lu",
"%s - %s [%s] \"%s\" %d %" PRIu64 " \"%s\" \"%s\" %lu",
ip, username,
datebuf, reqbuf, client->respcode, client->con->sent_bytes,
referrer, user_agent, (unsigned long)stayed);
@ -237,7 +237,7 @@ void restart_logging (ice_config_t *config)
log_set_filename (accesslog, fn_error);
log_set_trigger (accesslog, config->logsize);
log_set_lines_kept (accesslog, config->access_log_lines);
log_set_archive_timestamp(errorlog, config->logarchive);
log_set_archive_timestamp (accesslog, config->logarchive);
log_reopen (accesslog);
}
@ -248,7 +248,7 @@ void restart_logging (ice_config_t *config)
log_set_filename (playlistlog, fn_error);
log_set_trigger (playlistlog, config->logsize);
log_set_lines_kept (playlistlog, config->playlist_log_lines);
log_set_archive_timestamp(errorlog, config->logarchive);
log_set_archive_timestamp (playlistlog, config->logarchive);
log_reopen (playlistlog);
}
}

View File

@ -33,16 +33,19 @@ extern int playlistlog;
#define ERROR1(y, a) log_write(errorlog, 1, CATMODULE "/", __func__, y, a)
#define ERROR2(y, a, b) log_write(errorlog, 1, CATMODULE "/", __func__, y, a, b)
#define ERROR3(y, a, b, c) log_write(errorlog, 1, CATMODULE "/", __func__, y, a, b, c)
#define ERROR4(y, a, b, c, d) log_write(errorlog, 1, CATMODULE "/", __func__, y, a, b, c, d)
#define WARN0(y) log_write(errorlog, 2, CATMODULE "/", __func__, y)
#define WARN1(y, a) log_write(errorlog, 2, CATMODULE "/", __func__, y, a)
#define WARN2(y, a, b) log_write(errorlog, 2, CATMODULE "/", __func__, y, a, b)
#define WARN3(y, a, b, c) log_write(errorlog, 2, CATMODULE "/", __func__, y, a, b, c)
#define WARN4(y, a, b, c, d) log_write(errorlog, 2, CATMODULE "/", __func__, y, a, b, c, d)
#define INFO0(y) log_write(errorlog, 3, CATMODULE "/", __func__, y)
#define INFO1(y, a) log_write(errorlog, 3, CATMODULE "/", __func__, y, a)
#define INFO2(y, a, b) log_write(errorlog, 3, CATMODULE "/", __func__, y, a, b)
#define INFO3(y, a, b, c) log_write(errorlog, 3, CATMODULE "/", __func__, y, a, b, c)
#define INFO4(y, a, b, c, d) log_write(errorlog, 3, CATMODULE "/", __func__, y, a, b, c, d)
#define DEBUG0(y) log_write(errorlog, 4, CATMODULE "/", __func__, y)
#define DEBUG1(y, a) log_write(errorlog, 4, CATMODULE "/", __func__, y, a)

View File

@ -59,8 +59,6 @@
#ifdef _WIN32
/* For getpid() */
#include <process.h>
#define snprintf _snprintf
#define getpid _getpid
#endif
#undef CATMODULE
@ -275,69 +273,15 @@ static int _start_logging(void)
return 0;
}
static int _setup_sockets(void)
{
ice_config_t *config;
int i = 0;
int ret = 0;
int successful = 0;
char pbuf[1024];
config = config_get_config_unlocked();
for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
if(config->listeners[i].port <= 0)
break;
global.serversock[i] = sock_get_server_socket(
config->listeners[i].port, config->listeners[i].bind_address);
if (global.serversock[i] == SOCK_ERROR) {
memset(pbuf, '\000', sizeof(pbuf));
snprintf(pbuf, sizeof(pbuf)-1,
"Could not create listener socket on port %d",
config->listeners[i].port);
_fatal_error(pbuf);
return 0;
}
else {
ret = 1;
successful++;
}
}
global.server_sockets = successful;
return ret;
}
static int _start_listening(void)
{
int i;
for(i=0; i < global.server_sockets; i++) {
if (sock_listen(global.serversock[i], ICE_LISTEN_QUEUE) == SOCK_ERROR)
return 0;
sock_set_blocking(global.serversock[i], SOCK_NONBLOCK);
}
return 1;
}
/* bind the socket and start listening */
static int _server_proc_init(void)
{
ice_config_t *config;
ice_config_t *config = config_get_config_unlocked();
if (!_setup_sockets())
return 0;
if (config->chuid)
connection_setup_sockets (config);
if (!_start_listening()) {
_fatal_error("Failed trying to listen on server socket");
return 0;
}
config = config_get_config_unlocked();
/* recreate the pid file */
if (config->pidfile)
{
@ -356,18 +300,16 @@ static int _server_proc_init(void)
/* this is the heart of the beast */
static void _server_proc(void)
{
int i;
if (background)
{
fclose (stdin);
fclose (stdout);
fclose (stderr);
}
connection_accept_loop();
slave_initialize();
for(i=0; i < MAX_LISTEN_SOCKETS; i++)
sock_close(global.serversock[i]);
connection_thread_shutdown();
connection_setup_sockets (NULL);
}
/* chroot the process. Watch out - we need to do this before starting other
@ -543,7 +485,6 @@ int main(int argc, char **argv)
yp_initialize();
/* Do this after logging init */
slave_initialize();
auth_initialise ();
_server_proc();

View File

@ -39,7 +39,7 @@ refbuf_t *refbuf_new(size_t size)
{
refbuf_t *refbuf;
refbuf = (refbuf_t *)malloc(sizeof(refbuf_t));
refbuf = (refbuf_t *)calloc(1, sizeof(refbuf_t));
if (refbuf == NULL)
abort();
refbuf->data = NULL;
@ -60,6 +60,8 @@ refbuf_t *refbuf_new(size_t size)
void refbuf_addref(refbuf_t *self)
{
if (self == NULL)
return;
self->_count++;
}

View File

@ -23,13 +23,13 @@
typedef struct _refbuf_tag
{
unsigned int len;
unsigned int _count;
char *data;
size_t len;
int sync_point;
struct _refbuf_tag *associated;
struct _refbuf_tag *next;
int sync_point;
unsigned long _count;
} refbuf_t;
void refbuf_initialize(void);

View File

@ -32,17 +32,18 @@
#include <netinet/in.h>
#else
#include <winsock2.h>
#define snprintf _snprintf
#define strcasecmp stricmp
#define strncasecmp strnicmp
#endif
#ifdef HAVE_CURL
#include <curl/curl.h>
#endif
#ifdef HAVE_GETRLIMIT
#include <sys/resource.h>
#endif
#include "compat.h"
#include "thread/thread.h"
#include "timing/timing.h"
#include "avl/avl.h"
#include "net/sock.h"
#include "httpp/httpp.h"
@ -66,25 +67,32 @@ static void redirector_add (const char *server, int port, int interval);
static redirect_host *find_slave_host (const char *server, int port);
static void redirector_clearall (void);
static thread_type *_slave_thread_id;
static int slave_running = 0;
static int update_settings = 0;
static volatile unsigned int max_interval = 0;
static volatile int update_settings = 0;
static volatile int update_all_mounts = 0;
static volatile int restart_connection_thread = 0;
static time_t streamlist_check = 0;
static rwlock_t slaves_lock;
relay_server *relay_free (relay_server *relay)
{
relay_server *next = relay->next;
DEBUG1("freeing relay %s", relay->localmount);
if (relay->source)
source_free_source (relay->source);
xmlFree (relay->server);
xmlFree (relay->mount);
while (relay->masters)
{
relay_server_master *master = relay->masters;
relay->masters = master->next;
xmlFree (master->ip);
xmlFree (master->mount);
xmlFree (master->bind);
free (master);
}
xmlFree (relay->localmount);
if (relay->username)
xmlFree (relay->username);
if (relay->password)
xmlFree (relay->password);
if (relay->username) xmlFree (relay->username);
if (relay->password) xmlFree (relay->password);
free (relay);
return next;
}
@ -96,16 +104,29 @@ relay_server *relay_copy (relay_server *r)
if (copy)
{
copy->server = (char *)xmlCharStrdup (r->server);
copy->mount = (char *)xmlCharStrdup (r->mount);
relay_server_master *from = r->masters, **insert = &copy->masters;
while (from)
{
relay_server_master *to = calloc (1, sizeof (relay_server_master));
to->ip = (char *)xmlCharStrdup (from->ip);
to->mount = (char *)xmlCharStrdup (from->mount);
if (from->bind)
to->bind = (char *)xmlCharStrdup (from->bind);
to->port = from->port;
*insert = to;
from = from->next;
insert = &to->next;
}
copy->localmount = (char *)xmlStrdup (XMLSTR(r->localmount));
if (r->username)
copy->username = (char *)xmlStrdup (XMLSTR(r->username));
if (r->password)
copy->password = (char *)xmlStrdup (XMLSTR(r->password));
copy->port = r->port;
copy->mp3metadata = r->mp3metadata;
copy->on_demand = r->on_demand;
copy->interval = r->interval;
copy->enable = r->enable;
copy->source = r->source;
r->source = NULL;
@ -115,15 +136,25 @@ relay_server *relay_copy (relay_server *r)
/* force a recheck of the relays. This will recheck the master server if
* a this is a slave.
* this is a slave and rebuild all mountpoints in the stats tree
*/
void slave_recheck_mounts (void)
void slave_update_all_mounts (void)
{
max_interval = 0;
update_all_mounts = 1;
update_settings = 1;
}
/* called on reload, so drop all redirection and trigger a relay checkup and
* rebuild all stat mountpoints
*/
void slave_restart (void)
{
restart_connection_thread = 1;
redirector_clearall();
slave_update_all_mounts ();
}
/* Request slave thread to check the relay list for changes and to
* update the stats for the current streams.
*/
@ -140,11 +171,14 @@ void slave_initialize(void)
thread_rwlock_create (&slaves_lock);
slave_running = 1;
max_interval = 0;
streamlist_check = 0;
update_settings = 0;
update_all_mounts = 0;
restart_connection_thread = 0;
#ifndef HAVE_CURL
WARN0 ("streamlist request disabled, rebuild with libcurl if required");
ERROR0 ("streamlist request disabled, rebuild with libcurl if required");
#endif
_slave_thread_id = thread_create("Slave Thread", _slave_thread, NULL, THREAD_ATTACHED);
_slave_thread (NULL);
}
@ -153,8 +187,6 @@ void slave_shutdown(void)
if (!slave_running)
return;
slave_running = 0;
DEBUG0 ("waiting for slave thread");
thread_join (_slave_thread_id);
thread_rwlock_destroy (&slaves_lock);
}
@ -216,57 +248,65 @@ int redirect_client (const char *mountpoint, client_t *client)
}
/* This does the actual connection for a relay. A thread is
* started off if a connection can be acquired
/* Actually open the connection and do some http parsing, handle any 302
* responses within here.
*/
static void *start_relay_stream (void *arg)
static client_t *open_relay_connection (relay_server *relay, relay_server_master *master)
{
relay_server *relay = arg;
sock_t streamsock = SOCK_ERROR;
source_t *src = relay->source;
int redirects = 0;
char *server_id = NULL;
ice_config_t *config;
http_parser_t *parser = NULL;
connection_t *con=NULL;
char *server = strdup (master->ip);
char *mount = strdup (master->mount);
int port = master->port;
char *bind = NULL;
char *auth_header;
char header[4096];
relay->running = 1;
INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount);
do
{
char *auth_header;
char *server_id;
ice_config_t *config;
config = config_get_config ();
server_id = strdup (config->server_id);
config_release_config ();
streamsock = sock_connect_wto (relay->server, relay->port, 10);
if (relay->username && relay->password)
{
char *esc_authorisation;
unsigned len = strlen(relay->username) + strlen(relay->password) + 2;
auth_header = malloc (len);
snprintf (auth_header, len, "%s:%s", relay->username, relay->password);
esc_authorisation = util_base64_encode(auth_header);
free(auth_header);
len = strlen (esc_authorisation) + 24;
auth_header = malloc (len);
snprintf (auth_header, len,
"Authorization: Basic %s\r\n", esc_authorisation);
free(esc_authorisation);
}
else
auth_header = strdup ("");
while (redirects < 10)
{
sock_t streamsock;
/* policy decision, we assume a source bind even after redirect, possible option */
if (master->bind)
bind = master->bind;
if (bind)
INFO3 ("connecting to %s:%d, bound to %s", server, port, bind);
else
INFO2 ("connecting to %s:%d", server, port);
streamsock = sock_connect_wto_bind (server, port, bind, 10);
if (streamsock == SOCK_ERROR)
{
WARN3("Failed to relay stream from master server, couldn't connect to http://%s:%d%s",
relay->server, relay->port, relay->mount);
WARN2 ("Failed to connect to %s:%d", server, port);
break;
}
con = connection_create (streamsock, -1, NULL);
config = config_get_config ();
server_id = strdup (config->server_id);
if (relay->username && relay->password)
{
char *esc_authorisation;
unsigned len = strlen(relay->username) + strlen(relay->password) + 2;
auth_header = malloc (len);
snprintf (auth_header, len, "%s:%s", relay->username, relay->password);
esc_authorisation = util_base64_encode(auth_header);
free(auth_header);
len = strlen (esc_authorisation) + 24;
auth_header = malloc (len);
snprintf (auth_header, len,
"Authorization: Basic %s\r\n", esc_authorisation);
free(esc_authorisation);
}
else
{
auth_header = strdup ("");
}
config_release_config ();
con = connection_create (streamsock, SOCK_ERROR, strdup (server));
/* At this point we may not know if we are relaying an mp3 or vorbis
* stream, but only send the icy-metadata header if the relay details
@ -278,93 +318,189 @@ static void *start_relay_stream (void *arg)
"%s"
"%s"
"\r\n",
relay->mount,
mount,
server_id,
relay->mp3metadata?"Icy-MetaData: 1\r\n":"",
auth_header);
free (server_id);
free (auth_header);
memset (header, 0, sizeof(header));
if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0)
{
WARN0("Header read failed");
ERROR4 ("Header read failed for %s (%s:%d%s)", relay->localmount, server, port, mount);
break;
}
parser = httpp_create_parser();
httpp_initialize (parser, NULL);
if (! httpp_parse_response (parser, header, strlen(header), relay->localmount))
{
ERROR0("Error parsing relay request");
ERROR4("Error parsing relay request for %s (%s:%d%s)", relay->localmount,
server, port, mount);
break;
}
if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0)
{
ERROR1("Error from relay request: %s", httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
break;
}
src->parser = parser;
/* better retry the connection again but with different details */
const char *uri, *mountpoint;
int len;
global_lock ();
if (client_create (&src->client, con, parser) < 0)
{
global_unlock ();
/* make sure only the client_destory frees these */
uri = httpp_getvar (parser, "location");
INFO1 ("redirect received %s", uri);
if (strncmp (uri, "http://", 7) != 0)
break;
uri += 7;
mountpoint = strchr (uri, '/');
free (mount);
if (mountpoint)
mount = strdup (mountpoint);
else
mount = strdup ("/");
len = strcspn (uri, ":/");
port = 80;
if (uri [len] == ':')
port = atoi (uri+len+1);
free (server);
server = calloc (1, len+1);
strncpy (server, uri, len);
connection_close (con);
httpp_destroy (parser);
con = NULL;
parser = NULL;
break;
}
global_unlock ();
sock_set_blocking (streamsock, SOCK_NONBLOCK);
con = NULL;
parser = NULL;
client_set_queue (src->client, NULL);
else
{
client_t *client = NULL;
if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
{
ERROR2("Error from relay request: %s (%s)", relay->localmount,
httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
break;
}
global_lock ();
if (client_create (&client, con, parser) < 0)
{
global_unlock ();
/* make sure only the client_destory frees these */
con = NULL;
parser = NULL;
client_destroy (client);
break;
}
global_unlock ();
sock_set_blocking (streamsock, SOCK_NONBLOCK);
client_set_queue (client, NULL);
free (server);
free (mount);
free (server_id);
free (auth_header);
return client;
}
redirects++;
}
/* failed, better clean up */
free (server);
free (mount);
free (server_id);
free (auth_header);
if (con)
connection_close (con);
if (parser)
httpp_destroy (parser);
return NULL;
}
/* This does the actual connection for a relay. A thread is
* started off if a connection can be acquired
*/
static void *start_relay_stream (void *arg)
{
relay_server *relay = arg;
source_t *src = relay->source;
relay_server_master *master = relay->masters;
client_t *client = NULL;
mount_proxy *mountinfo;
ice_config_t *config;
INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount);
thread_mutex_lock (&src->lock);
do
{
if (master)
{
thread_mutex_unlock (&src->lock);
client = open_relay_connection (relay, master);
thread_mutex_lock (&src->lock);
if (client == NULL)
continue;
src->client = client;
src->parser = client->parser;
}
if (connection_complete_source (src, 0) < 0)
{
DEBUG0("Failed to complete source initialisation");
break;
INFO0("Failed to complete source initialisation");
client_destroy (client);
src->client = NULL;
continue;
}
if (client)
{
stats_event_inc (NULL, "source_relay_connections");
stats_event (relay->localmount, "source_ip", client->con->ip);
}
stats_event_inc(NULL, "source_relay_connections");
stats_event (relay->localmount, "source_ip", relay->server);
source_main (relay->source);
if (relay->on_demand == 0)
{
/* try next server if configured */
if (global.running == ICE_RUNNING && relay->running && master->next)
continue;
/* only keep refreshing YP entries for inactive on-demand relays */
yp_remove (relay->localmount);
relay->source->yp_public = -1;
relay->start = global.time + 10; /* prevent busy looping if failing */
slave_update_all_mounts();
}
/* we've finished, now get cleaned up */
thread_mutex_unlock (&src->lock);
relay->cleanup = 1;
slave_rebuild_mounts();
return NULL;
} while (0);
} while ((master = master->next));
if (relay->source->fallback_mount)
/* source should be locked here */
config = config_get_config();
mountinfo = config_find_mount (config, src->mount);
if (mountinfo && mountinfo->fallback_mount)
{
source_t *fallback_source;
DEBUG1 ("failed relay, fallback to %s", relay->source->fallback_mount);
INFO1 ("failed relay, fallback to %s", mountinfo->fallback_mount);
avl_tree_rlock(global.source_tree);
fallback_source = source_find_mount (relay->source->fallback_mount);
if (fallback_source != NULL)
source_move_clients (relay->source, fallback_source);
fallback_source = source_find_mount (mountinfo->fallback_mount);
if (fallback_source)
{
thread_mutex_unlock (&src->lock);
source_move_clients (src, fallback_source);
thread_mutex_lock (&src->lock);
}
avl_tree_unlock (global.source_tree);
}
config_release_config();
if (con)
connection_close (con);
if (parser)
httpp_destroy (parser);
source_clear_source (relay->source);
thread_mutex_unlock (&src->lock);
/* cleanup relay, but prevent this relay from starting up again too soon */
relay->start = global.time + max_interval;
relay->start = global.time + relay->interval;
relay->cleanup = 1;
return NULL;
@ -387,7 +523,8 @@ static void check_relay_stream (relay_server *relay)
if (relay->source)
{
DEBUG1("Adding relay source at mountpoint \"%s\"", relay->localmount);
slave_rebuild_mounts();
if (relay->on_demand)
slave_update_all_mounts();
}
else
WARN1 ("new relay but source \"%s\" already exists", relay->localmount);
@ -403,27 +540,33 @@ static void check_relay_stream (relay_server *relay)
stats_event (relay->localmount, NULL, NULL);
break;
}
/* check if an inactive on-demand relay has a fallback that has listeners */
if (relay->on_demand && source->on_demand_req == 0)
{
relay->source->on_demand = relay->on_demand;
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, relay->localmount);
if (source->fallback_mount && source->fallback_override)
source->on_demand = relay->on_demand;
if (mountinfo && mountinfo->fallback_mount && mountinfo->fallback_override)
{
source_t *fallback;
avl_tree_rlock (global.source_tree);
fallback = source_find_mount (source->fallback_mount);
fallback = source_find_mount (mountinfo->fallback_mount);
if (fallback && fallback->running && fallback->listeners)
{
DEBUG2 ("fallback running %d with %lu listeners", fallback->running, fallback->listeners);
DEBUG1 ("fallback running with %lu listeners", fallback->listeners);
source->on_demand_req = 1;
}
avl_tree_unlock (global.source_tree);
}
config_release_config();
if (source->on_demand_req == 0)
break;
}
relay->start = global.time + 5;
relay->running = 1;
relay->thread = thread_create ("Relay Thread", start_relay_stream,
relay, THREAD_ATTACHED);
return;
@ -444,14 +587,15 @@ static void check_relay_stream (relay_server *relay)
if (relay->enable == 0)
{
stats_event (relay->localmount, NULL, NULL);
slave_rebuild_mounts();
return;
}
if (relay->on_demand && relay->source)
{
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, relay->localmount);
thread_mutex_lock (&relay->source->lock);
source_update_settings (config, relay->source, mountinfo);
thread_mutex_unlock (&relay->source->lock);
config_release_config ();
stats_event (relay->localmount, "listeners", "0");
}
@ -466,11 +610,20 @@ static int relay_has_changed (relay_server *new, relay_server *old)
{
do
{
if (strcmp (new->mount, old->mount) != 0)
break;
if (strcmp (new->server, old->server) != 0)
break;
if (new->port != old->port)
relay_server_master *oldmaster = old->masters, *newmaster = new->masters;
while (oldmaster && newmaster)
{
if (strcmp (newmaster->mount, oldmaster->mount) != 0)
break;
if (strcmp (newmaster->ip, oldmaster->ip) != 0)
break;
if (newmaster->port != oldmaster->port)
break;
oldmaster = oldmaster->next;
newmaster = newmaster->next;
}
if (oldmaster || newmaster)
break;
if (new->mp3metadata != old->mp3metadata)
break;
@ -556,9 +709,9 @@ static void relay_check_streams (relay_server *to_start,
{
/* relay has been removed from xml, shut down active relay */
DEBUG1 ("source shutdown request on \"%s\"", to_free->localmount);
to_free->running = 0;
to_free->source->running = 0;
thread_join (to_free->thread);
slave_rebuild_mounts();
}
else
stats_event (to_free->localmount, NULL, NULL);
@ -587,9 +740,11 @@ struct master_conn_details
int on_demand;
int previous;
int ok;
int max_interval;
char *buffer;
char *username;
char *password;
char *bind;
char *server_id;
char *args;
relay_server *new_relays;
@ -671,12 +826,17 @@ static size_t streamlist_data (void *ptr, size_t size, size_t nmemb, void *strea
if (strlen (buf))
{
relay_server *r = calloc (1, sizeof (relay_server));
r->server = (char *)xmlStrdup (XMLSTR(master->server));
r->port = master->port;
r->mount = (char *)xmlStrdup (XMLSTR(buf));
relay_server_master *m = calloc (1, sizeof (relay_server_master));
m->ip = (char *)xmlStrdup (XMLSTR(master->server));
m->port = master->port;
if (master->bind)
m->bind = (char *)xmlStrdup (XMLSTR(master->bind));
m->mount = (char *)xmlStrdup (XMLSTR(buf));
r->masters = m;
r->localmount = (char *)xmlStrdup (XMLSTR(buf));
r->mp3metadata = 1;
r->on_demand = master->on_demand;
r->interval = master->max_interval;
r->enable = 1;
if (master->send_auth)
{
@ -727,7 +887,9 @@ static void *streamlist_thread (void *arg)
curl_easy_setopt (handle, CURLOPT_ERRORBUFFER, error);
curl_easy_setopt (handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt (handle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (handle, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt (handle, CURLOPT_TIMEOUT, 10L);
if (master->bind)
curl_easy_setopt (handle, CURLOPT_INTERFACE, master->bind);
if (curl_easy_perform (handle) != 0)
WARN2 ("Failed URL access \"%s\" (%s)", url, error);
@ -773,8 +935,10 @@ static void update_from_master (ice_config_t *config)
details->username = strdup (config->master_username);
details->password = strdup (config->master_password);
details->send_auth = config->master_relay_auth;
details->bind = (config->master_bind) ? strdup (config->master_bind) : NULL;
details->on_demand = config->on_demand;
details->server_id = strdup (config->server_id);
details->max_interval = config->master_update_interval;
if (config->master_redirect)
{
details->args = malloc (4096);
@ -794,10 +958,7 @@ static void update_master_as_slave (ice_config_t *config)
redirect_host *redirect;
if (config->master_server == NULL || config->master_redirect == 0 || config->max_redirects == 0)
{
redirector_clearall();
return;
}
return;
thread_rwlock_wlock (&slaves_lock);
redirect = find_slave_host (config->master_server, config->master_server_port);
@ -807,50 +968,76 @@ static void update_master_as_slave (ice_config_t *config)
redirector_add (config->master_server, config->master_server_port, 0);
}
else
redirect->next_update += max_interval;
redirect->next_update += config->master_update_interval;
thread_rwlock_unlock (&slaves_lock);
}
static void *_slave_thread(void *arg)
static void slave_startup (void)
{
ice_config_t *config;
unsigned int interval = 0;
#ifdef HAVE_GETRLIMIT
struct rlimit rlimit;
if (getrlimit (RLIMIT_NOFILE, &rlimit) == 0)
{
INFO2 ("max file descriptors %ld (hard limit %ld)",
(long)rlimit.rlim_cur, (long)rlimit.rlim_max);
}
#endif
update_settings = 0;
update_all_mounts = 0;
config = config_get_config();
update_master_as_slave (config);
stats_global (config);
config_release_config();
source_recheck_mounts();
source_recheck_mounts (1);
connection_thread_startup();
}
static void *_slave_thread(void *arg)
{
slave_startup();
while (1)
{
relay_server *cleanup_relays = NULL;
int skip_timer = 0;
uint64_t prev_time_ms = timing_get_time();
/* re-read xml file if requested */
if (global . schedule_config_reread)
do
{
event_config_read (NULL);
global . schedule_config_reread = 0;
}
/* re-read xml file if requested */
if (global . schedule_config_reread)
{
event_config_read ();
global . schedule_config_reread = 0;
}
thread_sleep (1000000);
if (slave_running == 0)
if (global.running != ICE_RUNNING)
break;
thread_sleep (100000);
global.time_ms = timing_get_time ();
global.time = (long)(global.time_ms/(uint64_t)1000);
global_add_bitrates (global.out_bitrate, 0L);
} while (global.time_ms - prev_time_ms < 1000);
prev_time_ms = global.time_ms;
if (global.running != ICE_RUNNING)
break;
time (&global.time);
++interval;
thread_mutex_lock (&(config_locks()->relay_lock));
/* only update relays lists when required */
if (max_interval <= interval)
/* only update relays lists from master when required */
if (streamlist_check <= global.time)
{
config = config_get_config();
ice_config_t *config = config_get_config();
if (max_interval == 0)
if (streamlist_check == 0)
skip_timer = 1;
interval = 0;
max_interval = config->master_update_interval;
streamlist_check = global.time + config->master_update_interval;
update_master_as_slave (config);
update_from_master (config);
@ -859,20 +1046,30 @@ static void *_slave_thread(void *arg)
config_release_config();
}
relay_check_streams (global.master_relays, NULL, skip_timer);
relay_check_streams (global.relays, cleanup_relays, skip_timer);
thread_mutex_unlock (&(config_locks()->relay_lock));
relay_check_streams (global.master_relays, NULL, skip_timer);
if (update_settings)
{
source_recheck_mounts (update_all_mounts);
update_settings = 0;
source_recheck_mounts();
update_all_mounts = 0;
if (restart_connection_thread)
{
connection_thread_startup();
restart_connection_thread = 0;
}
}
/* trigger any YP processing */
yp_thread_startup();
}
connection_thread_shutdown();
INFO0 ("shutting down current relays");
relay_check_streams (NULL, global.relays, 0);
relay_check_streams (NULL, global.master_relays, 0);
redirector_clearall();
/* send any removals to the YP servers */
yp_thread_startup();
INFO0 ("Slave thread shutdown complete");
@ -901,6 +1098,7 @@ static void redirector_clearall (void)
{
redirect_host *current = global.redirectors;
global.redirectors = current->next;
INFO2 ("removing %s:%d", current->server, current->port);
free (current->server);
free (current);
}
@ -914,7 +1112,7 @@ void redirector_update (client_t *client)
{
redirect_host *redirect;
const char *rserver = httpp_get_query_param (client->parser, "rserver");
char *value;
const char *value;
int rport, interval;
if (rserver==NULL) return;
@ -963,7 +1161,7 @@ static redirect_host *find_slave_host (const char *server, int port)
static void redirector_add (const char *server, int port, int interval)
{
ice_config_t *config = config_get_config();
int allowed = config->max_redirects;
unsigned int allowed = config->max_redirects;
redirect_host *redirect;
config_release_config();

View File

@ -13,18 +13,25 @@
#ifndef __SLAVE_H__
#define __SLAVE_H__
#include <thread/thread.h>
#include "thread/thread.h"
struct _client_tag;
typedef struct _relay_server {
char *server;
int port;
typedef struct _relay_server_master {
struct _relay_server_master *next;
char *ip;
char *bind;
char *mount;
int port;
} relay_server_master;
typedef struct _relay_server {
relay_server_master *masters;
char *username;
char *password;
char *localmount;
struct source_tag *source;
int interval;
int mp3metadata;
int on_demand;
int running;
@ -45,7 +52,8 @@ typedef struct _redirect_host
void slave_initialize(void);
void slave_shutdown(void);
void slave_recheck_mounts (void);
void slave_restart (void);
void slave_update_all_mounts (void);
void slave_rebuild_mounts (void);
relay_server *slave_find_relay (relay_server *relays, const char *mount);
int redirect_client (const char *mountpoint, struct _client_tag *client);

View File

@ -30,7 +30,6 @@
#else
#include <winsock2.h>
#include <windows.h>
#define snprintf _snprintf
#endif
#include "thread/thread.h"
@ -64,7 +63,6 @@ static int _compare_clients(void *compare_arg, void *a, void *b);
static void _parse_audio_info (source_t *source, const char *s);
static void source_shutdown (source_t *source);
static void process_listeners (source_t *source, int fast_clients_only, int deletion_expected);
static int source_free_client (source_t *source, client_t *client);
#ifdef _WIN32
#define source_run_script(x,y) WARN0("on [dis]connect scripts disabled");
#else
@ -95,8 +93,8 @@ source_t *source_reserve (const char *mount)
/* make duplicates for strings or similar */
src->mount = strdup (mount);
src->max_listeners = -1;
src->avg_bitrate_duration = 60;
src->listener_send_trigger = 10000;
thread_mutex_create (src->mount, &src->lock);
@ -191,12 +189,19 @@ int source_compare_sources(void *arg, void *a, void *b)
void source_clear_source (source_t *source)
{
int i;
ice_config_t *config;
mount_proxy *mountinfo;
refbuf_t *p;
DEBUG1 ("clearing source \"%s\"", source->mount);
/* log bytes read in access log */
if (source->client && source->format)
source->client->con->sent_bytes = source->format->read_bytes;
if (source->client)
{
source->client->authenticated = 0;
if (source->format)
source->client->con->sent_bytes = source->format->read_bytes;
}
client_destroy(source->client);
source->client = NULL;
source->parser = NULL;
@ -208,34 +213,56 @@ void source_clear_source (source_t *source)
source->dumpfile = NULL;
}
/* lets drop any clients still connected */
/* lets drop any listeners still connected */
i = 0;
config = config_get_config ();
mountinfo = config_find_mount (config, source->mount);
while (source->active_clients)
{
client_t *client = source->active_clients;
source->active_clients = client->next;
source_free_client (source, client);
i++;
client->next = NULL;
/* do not count listeners who have joined but haven't done any processing */
if (client->respcode == 200)
i++;
auth_release_listener (client, source->mount, mountinfo);
}
config_release_config ();
if (i)
stats_event_sub (NULL, "listeners", i);
source->fast_clients_p = &source->active_clients;
DEBUG1 ("removed %d listeners", i);
format_free_plugin (source->format);
source->format = NULL;
/* flush out the stream data, we don't want any left over */
while (source->stream_data)
/* first remove the reference for refbufs marked for burst */
p = source->burst_point;
while (p)
{
refbuf_t *p = source->stream_data;
source->stream_data = p->next;
/* can be referenced by burst handler as well */
while (p->_count > 1)
refbuf_release (p);
refbuf_release (p);
refbuf_t *to_go = p;
p = to_go->next;
refbuf_release (to_go);
}
source->burst_point = NULL;
/* then the normal queue, there could be listeners still pending */
p = source->stream_data;
while (p)
{
refbuf_t *to_go = p;
p = to_go->next;
refbuf_release (to_go);
}
/* the source holds 2 references on the very latest so that one
* always exists */
if (p)
refbuf_release (p);
source->stream_data = NULL;
source->stream_data_tail = NULL;
source->burst_point = NULL;
source->burst_size = 0;
source->burst_offset = 0;
source->queue_size = 0;
@ -243,21 +270,13 @@ void source_clear_source (source_t *source)
source->listeners = 0;
source->prev_listeners = 0;
source->shoutcast_compat = 0;
source->max_listeners = -1;
source->hidden = 0;
source->client_stats_update = 0;
util_dict_free (source->audio_info);
source->audio_info = NULL;
free(source->fallback_mount);
source->fallback_mount = NULL;
free(source->dumpfilename);
source->dumpfilename = NULL;
free (source->charset);
source->charset = NULL;
if (source->intro_file)
{
fclose (source->intro_file);
@ -392,10 +411,8 @@ void source_move_clients (source_t *source, source_t *dest)
thread_mutex_unlock (&source->lock);
/* see if we need to wake up an on-demand relay */
if (dest->running == 0 && dest->on_demand && count)
{
dest->on_demand_req = 1;
slave_rebuild_mounts();
}
thread_mutex_unlock (&dest->lock);
thread_mutex_unlock (&move_clients_mutex);
}
@ -405,9 +422,10 @@ void source_move_clients (source_t *source, source_t *dest)
*/
static void update_source_stats (source_t *source)
{
int incoming_rate = 8 * rate_avg (source->format->in_bitrate);
int64_t kbytes_sent = source->bytes_sent_since_update/1024;
int64_t kbytes_read = source->bytes_read_since_update/1024;
unsigned long incoming_rate = 8 * rate_avg (source->format->in_bitrate);
unsigned long kbytes_sent = source->bytes_sent_since_update/1024;
unsigned long kbytes_read = source->bytes_read_since_update/1024;
source->format->sent_bytes += kbytes_sent*1024;
source->bytes_sent_since_update %= 1024;
source->bytes_read_since_update %= 1024;
@ -416,11 +434,11 @@ static void update_source_stats (source_t *source)
(8 * rate_avg (source->format->out_bitrate))/1000);
stats_event_args (source->mount, "incoming_bitrate", "%ld", incoming_rate/1000);
stats_event_args (source->mount, "total_bytes_read",
FORMAT_UINT64, source->format->read_bytes);
"%"PRIu64, source->format->read_bytes);
stats_event_args (source->mount, "total_bytes_sent",
FORMAT_UINT64, source->format->sent_bytes);
"%"PRIu64, source->format->sent_bytes);
if (source->client)
stats_event_args (source->mount, "connected", FORMAT_UINT64,
stats_event_args (source->mount, "connected", "%"PRIu64,
(uint64_t)(global.time - source->client->con->con_time));
stats_event_add (NULL, "stream_kbytes_sent", kbytes_sent);
stats_event_add (NULL, "stream_kbytes_read", kbytes_read);
@ -432,7 +450,7 @@ static void update_source_stats (source_t *source)
/* when throttling, we perform a sleep so that the input is not read as quickly, we
* don't do precise timing here as this just makes sure the incoming bitrate is not
* excessive. lower bitrate stream have higher sleep counts */
float kbits = incoming_rate/8.0;
float kbits = incoming_rate/8.0f;
if (kbits < 1200)
kbits = 1200;
source->throttle_stream = (int)(1000000 / kbits * 1000);
@ -477,6 +495,8 @@ static void get_next_buffer (source_t *source)
time_t current = global.time;
int delay = 200;
source->amount_added_to_queue = 0;
/* service fast clients but jump out once in a while to check on
* normal clients */
if (no_delay_count == 10)
@ -523,7 +543,7 @@ static void get_next_buffer (source_t *source)
{
if (source->last_read + (time_t)source->timeout < current)
{
DEBUG3 ("last %ld, timeout %d, now %ld", (long)source->last_read,
DEBUG3 ("last %ld, timeout %d, now %ld", (long)source->last_read,
source->timeout, (long)current);
WARN0 ("Disconnecting source due to socket timeout");
source->running = 0;
@ -542,6 +562,10 @@ static void get_next_buffer (source_t *source)
if (refbuf)
{
source->bytes_read_since_update += refbuf->len;
source->amount_added_to_queue = refbuf->len;
/* the latest refbuf is counted twice so that it stays */
refbuf_addref (refbuf);
/* append buffer to the in-flight data queue, */
if (source->stream_data == NULL)
@ -551,9 +575,14 @@ static void get_next_buffer (source_t *source)
source->burst_offset = 0;
}
if (source->stream_data_tail)
{
source->stream_data_tail->next = refbuf;
refbuf_release (source->stream_data_tail);
}
source->stream_data_tail = refbuf;
source->queue_size += refbuf->len;
/* increase refcount for keeping burst data */
refbuf_addref (refbuf);
/* move the starting point for new listeners */
@ -576,11 +605,13 @@ static void get_next_buffer (source_t *source)
source->format->write_buf_to_file (source, refbuf);
}
else
{
if (source->client->con && source->client->con->error)
{
INFO1 ("End of Stream %s", source->mount);
source->running = 0;
}
}
break;
}
}
@ -598,26 +629,34 @@ static int send_to_listener (source_t *source, client_t *client, int deletion_ex
{
int bytes;
int loop = 10; /* max number of iterations in one go */
unsigned int total_written = 0;
long total_written = 0;
int ret = 0;
/* check for limited listener time */
if (client->con->discon_time && global.time >= client->con->discon_time)
{
INFO1 ("time limit reached for client #%lu", client->con->id);
client->con->error = 1;
return 0;
}
if (source->amount_added_to_queue)
client->lag += source->amount_added_to_queue;
while (1)
{
/* check for limited listener time */
if (client->con->discon_time)
if (global.time >= client->con->discon_time)
{
INFO1 ("time limit reached for client #%lu", client->con->id);
client->con->error = 1;
}
/* jump out if client connection has died */
if (client->con->error)
break;
if (loop == 0)
{
ret = 0;
break;
}
/* lets not send too much to one client in one go, but don't
sleep for too long if more data can be sent */
if (total_written > 20000 || loop == 0)
if (total_written > source->listener_send_trigger)
{
ret = 1;
break;
@ -637,6 +676,8 @@ static int send_to_listener (source_t *source, client_t *client, int deletion_ex
rate_add (source->format->out_bitrate, total_written, global.time);
source->bytes_sent_since_update += total_written;
global_add_bitrates (global.out_bitrate, total_written);
/* the refbuf referenced at head (last in queue) may be marked for deletion
* if so, check to see if this client is still referring to it */
if (deletion_expected && client->refbuf && client->refbuf == source->stream_data)
@ -645,7 +686,6 @@ static int send_to_listener (source_t *source, client_t *client, int deletion_ex
client->con->id, client->con->ip);
stats_event_inc (source->mount, "slow_listeners");
client->con->error = 1;
ret = 1;
}
return ret;
}
@ -675,11 +715,17 @@ static void process_listeners (source_t *source, int fast_clients_only, int dele
if (client->con->error)
{
client_t *to_go = client;
ice_config_t *config;
mount_proxy *mountinfo;
*client_p = client->next;
client = client->next;
source_free_client (source, to_go);
config = config_get_config ();
mountinfo = config_find_mount (config, source->mount);
auth_release_listener (to_go, source->mount, mountinfo);
config_release_config();
source->listeners--;
stats_event_dec (NULL, "listeners");
DEBUG0("Client removed");
@ -718,6 +764,9 @@ static void process_listeners (source_t *source, int fast_clients_only, int dele
stats_event_args (source->mount, "listeners", "%lu", source->listeners);
if (source->listeners == 0)
rate_add (source->format->out_bitrate, 0, 0);
/* change of listener numbers, so reduce scope of global sampling */
global_reduce_bitrate_sampling (global.out_bitrate);
/* do we need to shutdown an on-demand relay */
if (source->listeners == 0 && source->on_demand)
source->running = 0;
}
@ -729,13 +778,11 @@ static void process_listeners (source_t *source, int fast_clients_only, int dele
*/
static void source_init (source_t *source)
{
char *str;
mount_proxy *mountinfo;
thread_mutex_lock (&source->lock);
if (source->dumpfilename != NULL)
{
INFO2 ("dumpfile \"%s\" for %s", source->dumpfilename, source->mount);
source->dumpfile = fopen (source->dumpfilename, "ab");
if (source->dumpfile == NULL)
{
@ -768,7 +815,7 @@ static void source_init (source_t *source)
source->audio_info = util_dict_new();
if (source->client)
{
str = httpp_getvar(source->client->parser, "ice-audio-info");
const char *str = httpp_getvar(source->client->parser, "ice-audio-info");
if (str)
{
_parse_audio_info (source, str);
@ -778,39 +825,43 @@ static void source_init (source_t *source)
thread_mutex_unlock (&source->lock);
/* on demand relays should of already called this */
if (source->on_demand == 0)
slave_update_all_mounts();
mountinfo = config_find_mount (config_get_config(), source->mount);
if (mountinfo)
{
if (mountinfo->on_connect)
source_run_script (mountinfo->on_connect, source->mount);
auth_stream_start (mountinfo, source->mount);
/*
** Now, if we have a fallback source and override is on, we want
** to steal its clients, because it means we've come back online
** after a failure and they should be gotten back from the waiting
** loop or jingle track or whatever the fallback is used for
*/
if (mountinfo->fallback_override && mountinfo->fallback_mount)
{
source_t *fallback_source;
avl_tree_rlock(global.source_tree);
fallback_source = source_find_mount (mountinfo->fallback_mount);
if (fallback_source)
source_move_clients (fallback_source, source);
avl_tree_unlock(global.source_tree);
}
}
config_release_config();
/*
** Now, if we have a fallback source and override is on, we want
** to steal its clients, because it means we've come back online
** after a failure and they should be gotten back from the waiting
** loop or jingle track or whatever the fallback is used for
*/
if (source->fallback_override && source->fallback_mount)
{
source_t *fallback_source;
avl_tree_rlock(global.source_tree);
fallback_source = source_find_mount(source->fallback_mount);
if (fallback_source)
source_move_clients (fallback_source, source);
avl_tree_unlock(global.source_tree);
}
source->format->in_bitrate = rate_setup (source->avg_bitrate_duration);
source->format->out_bitrate = rate_setup (source->avg_bitrate_duration);
thread_mutex_lock (&source->lock);
source->format->in_bitrate = rate_setup (source->avg_bitrate_duration+1);
source->format->out_bitrate = rate_setup (source->avg_bitrate_duration+1);
}
@ -838,9 +889,16 @@ void source_main (source_t *source)
*/
if (source->stream_data)
{
/* if we need to prune the queue to keep within the specific limit then
* all other references to the last block need of been gone by now
*/
if (remove_from_q && source->stream_data->_count > 1)
ERROR1 ("unable to prune queue, size is at %ld", source->queue_size);
/* normal unreferenced queue data will have a refcount 1, but
* burst queue data will be at least 2, active clients will also
* increase refcount */
* increase refcount
*/
while (source->stream_data->_count == 1)
{
refbuf_t *to_go = source->stream_data;
@ -848,7 +906,8 @@ void source_main (source_t *source)
if (to_go->next == NULL || source->burst_point == to_go)
{
/* this should not happen */
ERROR0 ("queue state is unexpected");
ERROR2 ("queue state is unexpected (%p, %d)", to_go->next,
source->burst_point == to_go ? 1: 0);
source->running = 0;
break;
}
@ -864,12 +923,13 @@ void source_main (source_t *source)
}
/* this function is expected to keep lock held on exit */
static void source_shutdown (source_t *source)
{
mount_proxy *mountinfo;
INFO1("Source \"%s\" exiting", source->mount);
source->running = 0;
INFO1("Source \"%s\" exiting", source->mount);
update_source_stats (source);
mountinfo = config_find_mount (config_get_config(), source->mount);
@ -878,33 +938,30 @@ static void source_shutdown (source_t *source)
if (mountinfo->on_disconnect)
source_run_script (mountinfo->on_disconnect, source->mount);
auth_stream_end (mountinfo, source->mount);
/* we have de-activated the source now, so no more clients will be
* added, now move the listeners we have to the fallback (if any)
*/
if (mountinfo->fallback_mount)
{
source_t *fallback_source;
avl_tree_rlock (global.source_tree);
fallback_source = source_find_mount (mountinfo->fallback_mount);
if (fallback_source != NULL)
{
/* be careful wrt to deadlocking */
thread_mutex_unlock (&source->lock);
source_move_clients (source, fallback_source);
thread_mutex_lock (&source->lock);
}
avl_tree_unlock (global.source_tree);
}
}
config_release_config();
/* we have de-activated the source now, so no more clients will be
* added, now move the listeners we have to the fallback (if any)
*/
if (source->fallback_mount)
{
source_t *fallback_source;
avl_tree_rlock(global.source_tree);
fallback_source = source_find_mount (source->fallback_mount);
if (fallback_source != NULL)
{
/* be careful wrt to deadlocking */
thread_mutex_unlock (&source->lock);
source_move_clients (source, fallback_source);
thread_mutex_lock (&source->lock);
}
avl_tree_unlock (global.source_tree);
}
if (source->listeners)
stats_event_sub (NULL, "listeners", source->listeners);
/* delete this sources stats */
stats_event(source->mount, NULL, NULL);
@ -912,7 +969,7 @@ static void source_shutdown (source_t *source)
therefore reserved */
source_clear_source (source);
thread_mutex_unlock (&source->lock);
global_reduce_bitrate_sampling (global.out_bitrate);
global_lock();
global.sources--;
@ -939,17 +996,6 @@ static int _compare_clients(void *compare_arg, void *a, void *b)
}
int source_free_client (source_t *source, client_t *client)
{
/* if no response has been sent then send a 404 */
if (client->respcode == 0)
client_send_404 (client, "Mount unavailable");
else
client_destroy (client);
return 1;
}
static void _parse_audio_info (source_t *source, const char *s)
{
const char *start = s;
@ -989,7 +1035,7 @@ static void _parse_audio_info (source_t *source, const char *s)
/* Apply the mountinfo details to the source */
static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
{
char *str;
const char *str;
int val;
http_parser_t *parser = NULL;
@ -999,19 +1045,16 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
INFO2 ("Applying mount information for \"%s\" from \"%s\"",
source->mount, mountinfo->mountname);
if (mountinfo)
{
source->max_listeners = mountinfo->max_listeners;
source->fallback_override = mountinfo->fallback_override;
source->hidden = mountinfo->hidden;
}
/* if a setting is available in the mount details then use it, else
* check the parser details. */
if (source->client)
parser = source->client->parser;
/* to be done before possible non-utf8 stats */
if (source->format && source->format->apply_settings)
source->format->apply_settings (source->client, source->format, mountinfo);
/* public */
if (mountinfo && mountinfo->yp_public >= 0)
val = mountinfo->yp_public;
@ -1044,7 +1087,7 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
/* stream name */
if (mountinfo && mountinfo->stream_name)
str = mountinfo->stream_name;
stats_event (source->mount, "server_name", mountinfo->stream_name);
else
{
do {
@ -1056,11 +1099,9 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
if (str) break;
str = "Unspecified name";
} while (0);
if (source->format)
stats_event_conv (source->mount, "server_name", str, source->format->charset);
}
if (mountinfo && mountinfo->charset)
stats_event_conv (source->mount, "server_name", str, mountinfo->charset);
else
stats_event (source->mount, "server_name", str);
/* stream description */
if (mountinfo && mountinfo->stream_description)
@ -1077,10 +1118,8 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
str = "Unspecified description";
} while (0);
}
if (mountinfo && mountinfo->charset)
stats_event_conv (source->mount, "server_description", str, mountinfo->charset);
else
stats_event (source->mount, "server_description", str);
if (str && source->format)
stats_event_conv (source->mount, "server_description", str, source->format->charset);
/* stream URL */
if (mountinfo && mountinfo->stream_url)
@ -1096,7 +1135,8 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
if (str) break;
} while (0);
}
stats_event (source->mount, "server_url", str);
if (str && source->format)
stats_event_conv (source->mount, "server_url", str, source->format->charset);
/* stream genre */
if (mountinfo && mountinfo->stream_genre)
@ -1113,7 +1153,10 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
str = "various";
} while (0);
}
stats_event (source->mount, "genre", str);
if (source->format)
stats_event_conv (source->mount, "genre", str, source->format->charset);
else
stats_event (source->mount, "genre", str);
/* stream bitrate */
if (mountinfo && mountinfo->bitrate)
@ -1145,16 +1188,6 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
else
stats_event (source->mount, "authenticator", NULL);
free (source->fallback_mount);
source->fallback_mount = NULL;
if (mountinfo && mountinfo->fallback_mount)
source->fallback_mount = strdup (mountinfo->fallback_mount);
free (source->charset);
source->charset = NULL;
if (mountinfo && mountinfo->charset)
source->charset = strdup (mountinfo->charset);
source->limit_rate = 0;
if (mountinfo && mountinfo->limit_rate)
source->limit_rate = mountinfo->limit_rate;
@ -1207,15 +1240,9 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
if (mountinfo && mountinfo->burst_size >= 0)
source->burst_size = (unsigned int)mountinfo->burst_size;
if (mountinfo && mountinfo->fallback_when_full)
source->fallback_when_full = mountinfo->fallback_when_full;
source->wait_time = 0;
if (mountinfo && mountinfo->wait_time)
source->wait_time = (time_t)mountinfo->wait_time;
if (source->format && source->format->apply_settings)
source->format->apply_settings (source->client, source->format, mountinfo);
}
@ -1224,62 +1251,64 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
*/
void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo)
{
/* skip if source is a fallback to file */
source->timeout = config->source_timeout;
/* handle fallback to file specially */
if (source->running && source->client == NULL)
{
stats_event_hidden (source->mount, NULL, 1);
if (mountinfo && mountinfo->source_timeout > 0)
source->timeout = mountinfo->source_timeout;
return;
}
thread_mutex_lock (&source->lock);
/* set global settings first */
source->queue_size_limit = config->queue_size_limit;
source->timeout = config->source_timeout;
source->burst_size = config->burst_size;
stats_event_args (source->mount, "listenurl", "http://%s:%d%s",
config->hostname, config->port, source->mount);
source_apply_mount (source, mountinfo);
if (source->fallback_mount)
DEBUG1 ("fallback %s", source->fallback_mount);
if (source->dumpfilename)
DEBUG1 ("Dumping stream to %s", source->dumpfilename);
if (mountinfo && mountinfo->on_connect)
DEBUG1 ("connect script \"%s\"", mountinfo->on_connect);
if (mountinfo && mountinfo->on_disconnect)
DEBUG1 ("disconnect script \"%s\"", mountinfo->on_disconnect);
if (source->on_demand)
{
DEBUG0 ("on_demand set");
stats_event (source->mount, "on_demand", "1");
stats_event_args (source->mount, "listeners", "%ld", source->listeners);
}
else
stats_event (source->mount, "on_demand", NULL);
if (source->charset)
DEBUG1 ("charset is %s", source->charset);
if (source->hidden)
if (mountinfo)
{
stats_event_hidden (source->mount, NULL, 1);
DEBUG0 ("hidden from xsl");
if (mountinfo->hidden)
{
stats_event_hidden (source->mount, NULL, 1);
DEBUG0 ("hidden from public");
}
else
stats_event_hidden (source->mount, NULL, 0);
if (mountinfo->on_connect)
DEBUG1 ("connect script \"%s\"", mountinfo->on_connect);
if (mountinfo->on_disconnect)
DEBUG1 ("disconnect script \"%s\"", mountinfo->on_disconnect);
if (mountinfo->fallback_when_full)
DEBUG1 ("fallback_when_full to %u", mountinfo->fallback_when_full);
DEBUG1 ("max listeners to %d", mountinfo->max_listeners);
stats_event_args (source->mount, "max_listeners", "%d", mountinfo->max_listeners);
}
else
stats_event_hidden (source->mount, NULL, 0);
if (source->max_listeners == -1)
stats_event (source->mount, "max_listeners", "unlimited");
else
{
char buf [10];
snprintf (buf, sizeof (buf), "%ld", source->max_listeners);
stats_event (source->mount, "max_listeners", buf);
DEBUG0 ("max listeners is not specified");
stats_event (source->mount, "max_listeners", "unlimited");
stats_event_hidden (source->mount, NULL, 0);
}
DEBUG1 ("public set to %d", source->yp_public);
DEBUG1 ("max listeners to %ld", source->max_listeners);
DEBUG1 ("queue size to %u", source->queue_size_limit);
DEBUG1 ("burst size to %u", source->burst_size);
DEBUG1 ("source timeout to %u", source->timeout);
DEBUG1 ("fallback_when_full to %u", source->fallback_when_full);
thread_mutex_unlock (&source->lock);
}
@ -1288,20 +1317,23 @@ void *source_client_thread (void *arg)
source_t *source = arg;
stats_event_inc(NULL, "source_client_connections");
stats_event (source->mount, "listeners", "0");
thread_mutex_lock (&source->lock);
source_main (source);
if (source->wait_time)
{
time_t release = source->wait_time + global.time;
INFO2 ("keeping %s reserved for %d seconds", source->mount, source->wait_time);
source->wait_time += global.time;
while (global.running && source->wait_time >= global.time)
thread_sleep (500000);
thread_mutex_unlock (&source->lock);
while (global.running && release >= global.time)
thread_sleep (300000);
}
else
thread_mutex_unlock (&source->lock);
source_free_source (source);
source_recheck_mounts ();
slave_update_all_mounts();
return NULL;
}
@ -1373,7 +1405,7 @@ static void source_run_script (char *command, char *mountpoint)
static void *source_fallback_file (void *arg)
{
char *mount = arg;
const char *type;
char *type;
char *path;
unsigned int len;
FILE *file = NULL;
@ -1398,7 +1430,7 @@ static void *source_fallback_file (void *arg)
file = fopen (path, "rb");
if (file == NULL)
{
DEBUG1 ("unable to open file \"%s\"", path);
WARN1 ("unable to open file \"%s\"", path);
free (path);
break;
}
@ -1406,19 +1438,23 @@ static void *source_fallback_file (void *arg)
source = source_reserve (mount);
if (source == NULL)
{
DEBUG1 ("mountpoint \"%s\" already reserved", mount);
WARN1 ("mountpoint \"%s\" already reserved", mount);
break;
}
INFO1 ("mountpoint %s is reserved", mount);
type = fserve_content_type (mount);
parser = httpp_create_parser();
httpp_initialize (parser, NULL);
httpp_setvar (parser, "content-type", type);
free (type);
source->hidden = 1;
stats_event_hidden (source->mount, NULL, 1);
source->yp_public = 0;
source->intro_file = file;
source->parser = parser;
source->avg_bitrate_duration = 20;
source->listener_send_trigger = 4096;
source->on_demand = 1;
file = NULL;
if (connection_complete_source (source, 0) < 0)
@ -1432,20 +1468,37 @@ static void *source_fallback_file (void *arg)
return NULL;
}
static int is_mount_template (const char *mount)
{
if (strchr (mount, '*') || strchr (mount, '?') || strchr (mount, '['))
return 1;
return 0;
}
/* rescan the mount list, so that xsl files are updated to show
* unconnected but active fallback mountpoints
*/
void source_recheck_mounts (void)
void source_recheck_mounts (int update_all)
{
ice_config_t *config = config_get_config();
mount_proxy *mount = config->mounts;
avl_tree_rlock (global.source_tree);
if (update_all)
stats_clear_virtual_mounts ();
while (mount)
{
source_t *source = source_find_mount (mount->mountname);
source_t *source;
if (is_mount_template (mount->mountname))
{
mount = mount->next;
continue;
}
source = source_find_mount (mount->mountname);
if (source)
{
@ -1453,11 +1506,15 @@ void source_recheck_mounts (void)
if (source)
{
mount_proxy *mountinfo = config_find_mount (config, source->mount);
thread_mutex_lock (&source->lock);
source_update_settings (config, source, mountinfo);
thread_mutex_unlock (&source->lock);
}
else
else if (update_all)
{
stats_event_hidden (mount->mountname, NULL, mount->hidden);
stats_event_args (mount->mountname, "listenurl", "http://%s:%d%s",
config->hostname, config->port, mount->mountname);
stats_event (mount->mountname, "listeners", "0");
if (mount->max_listeners < 0)
stats_event (mount->mountname, "max_listeners", "unlimited");

View File

@ -28,9 +28,6 @@ typedef struct source_tag
char *mount;
/* If this source drops, try to move all clients to this fallback */
char *fallback_mount;
/* set to zero to request the source to shutdown without causing a global
* shutdown */
int running;
@ -49,21 +46,18 @@ typedef struct source_tag
char *dumpfilename; /* Name of a file to dump incoming stream to */
FILE *dumpfile;
char *charset;
int throttle_stream;
time_t throttle_termination;
int limit_rate;
uint64_t limit_rate;
int avg_bitrate_duration;
time_t wait_time;
long listener_send_trigger;
unsigned long peak_listeners;
unsigned long listeners;
unsigned long prev_listeners;
long max_listeners;
int yp_public;
int fallback_override;
int fallback_when_full;
int shoutcast_compat;
/* per source burst handling for connecting clients */
@ -73,13 +67,13 @@ typedef struct source_tag
unsigned int queue_size;
unsigned int queue_size_limit;
unsigned int amount_added_to_queue;
unsigned timeout; /* source timeout in seconds */
int on_demand;
int on_demand_req;
int hidden;
uint64_t bytes_sent_since_update;
uint64_t bytes_read_since_update;
unsigned long bytes_sent_since_update;
unsigned long bytes_read_since_update;
int stats_interval;
time_t last_read;
@ -105,7 +99,7 @@ void source_free_source(source_t *source);
void source_move_clients (source_t *source, source_t *dest);
int source_remove_client(void *key);
void source_main(source_t *source);
void source_recheck_mounts (void);
void source_recheck_mounts (int update_all);
extern mutex_t move_clients_mutex;

View File

@ -23,10 +23,10 @@
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <thread/thread.h>
#include <avl/avl.h>
#include <httpp/httpp.h>
#include <net/sock.h>
#include "thread/thread.h"
#include "avl/avl.h"
#include "httpp/httpp.h"
#include "net/sock.h"
#include "connection.h"
@ -41,14 +41,10 @@
#define CATMODULE "stats"
#include "logging.h"
#ifdef _WIN32
#define vsnprintf _vsnprintf
#define snprintf _snprintf
#define atoll _atoi64
#endif
#if !defined HAVE_ATOLL && defined HAVE_STRTOLL
#define atoll(nptr) strtoll(nptr, (char **)NULL, 10)
#endif
static void stats_global_calc (void);
#define STATS_EVENT_SET 0
#define STATS_EVENT_INC 1
@ -84,7 +80,7 @@ static stats_t _stats;
static mutex_t _stats_mutex;
static event_queue_t _global_event_queue;
mutex_t _global_event_mutex;
spin_t _global_event_mutex;
static volatile event_listener_t *_event_listeners;
@ -95,8 +91,8 @@ static int _compare_source_stats(void *a, void *b, void *arg);
static int _free_stats(void *key);
static int _free_source_stats(void *key);
static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue);
static stats_node_t *_find_node(avl_tree *tree, char *name);
static stats_source_t *_find_source(avl_tree *tree, char *source);
static stats_node_t *_find_node(avl_tree *tree, const char *name);
static stats_source_t *_find_source(avl_tree *tree, const char *source);
static void _free_event(stats_event_t *event);
static stats_event_t *_get_event_from_queue (event_queue_t *queue);
@ -123,9 +119,9 @@ static stats_event_t *build_event (const char *source, const char *name, const c
static void queue_global_event (stats_event_t *event)
{
thread_mutex_lock(&_global_event_mutex);
thread_spin_lock(&_global_event_mutex);
_add_event_to_queue (event, &_global_event_queue);
thread_mutex_unlock(&_global_event_mutex);
thread_spin_unlock(&_global_event_mutex);
}
void stats_initialize(void)
@ -141,7 +137,7 @@ void stats_initialize(void)
/* set up stats queues */
event_queue_init (&_global_event_queue);
thread_mutex_create("stats_global_event", &_global_event_mutex);
thread_spin_create("stats_global_event", &_global_event_mutex);
/* fire off the stats thread */
_stats_running = 1;
@ -171,7 +167,7 @@ void stats_shutdown(void)
/* free the queues */
/* destroy the queue mutexes */
thread_mutex_destroy(&_global_event_mutex);
thread_spin_destroy(&_global_event_mutex);
thread_mutex_destroy(&_stats_mutex);
avl_tree_free(_stats.source_tree, _free_source_stats);
@ -209,12 +205,18 @@ void stats_event(const char *source, const char *name, const char *value)
{
stats_event_t *event;
if (value && xmlCheckUTF8 ((unsigned char *)value) == 0)
{
WARN2 ("seen non-UTF8 data, probably incorrect metadata (%s, %s)", name, value);
return;
}
event = build_event (source, name, value);
if (event)
queue_global_event (event);
}
/* wrapper for stats_event, this takes a charset to convert from */
void stats_event_conv(const char *mount, const char *name, const char *value, const char *charset)
{
const char *metadata = value;
@ -291,7 +293,7 @@ void stats_event_args(const char *source, char *name, char *format, ...)
stats_event(source, name, buf);
}
static char *_get_stats(char *source, char *name)
static char *_get_stats(const char *source, const char *name)
{
stats_node_t *stats = NULL;
stats_source_t *src = NULL;
@ -315,7 +317,7 @@ static char *_get_stats(char *source, char *name)
return value;
}
char *stats_get_value(char *source, char *name)
char *stats_get_value(const char *source, const char *name)
{
return(_get_stats(source, name));
}
@ -373,7 +375,7 @@ void stats_event_dec(const char *source, const char *name)
/* note: you must call this function only when you have exclusive access
** to the avl_tree
*/
static stats_node_t *_find_node(avl_tree *stats_tree, char *name)
static stats_node_t *_find_node(avl_tree *stats_tree, const char *name)
{
stats_node_t *stats;
avl_node *node;
@ -400,7 +402,7 @@ static stats_node_t *_find_node(avl_tree *stats_tree, char *name)
/* note: you must call this function only when you have exclusive access
** to the avl_tree
*/
static stats_source_t *_find_source(avl_tree *source_tree, char *source)
static stats_source_t *_find_source(avl_tree *source_tree, const char *source)
{
stats_source_t *stats;
avl_node *node;
@ -478,7 +480,7 @@ static void modify_node_event (stats_node_t *node, stats_event_t *event)
break;
}
str = malloc (20);
snprintf (str, 20, FORMAT_INT64, value);
snprintf (str, 20, "%" PRId64, value);
free (event->value);
event->value = strdup (str);
}
@ -486,9 +488,6 @@ static void modify_node_event (stats_node_t *node, stats_event_t *event)
str = (char *)strdup (event->value);
free (node->value);
node->value = str;
DEBUG3 ("update node on %s \"%s\" (%s)",
event->source ? event->source : "global",
node->name, node->value);
}
@ -509,6 +508,9 @@ static void process_global_event (stats_event_t *event)
if (node)
{
modify_node_event (node, event);
DEBUG3 ("update node on %s \"%s\" (%s)",
event->source ? event->source : "global",
node->name, node->value);
}
else
{
@ -606,15 +608,43 @@ void stats_event_time (const char *mount, const char *name)
stats_event (mount, name, buffer);
}
void stats_listener_send (stats_event_t *event)
{
event_listener_t *listener = (event_listener_t *)_event_listeners;
while (listener)
{
int send_it = 1;
if (event->source && listener->source &&
strcmp (event->source, listener->source) != 0)
send_it = 0;
if (send_it)
{
stats_event_t *copy = _copy_event(event);
thread_mutex_lock (&listener->mutex);
_add_event_to_queue (copy, &listener->queue);
thread_mutex_unlock (&listener->mutex);
}
listener = listener->next;
}
}
void stats_global (ice_config_t *config)
{
stats_event (NULL, "server_id", config->server_id);
stats_event (NULL, "host", config->hostname);
stats_event (NULL, "location", config->location);
stats_event (NULL, "admin", config->admin);
}
static void *_stats_thread(void *arg)
{
stats_event_t *event;
event_listener_t *listener;
ice_config_t *config = config_get_config ();
stats_event (NULL, "server", config->server_id);
config_release_config();
stats_event_time (NULL, "server_start");
/* global currently active stats */
@ -631,15 +661,18 @@ static void *_stats_thread(void *arg)
stats_event (NULL, "source_total_connections", "0");
stats_event (NULL, "stats_connections", "0");
stats_event (NULL, "listener_connections", "0");
stats_event (NULL, "outgoing_kbitrate", "0");
INFO0 ("stats thread started");
while (_stats_running) {
if (_global_event_queue.head != NULL) {
/* grab the next event from the queue */
thread_mutex_lock(&_global_event_mutex);
thread_spin_lock(&_global_event_mutex);
event = _get_event_from_queue (&_global_event_queue);
thread_mutex_unlock(&_global_event_mutex);
thread_spin_unlock(&_global_event_mutex);
if (event == NULL)
continue;
event->next = NULL;
thread_mutex_lock(&_stats_mutex);
@ -649,28 +682,10 @@ static void *_stats_thread(void *arg)
process_global_event (event);
else
process_source_event (event);
/* now we have an event that's been processed into the running stats */
/* this event should get copied to event listeners' queues */
listener = (event_listener_t *)_event_listeners;
while (listener)
{
int send_it = 1;
if (event->source && listener->source &&
strcmp (event->source, listener->source) != 0)
send_it = 0;
if (send_it)
{
stats_event_t *copy = _copy_event(event);
thread_mutex_lock (&listener->mutex);
_add_event_to_queue (copy, &listener->queue);
thread_mutex_unlock (&listener->mutex);
}
listener = listener->next;
}
stats_listener_send (event);
/* now we need to destroy the event */
_free_event(event);
@ -678,8 +693,8 @@ static void *_stats_thread(void *arg)
thread_mutex_unlock(&_stats_mutex);
continue;
}
thread_sleep(400000);
thread_sleep(500000);
stats_global_calc();
}
return NULL;
@ -847,9 +862,9 @@ static void _register_listener (event_listener_t *listener)
static void check_uri (event_listener_t *listener, client_t *client)
{
char *mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
const char *mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
if (strcmp (mount, "/") != 0)
listener->source = mount;
listener->source = strdup (mount);
}
@ -897,6 +912,7 @@ void *stats_connection(void *arg)
thread_mutex_unlock(&_stats_mutex);
thread_mutex_destroy (&listener.mutex);
free (listener.source);
client_destroy (client);
INFO0 ("stats client finished");
@ -965,8 +981,9 @@ void stats_transform_xslt(client_t *client, const char *uri)
{
xmlDocPtr doc;
char *xslpath = util_get_path_from_normalised_uri (uri);
const char *mount = httpp_get_query_param (client->parser, "mount");
stats_get_xml(&doc, 0);
stats_get_xml(&doc, 0, mount);
xslt_transform(doc, xslpath, client);
@ -974,7 +991,7 @@ void stats_transform_xslt(client_t *client, const char *uri)
free (xslpath);
}
void stats_get_xml(xmlDocPtr *doc, int show_hidden)
void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount)
{
stats_event_t *event;
event_queue_t queue;
@ -994,24 +1011,32 @@ void stats_get_xml(xmlDocPtr *doc, int show_hidden)
{
if (event->hidden <= show_hidden)
{
xmlChar *name, *value;
name = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->name));
value = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->value));
srcnode = node;
if (event->source) {
srcnode = _find_xml_node(event->source, &src_nodes, node);
}
xmlNewChild(srcnode, NULL, name, value);
xmlFree (value);
xmlFree (name);
do
{
xmlChar *name, *value;
if (event->source)
{
if (show_mount && strcmp (event->source, show_mount) != 0)
break;
srcnode = _find_xml_node(event->source, &src_nodes, node);
}
else
srcnode = node;
name = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->name));
value = xmlEncodeEntitiesReentrant (*doc, XMLSTR(event->value));
xmlNewChild(srcnode, NULL, name, value);
xmlFree (value);
xmlFree (name);
} while (0);
}
_free_event(event);
event = _get_event_from_queue(&queue);
}
if (show_hidden)
if (show_mount)
{
/* process each listener */
/* show each listener */
source_xml_t *src = src_nodes;
avl_tree_rlock (global.source_tree);
while (src)
@ -1033,7 +1058,6 @@ void stats_get_xml(xmlDocPtr *doc, int show_hidden)
}
}
static int _compare_stats(void *arg, void *a, void *b)
{
stats_node_t *nodea = (stats_node_t *)a;
@ -1077,3 +1101,95 @@ static void _free_event(stats_event_t *event)
if (event->value) free(event->value);
free(event);
}
/* get a list of mountpoints that are in the stats but are not marked as hidden */
void stats_get_streamlist (char *buffer, size_t remaining)
{
avl_node *node;
/* now the stats for each source */
thread_mutex_lock (&_stats_mutex);
node = avl_get_first(_stats.source_tree);
while (node)
{
int ret;
stats_source_t *source = (stats_source_t *)node->key;
if (source->hidden == 0)
{
if (remaining <= strlen (source->source)+2)
{
WARN0 ("streamlist was truncated");
break;
}
ret = snprintf (buffer, remaining, "%s\r\n", source->source);
if (ret > 0)
{
buffer += ret;
remaining -= ret;
}
}
node = avl_get_next(node);
}
thread_mutex_unlock (&_stats_mutex);
}
/* This removes any source stats from virtual mountpoints, ie mountpoints
* where no source_t exists. This function requires the global sources lock
* to be held before calling.
*/
void stats_clear_virtual_mounts (void)
{
avl_node *snode;
thread_mutex_lock (&_stats_mutex);
snode = avl_get_first(_stats.source_tree);
while (snode)
{
stats_source_t *src = (stats_source_t *)snode->key;
source_t *source = source_find_mount_raw (src->source);
if (source == NULL)
{
/* no source_t is reserved so remove them now */
snode = avl_get_next (snode);
DEBUG1 ("releasing %s stats", src->source);
avl_delete (_stats.source_tree, src, _free_source_stats);
continue;
}
snode = avl_get_next (snode);
}
thread_mutex_unlock (&_stats_mutex);
}
static void stats_global_calc (void)
{
stats_event_t event;
stats_node_t *node;
char buffer [20];
static time_t next_update = 0;
if (global.time < next_update)
return;
next_update = global.time + 1;
event.source = NULL;
event.name = "outgoing_kbitrate";
event.value = buffer;
event.action = STATS_EVENT_SET;
thread_mutex_lock (&_stats_mutex);
node = _find_node(_stats.global_tree, event.name);
if (node)
{
snprintf (buffer, sizeof(buffer), "%" PRIu64,
(int64_t)(global_getrate_avg (global.out_bitrate) * 8 / 1000.0 + 0.5));
modify_node_event (node, &event);
stats_listener_send (&event);
}
thread_mutex_unlock (&_stats_mutex);
}

View File

@ -13,6 +13,7 @@
#ifndef __STATS_H__
#define __STATS_H__
#include "cfgfile.h"
#include "connection.h"
#include "httpp/httpp.h"
#include "client.h"
@ -73,7 +74,10 @@ typedef struct _stats_tag
void stats_initialize(void);
void stats_shutdown(void);
void stats_global(ice_config_t *config);
stats_t *stats_get_stats(void);
void stats_get_streamlist (char *buffer, size_t remaining);
void stats_clear_virtual_mounts (void);
void stats_event(const char *source, const char *name, const char *value);
void stats_event_conv(const char *mount, const char *name,
@ -91,12 +95,8 @@ void stats_callback (client_t *client, void *notused);
void stats_transform_xslt(client_t *client, const char *uri);
void stats_sendxml(client_t *client);
void stats_get_xml(xmlDocPtr *doc, int show_hidden);
char *stats_get_value(char *source, char *name);
void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount);
char *stats_get_value(const char *source, const char *name);
#endif /* __STATS_H__ */

View File

@ -29,9 +29,6 @@
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#define snprintf _snprintf
#define strcasecmp stricmp
#define strncasecmp strnicmp
#endif
#include "net/sock.h"
@ -57,8 +54,10 @@ struct rate_calc_node
struct rate_calc
{
uint64_t total;
unsigned int seconds;
unsigned int blocks;
unsigned int recalc_total;
struct rate_calc_node *current;
};
@ -72,7 +71,7 @@ struct rate_calc
* 0 if no activity occurs
* < 0 for error.
*/
int util_timed_wait_for_fd(int fd, int timeout)
int util_timed_wait_for_fd(sock_t fd, int timeout)
{
#ifdef HAVE_POLL
struct pollfd ufds;
@ -154,7 +153,7 @@ char *util_get_extension(const char *path) {
return ext+1;
}
int util_check_valid_extension(char *uri) {
int util_check_valid_extension(const char *uri) {
int ret = 0;
char *p2;
@ -277,12 +276,12 @@ static char safechars[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
char *util_url_escape(void *src)
char *util_url_escape (const char *src)
{
int len = strlen(src);
/* Efficiency not a big concern here, keep the code simple/conservative */
char *dst = calloc(1, len*3 + 1);
unsigned char *source = src;
unsigned char *source = (unsigned char *)src;
int i,j=0;
for(i=0; i < len; i++) {
@ -301,10 +300,10 @@ char *util_url_escape(void *src)
return dst;
}
char *util_url_unescape(char *src)
char *util_url_unescape (const char *src)
{
int len = strlen(src);
void *decoded;
char *decoded;
int i;
char *dst;
int done = 0;
@ -354,7 +353,7 @@ char *util_url_unescape(char *src)
* escape from the webroot) or if it cannot be URI-decoded.
* Caller should free the path.
*/
char *util_normalise_uri(char *uri) {
char *util_normalise_uri(const char *uri) {
char *path;
if(uri[0] != '/')
@ -419,9 +418,8 @@ char *util_bin_to_hex(unsigned char *data, int len)
}
/* This isn't efficient, but it doesn't need to be */
char *util_base64_encode(void *ptr)
char *util_base64_encode(const char *data)
{
char *data = ptr;
int len = strlen(data);
char *out = malloc(len*4/3 + 4);
char *result = out;
@ -453,10 +451,10 @@ char *util_base64_encode(void *ptr)
return result;
}
char *util_base64_decode(void *ptr)
char *util_base64_decode(const char *data)
{
unsigned char *input = ptr;
int len = strlen(ptr);
const unsigned char *input = (const unsigned char *)data;
int len = strlen (data);
char *out = malloc(len*3/4 + 5);
char *result = out;
signed char vals[4];
@ -649,6 +647,46 @@ struct tm *localtime_r (const time_t *timep, struct tm *result)
}
#endif
/* helper function for converting a passed string in one character set to another
* we use libxml2 for this
*/
char *util_conv_string (const char *string, const char *in_charset, const char *out_charset)
{
xmlCharEncodingHandlerPtr in, out;
char *ret = NULL;
if (string == NULL || in_charset == NULL || out_charset == NULL)
return NULL;
in = xmlFindCharEncodingHandler (in_charset);
out = xmlFindCharEncodingHandler (out_charset);
if (in && out)
{
xmlBufferPtr orig = xmlBufferCreate ();
xmlBufferPtr utf8 = xmlBufferCreate ();
xmlBufferPtr conv = xmlBufferCreate ();
INFO2 ("converting metadata from %s to %s", in_charset, out_charset);
xmlBufferCCat (orig, string);
if (xmlCharEncInFunc (in, utf8, orig) > 0)
{
xmlCharEncOutFunc (out, conv, NULL);
if (xmlCharEncOutFunc (out, conv, utf8) >= 0)
ret = strdup ((const char *)xmlBufferContent (conv));
}
xmlBufferFree (orig);
xmlBufferFree (utf8);
xmlBufferFree (conv);
}
xmlCharEncCloseFunc (in);
xmlCharEncCloseFunc (out);
return ret;
}
/* setup a rate block of so many seconds, so that an average can be
* determined of that range
*/
@ -682,6 +720,18 @@ struct rate_calc *rate_setup (unsigned int seconds)
return calc;
}
/* */
static void rate_recalc_total (struct rate_calc *calc)
{
int i;
struct rate_calc_node *p = calc->current->prev;
calc->total = 0;
for (i=calc->blocks-1; i; i--)
{
calc->total += p->value;
p = p->prev;
}
}
/* add a value to sampled data, t is used to determine which sample
* block the sample goes into.
@ -695,11 +745,25 @@ void rate_add (struct rate_calc *calc, long value, time_t t)
}
if (t != calc->current->time)
{
calc->current = calc->current->next;
if (calc->recalc_total)
{
/* here we keep the number of blocks and recalculate the total */
calc->current = calc->current->next;
rate_recalc_total (calc);
calc->recalc_total--;
}
else
{
/* common case */
calc->total += calc->current->value;
calc->current = calc->current->next;
if (calc->blocks == calc->seconds)
calc->total -= calc->current->value;
else
calc->blocks++;
}
calc->current->value = 0;
calc->current->time = t;
if (calc->blocks < calc->seconds)
calc->blocks++;
}
calc->current->value += value;
}
@ -710,19 +774,19 @@ void rate_add (struct rate_calc *calc, long value, time_t t)
*/
long rate_avg (struct rate_calc *calc)
{
struct rate_calc_node *node = calc->current->next;
int i = calc->blocks;
long total = 0;
if (i < 2)
if (calc->blocks < 2)
return 0;
i--;
for (; i; i--)
return (long)(calc->total / (calc->blocks-1));
}
/* reduce the samples used to calculate average */
void rate_reduce (struct rate_calc *calc, unsigned long count)
{
if (calc && count < calc->blocks && count >= 2)
{
total += node->value;
node = node->prev;
calc->recalc_total = count*2;
calc->blocks = count;
}
return total / (calc->blocks - 1);
}
@ -745,3 +809,17 @@ void rate_free (struct rate_calc *calc)
free (calc);
}
int get_line(FILE *file, char *buf, size_t siz)
{
if(fgets(buf, (int)siz, file)) {
size_t len = strlen(buf);
if(len > 0 && buf[len-1] == '\n') {
buf[--len] = 0;
if(len > 0 && buf[len-1] == '\r')
buf[--len] = 0;
}
return 1;
}
return 0;
}

View File

@ -21,19 +21,22 @@
#define READ_ENTIRE_HEADER 1
#define READ_LINE 0
int util_timed_wait_for_fd(int fd, int timeout);
#define MAX_LINE_LEN 512
int util_timed_wait_for_fd(sock_t fd, int timeout);
int util_read_header(int sock, char *buff, unsigned long len, int entire);
int util_check_valid_extension(char *uri);
int util_check_valid_extension(const char *uri);
char *util_get_extension(const char *path);
char *util_get_path_from_uri(char *uri);
char *util_get_path_from_normalised_uri(const char *uri);
char *util_normalise_uri(char *uri);
char *util_base64_encode(void *data);
char *util_base64_decode(void *input);
char *util_normalise_uri(const char *uri);
char *util_base64_encode(const char *data);
char *util_base64_decode(const char *input);
char *util_bin_to_hex(unsigned char *data, int len);
char *util_url_unescape(char *src);
char *util_url_escape(void *src);
char *util_url_unescape(const char *src);
char *util_url_escape(const char *src);
/* String dictionary type, without support for NULL keys, or multiple
* instances of the same key */
@ -53,10 +56,14 @@ char *util_dict_urlencode(util_dict *dict, char delim);
#ifndef HAVE_LOCALTIME_R
struct tm *localtime_r (const time_t *timep, struct tm *result);
#endif
char *util_conv_string (const char *string, const char *in_charset, const char *out_charset);
struct rate_calc *rate_setup (unsigned int seconds);
void rate_add (struct rate_calc *calc, long value, time_t t);
long rate_avg (struct rate_calc *calc);
void rate_free (struct rate_calc *calc);
void rate_reduce (struct rate_calc *calc, unsigned long count);
int get_line(FILE *file, char *buf, size_t siz);
#endif /* __UTIL_H__ */

View File

@ -34,10 +34,6 @@
#include <sys/time.h>
#endif
#ifdef WIN32
#define snprintf _snprintf
#endif
#include "thread/thread.h"
#include "avl/avl.h"
#include "httpp/httpp.h"
@ -226,17 +222,17 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client)
if (problem == 0)
{
/* the 100 is to allow for the hardcoded headers */
unsigned int header_len = strlen (mediatype) + len + 100;
refbuf_t *refbuf = refbuf_new (header_len);
unsigned int full_len = strlen (mediatype) + len + 100;
refbuf_t *refbuf = refbuf_new (full_len);
if (string == NULL)
string = xmlCharStrdup ("");
len = snprintf (refbuf->data, header_len,
snprintf (refbuf->data, full_len,
"HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
mediatype, len, string);
client_set_queue (client, NULL);
client->respcode = 200;
client_set_queue (client, NULL);
client->refbuf = refbuf;
refbuf->len = strlen (refbuf->data);
fserve_add_client (client, NULL);

114
src/yp.c
View File

@ -20,7 +20,7 @@
#include <stdlib.h>
#include <curl/curl.h>
#include <thread/thread.h>
#include "thread/thread.h"
#include "connection.h"
#include "refbuf.h"
@ -31,15 +31,12 @@
#include "cfgfile.h"
#include "stats.h"
#ifdef WIN32
#define snprintf _snprintf
#endif
#define CATMODULE "yp"
struct yp_server
{
char *url;
char *server_id;
unsigned url_timeout;
unsigned touch_interval;
int remove;
@ -87,9 +84,8 @@ static mutex_t yp_pending_lock;
static volatile struct yp_server *active_yps = NULL, *pending_yps = NULL;
static volatile int yp_update = 0;
static int yp_running;
static time_t now;
static thread_type *yp_thread;
static volatile thread_type *yp_thread;
static volatile unsigned client_limit = 0;
static volatile char *server_version = NULL;
@ -184,6 +180,7 @@ static void destroy_yp_server (struct yp_server *server)
if (server->mounts) WARN0 ("active ypdata not freed up");
if (server->pending_mounts) WARN0 ("pending ypdata not freed up");
free (server->url);
free (server->server_id);
free (server);
}
@ -234,6 +231,7 @@ void yp_recheck_config (ice_config_t *config)
destroy_yp_server (server);
break;
}
server->server_id = strdup ((char *)server_version);
server->url = strdup (config->yp_url[i]);
server->url_timeout = config->yp_url_timeout[i];
server->touch_interval = config->yp_touch_interval[i];
@ -245,7 +243,7 @@ void yp_recheck_config (ice_config_t *config)
}
if (server->touch_interval < 30)
server->touch_interval = 30;
curl_easy_setopt (server->curl, CURLOPT_USERAGENT, server_version);
curl_easy_setopt (server->curl, CURLOPT_USERAGENT, server->server_id);
curl_easy_setopt (server->curl, CURLOPT_URL, server->url);
curl_easy_setopt (server->curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
curl_easy_setopt (server->curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
@ -275,8 +273,6 @@ void yp_initialize(void)
thread_mutex_create ("yp", &yp_pending_lock);
yp_recheck_config (config);
config_release_config ();
yp_thread = thread_create("YP Touch Thread", yp_update_thread,
(void *)NULL, THREAD_ATTACHED);
}
@ -429,13 +425,11 @@ static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len)
free (val);
}
val = stats_get_value (yp->mount, "max_listeners");
if (val == NULL || strcmp (val, "unlimited") == 0)
{
free (val);
if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
max_listeners = client_limit;
}
else
max_listeners = atoi (val);
free (val);
val = stats_get_value (yp->mount, "subtype");
if (val)
@ -669,52 +663,39 @@ static void delete_marked_yp (struct yp_server *server)
static void *yp_update_thread(void *arg)
{
INFO0("YP update thread started");
struct yp_server *server;
yp_running = 1;
while (yp_running)
/* DEBUG0("YP thread started"); */
/* do the YP communication */
thread_rwlock_rlock (&yp_lock);
server = (struct yp_server *)active_yps;
while (server)
{
struct yp_server *server;
/* DEBUG1 ("trying %s", server->url); */
yp_process_server (server);
server = server->next;
}
thread_rwlock_unlock (&yp_lock);
thread_sleep (200000);
/* do the YP communication */
thread_rwlock_rlock (&yp_lock);
/* update the local YP structure */
if (yp_update)
{
thread_rwlock_wlock (&yp_lock);
check_servers ();
server = (struct yp_server *)active_yps;
while (server)
{
/* DEBUG1 ("trying %s", server->url); */
yp_process_server (server);
/* DEBUG1 ("Checking yps %s", server->url); */
add_pending_yp (server);
delete_marked_yp (server);
server = server->next;
}
yp_update = 0;
thread_rwlock_unlock (&yp_lock);
/* update the local YP structure */
if (yp_update)
{
thread_rwlock_wlock (&yp_lock);
check_servers ();
server = (struct yp_server *)active_yps;
while (server)
{
/* DEBUG1 ("Checking yps %s", server->url); */
add_pending_yp (server);
delete_marked_yp (server);
server = server->next;
}
yp_update = 0;
thread_rwlock_unlock (&yp_lock);
}
}
thread_rwlock_destroy (&yp_lock);
thread_mutex_destroy (&yp_pending_lock);
/* free server and ypdata left */
while (active_yps)
{
struct yp_server *server = (struct yp_server *)active_yps;
active_yps = server->next;
destroy_yp_server (server);
}
yp_thread = NULL;
/* DEBUG0("YP thread shutdown"); */
return NULL;
}
@ -927,12 +908,35 @@ void yp_touch (const char *mount)
void yp_shutdown (void)
{
yp_running = 0;
int loop=25;
yp_update = 1;
if (yp_thread)
thread_join (yp_thread);
free ((char*)server_version);
server_version = NULL;
while (yp_thread && loop)
{
thread_sleep (200000);
loop--;
}
if (yp_thread == NULL)
{
thread_rwlock_destroy (&yp_lock);
thread_mutex_destroy (&yp_pending_lock);
/* free server and ypdata left */
while (active_yps)
{
struct yp_server *server = (struct yp_server *)active_yps;
active_yps = server->next;
destroy_yp_server (server);
}
free ((char*)server_version);
server_version = NULL;
}
INFO0 ("YP thread down");
}
void yp_thread_startup (void)
{
if (yp_thread == NULL)
yp_thread = thread_create ("YP Thread", yp_update_thread, NULL, THREAD_DETACHED);
}

View File

@ -34,6 +34,7 @@ void yp_touch (const char *mount);
void yp_recheck_config (ice_config_t *config);
void yp_initialize(void);
void yp_shutdown(void);
void yp_thread_startup (void);
#else
@ -43,6 +44,7 @@ void yp_shutdown(void);
#define yp_recheck_config(x) do{}while(0)
#define yp_initialize() WARN0("YP server handling has been disabled")
#define yp_shutdown() do{}while(0)
#define yp_thread_startup() do{}while(0)
#endif /* USE_YP */

View File

@ -9,8 +9,8 @@ dist_web_DATA = status.xsl \
style.css \
auth.xsl \
server_version.xsl \
server_uptime.xsl \
uptime_xml.xsl \
navbar.html \
adminbar.html \
statusbar.html \
admin.html \
index.html \
favicon.ico

View File

@ -1,10 +1,14 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="/style.css" />
<title>Icecast Streaming Media Server</title>
</head>
<body>
<!--index header menu -->
<div class="nav">
<h1>Icecast2 Admin</h1>
<center>
<table class="roundcont">
<tr>
@ -13,13 +17,12 @@
<table>
<tr>
<td>
<a target="_parent" href="/admin/stats.xsl">Admin Home</a>
<a target="_parent" href="/admin/listmounts.xsl">List Mountpoints</a>
<a target="_parent" href="/admin/moveclients.xsl">Move Listeners</a>
<a target="_parent" href="/admin/managerelays.xsl">Manage Relays</a>
<a target="_parent" href="/admin/function.xsl?perform=updatecfg">Reload Config</a>
<a target="_parent" href="/admin/logs.xsl">Logs</a>
<a target="_parent" href="/status.xsl">Index</a>
<a target="content" href="/admin/stats.xsl">Admin Home</a>
<a target="content" href="/admin/listmounts.xsl">List Mountpoints</a>
<a target="content" href="/admin/managerelays.xsl">Manage Relays</a>
<a target="content" href="/admin/function.xsl?perform=updatecfg">Reload Config</a>
<a target="content" href="/admin/logs.xsl">Logs</a>
<a target="_parent" href="/index.html">Index</a>
</td>
</tr>
</table>

View File

@ -13,8 +13,6 @@
<body>
<div class="main">
<h1>Authorization Page</h1>
<iframe frameborder="0" scrolling="no" height="50" src="/navbar.html" />
<table border="0" width="100%">
<tr>

12
web/index.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="/style.css" />
<title>Icecast Streaming Media Server</title>
</head>
<frameset rows="170,*" border="0">
<frame name="header" frameborder="0" scrolling="no" width="100%" src="/statusbar.html" />
<frame name="content" frameborder="0" scrolling="auto" width="100%" src="/status.xsl" />
</frameset>
</html>

View File

@ -1,46 +0,0 @@
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
<xsl:template match = "/icestats" >
<html>
<head>
<title>Icecast Streaming Media Server</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div class="main">
<h1>Icecast2 Status (Server Uptime)</h1>
<iframe frameborder="0" height="50" scrolling="no" src="/navbar.html" />
<div class="roundcont">
<div class="roundtop">
<img src="/images/corner_topleft.jpg" class="corner" style="display: none" />
</div>
<div class="newscontent">
<h3>Server Stats</h3>
<table border="0" cellpadding="4">
<xsl:for-each select="/icestats">
<xsl:for-each select="server_start">
<xsl:if test = "name()!='source'">
<tr>
<td width="130">Server Uptime</td>
<td class="streamdata"><xsl:value-of select="." /></td>
</tr>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</table>
</div>
<div class="roundbottom">
<img src="/images/corner_bottomleft.jpg" class="corner" style="display: none" />
</div>
</div>
<br />
<br />
<div class="poster">Support icecast development at <a class="nav" target="_blank" href="http://www.icecast.org">www.icecast.org</a></div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

View File

@ -12,8 +12,6 @@
<body>
<div class="main">
<h1>Icecast2 Status (Version Info)</h1>
<iframe frameborder="0" scrolling="no" height="50" src="/navbar.html" />
<div class="roundcont">
<div class="roundtop">
@ -22,15 +20,28 @@
<div class="newscontent">
<h3>Information</h3>
<table border="0" cellpadding="4">
<xsl:for-each select="/icestats">
<xsl:for-each select="server">
<xsl:if test = "name()!='source'">
<tr>
<td width="130">Build</td>
<td class="streamdata"><xsl:value-of select="." /></td>
<td width="130">Location</td>
<td class="streamdata"><xsl:value-of select="location" /></td>
</tr>
<tr>
<td width="130">Admin</td>
<td class="streamdata"><xsl:value-of select="admin" /></td>
</tr>
<tr>
<td width="130">Host</td>
<td class="streamdata"><xsl:value-of select="host" /></td>
</tr>
<tr>
<td width="130">Version</td>
<td class="streamdata"><xsl:value-of select="server_id" /></td>
</tr>
<tr>
<td width="130">Started</td>
<td class="streamdata"><xsl:value-of select="server_start" /></td>
</tr>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
<tr>
<td width="130">Download</td>

View File

@ -13,8 +13,6 @@
<body>
<div class="main">
<h1>Icecast2 Status</h1>
<iframe frameborder="0" height="50" scrolling="no" src="/navbar.html" />
<!--mount point stats-->
<xsl:for-each select="source">

View File

@ -2,30 +2,32 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="/style.css" />
<title>Icecast Streaming Media Server</title>
</head>
<body>
<!--index header menu -->
<div class="nav">
<h1>Icecast2</h1>
<center>
<table class="roundcont">
<tr>
<td class="topleft"></td>
<td rowspan="2" class="nav">
<table>
<tr>
<td>
<a href="/admin/stats.xsl" target="_parent">Admin</a>
<a href="/status.xsl" target="_parent">Server Status</a>
<a href="/server_uptime.xsl" target="_parent">Server Uptime</a>
<a href="/server_version.xsl" target="_parent">Server Info</a>
</td>
<tr>
<td>
<a target="_parent" href="/admin.html">Admin</a>
<a target="content" href="/status.xsl">Status</a>
<a target="content" href="/server_version.xsl">Info</a>
<a target="content" href="http://dir.xiph.org/">Stream Directory</a>
</td>
</tr>
</table>
</td>
<td class="topright"></td>
</tr>
<tr>
<td class="bottomleft"></td>
<td class="bottomleft"></td>
<td class="bottomright"></td>
</tr>
</table>

View File

@ -26,19 +26,25 @@ html, body {
color: #000;
background: #aaa;
}
h1 {
.nav h1 {
font-family: Verdana, sans-serif;
text-decoration: none;
font-weight: bold;
font-size: 100%;
font-size: 300%;
color: #fff;
margin-top:3px;
margin-top: 0px;
margin-bottom: 10px;
padding-top: 10px;
padding-bottom: 0px;
padding-left: 90px;
height: 70px;
background: url(/images/icecast.png) no-repeat left center;
}
.nav {
font-family: Verdana, sans-serif;
text-decoration: none;
font-weight: bold;
font-size: 110%;
font-size: 90%;
}
.nav a {
color: white;

View File

@ -1,15 +0,0 @@
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
<xsl:output method="xml" media-type="text/html" indent="yes" encoding="UTF-8"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" />
<xsl:template match = "/icestats" >
<xsl:for-each select="/icestats">
<xsl:for-each select="server_start">
<xsl:if test = "name()!='source'">
<UPTIME><xsl:value-of select="." /></UPTIME>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

View File

@ -43,7 +43,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../src" /I "../src/httpp" /I "../src/thread" /I "../src/log" /I "../src/avl" /I "../src/net" /I "src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../src" /I "../src/httpp" /I "../src/thread" /I "../src/log" /I "../src/avl" /I "../src/net" /I "src/timings" /I "../" /I ".." /I "..\src" /D "NDEBUG" /D "_WINDOWS" /D HAVE_CONFIG_H=1 /D "WIN32" /D "_MBCS" /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
@ -53,7 +53,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386
# ADD LINK32 Releaseicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib winmm.lib ../../theora/win32/Static_Release/theora_static.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib" /out:"Release/Icecast2.exe"
# ADD LINK32 Releaseicecast\icecast.lib libcurl.lib ogg_static_d.lib vorbis_static.lib theora_static_d.lib libspeex.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib winmm.lib ssleay32MT.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /out:"Release/Icecast2.exe"
# SUBTRACT LINK32 /pdb:none
!ELSEIF "$(CFG)" == "Icecast2win - Win32 Debug"
@ -70,7 +70,7 @@ LINK32=link.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../src" /I "../src/httpp" /I "../src/thread" /I "../src/log" /I "../src/avl" /I "../src/net" /I "src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_WIN32" /D "_AFXDLL" /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../src" /I "../src/httpp" /I "../src/thread" /I "../src/log" /I "../src/avl" /I "../src/net" /I "src/timings" /I "../" /I ".." /I "..\src" /D "_DEBUG" /D "_WIN32" /D "_AFXDLL" /D "_WINDOWS" /D HAVE_CONFIG_H=1 /D "WIN32" /D "_MBCS" /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
@ -80,7 +80,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 Debugicecast\icecast.lib ..\..\curl\lib\Debug\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static_d.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static_d.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib winmm.lib ../../theora/win32/Static_Debug/theora_static_d.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /nodefaultlib:"libcd.lib" /pdbtype:sept
# ADD LINK32 Debugicecast\icecast.lib libcurl.lib ogg_static_d.lib vorbis_static.lib theora_static_d.lib libspeex.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib winmm.lib ssleay32MT.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmt.lib" /nodefaultlib:"libcmtd.lib" /pdbtype:sept
# SUBTRACT LINK32 /pdb:none
!ENDIF

View File

@ -1,5 +1,5 @@
// Icecast2winDlg.cpp : implementation file
//
//
#include "stdafx.h"
#include "Icecast2win.h"
@ -472,6 +472,8 @@ void AddUpdateStatistic(int sourceIndex, char *name, char *value)
}
}
int numStats = gStats[sourceIndex].numStats;
if (numStats >= MAXSTATSPERSOURCE)
return;
/* If we get here, we haven't found the stat, so add it */
gStats[sourceIndex].stats[numStats].name = name;
gStats[sourceIndex].stats[numStats].value = value;
@ -488,6 +490,8 @@ int GetSourceIndex(char *sourceName)
return i;
}
}
if (numMainStats >= MAXSOURCES)
return 0;
/* This means we haven't seen the source, so lets add it */
numMainStats++;
gStats[numMainStats].source = sourceName;
@ -537,7 +541,7 @@ void StartStats(void *dummy)
xmlDocPtr doc;
stats_get_xml(&doc, 0);
stats_get_xml(&doc, 0, NULL);
xmlNodePtr cur;
cur = xmlDocGetRootElement(doc);

View File

@ -12,5 +12,5 @@ EXTRA_DIST = ConfigTab.cpp ConfigTab.h Icecast2win.clw Icecast2win.cpp \
colors.h icecast.dsp icecast.ico icecast2.iss icecast2logo2.bmp\
resource.h running.bmp stopped.bmp TRAYNOT.h Traynot.cpp \
icecast2_console.dsw icecast2_console.dsp credits.bmp icecast2title.bmp \
icecastService.cpp icecastService.dsp
icecastService.cpp icecastService.dsp config.h.vc6 fnmatch.h

View File

@ -41,7 +41,7 @@ RSC=rc.exe
# PROP Intermediate_Dir "Releaseicecast"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../curl/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /I "../../speex/include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_LOCALTIME_R" /D "HAVE_OLD_VSNPRINTF" /D "HAVE_THEORA" /D "HAVE_SPEEX" /D "HAVE_AUTH_URL" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /I "..\src" /D "NDEBUG" /D "_LIB" /D HAVE_CONFIG_H=1 /D "WIN32" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
@ -64,7 +64,7 @@ LIB32=link.exe -lib
# PROP Intermediate_Dir "Debugicecast"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../curl/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../" /I "../../libxslt/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /I "../../speex/include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "_WIN32" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_LOCALTIME_R" /D "HAVE_OLD_VSNPRINTF" /D "HAVE_THEORA" /D "HAVE_SPEEX" /D "HAVE_AUTH_URL" /FD /D /GZ /c
# ADD CPP /nologo /MT /W3 /Gm /GX /ZI /Od /I ".." /I "..\src" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /D HAVE_CONFIG_H=1 /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
@ -126,6 +126,15 @@ SOURCE=..\src\avl\avl.h
# Begin Source File
SOURCE=..\src\cfgfile.c
!IF "$(CFG)" == "icecast - Win32 Release"
# ADD CPP /I "."
!ELSEIF "$(CFG)" == "icecast - Win32 Debug"
!ENDIF
# End Source File
# Begin Source File
@ -161,6 +170,19 @@ SOURCE=..\src\event.h
# End Source File
# Begin Source File
SOURCE=..\src\fnmatch.c
!IF "$(CFG)" == "icecast - Win32 Release"
# ADD CPP /I "."
!ELSEIF "$(CFG)" == "icecast - Win32 Debug"
!ENDIF
# End Source File
# Begin Source File
SOURCE=..\src\format.c
# End Source File
# Begin Source File
@ -369,6 +391,14 @@ SOURCE=..\src\yp.h
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=\config.h
# End Source File
# Begin Source File
SOURCE=.\fnmatch.h
# End Source File
# Begin Source File
SOURCE=..\src\timing\timing.h
# End Source File
# Begin Source File

View File

@ -2,18 +2,18 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
[Setup]
AppName=Icecast2 Win32
AppVerName=Icecast v2.3.1
AppName=Icecast2-KH
AppVerName=Icecast v2.3.1-kh27
AppPublisherURL=http://www.icecast.org
AppSupportURL=http://www.icecast.org
AppUpdatesURL=http://www.icecast.org
DefaultDirName={pf}\Icecast2 Win32
DefaultGroupName=Icecast2 Win32
DefaultDirName={pf}\Icecast2 Win32 KH
DefaultGroupName=Icecast2 Win32 KH
AllowNoIcons=yes
LicenseFile=..\COPYING
InfoAfterFile=..\README
OutputDir=.
OutputBaseFilename=icecast2_win32_v2.3.1_setup
OutputBaseFilename=icecast2_win32_v2.3.1-kh27_setup
WizardImageFile=icecast2logo2.bmp
WizardImageStretch=no
; uncomment the following line if you want your installation to run on NT 3.51 too.
@ -35,18 +35,22 @@ Source: "Release\Icecast2.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "Release\icecast2console.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "Release\icecastService.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\examples\*"; DestDir: "{app}\examples"; Flags: ignoreversion
Source: "..\doc\icecast2.chm"; DestDir: "{app}\doc"; Flags: ignoreversion
Source: "..\doc\*"; DestDir: "{app}\doc"; Flags: ignoreversion
Source: "..\web\*.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
Source: "..\web\*.png"; DestDir: "{app}\web"; Flags: ignoreversion
Source: "..\web\*.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
Source: "..\web\*.html"; DestDir: "{app}\web"; Flags: ignoreversion
Source: "..\web\images\*.png"; DestDir: "{app}\web\images"; Flags: ignoreversion
Source: "..\web\images\*.jpg"; DestDir: "{app}\web\images"; Flags: ignoreversion
Source: "..\web\*.css"; DestDir: "{app}\web"; Flags: ignoreversion
Source: "..\admin\*.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
Source: "..\..\pthreads\pthreadVSE.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\conf\*.xml"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\..\iconv\lib\iconv.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\..\libxslt\lib\libxslt.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\..\libxml2\lib\libxml2.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\..\curl\lib\Release\libcurl.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "c:\xiph\lib\pthreadVSE.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\conf\*.dist"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\examples\icecast_shoutcast_compat.xml"; DestDir: "{app}"; DestName: "icecast.xml"; Flags: ignoreversion
Source: "c:\xiph\lib\iconv.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "c:\xiph\lib\libxslt.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "c:\xiph\lib\libxml2.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "c:\xiph\lib\libcurl.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "c:\xiph\lib\libeay32.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "c:\xiph\lib\ssleay32.dll"; DestDir: "{app}"; Flags: ignoreversion
[Icons]

View File

@ -42,7 +42,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_THEORA" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "../" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I ".." /D "NDEBUG" /D "_CONSOLE" /D HAVE_CONFIG_H=1 /D "WIN32" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
@ -50,7 +50,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Releaseicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib ..\..\theora\win32\Static_Release\theora_static.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libc.lib" /out:"Release/icecast2console.exe"
# ADD LINK32 Releaseicecast\icecast.lib theora_static_d.lib libspeex.lib ogg_static_d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libcurl.lib vorbis_static.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib ssleay32MT.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"Release/icecast2console.exe"
!ELSEIF "$(CFG)" == "icecast2 console - Win32 Debug"
@ -66,7 +66,7 @@ LINK32=link.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../" /I "../../libxslt/include" /I "../../curl/include" /I "../../iconv/include" /I "../../libxml2/include" /I "..\src" /I "..\src/httpp" /I "..\src/thread" /I "..\src/log" /I "..\src/avl" /I "..\src/net" /I "..\src/timings" /I "../../pthreads" /I "../../oggvorbis-win32sdk-1.0.1/include" /I "../../theora/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_CURL" /D "USE_YP" /D "HAVE_SYS_STAT_H" /D PACKAGE_VERSION=\"2.3.1\" /D "HAVE_THEORA" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /I "..\src" /D "_DEBUG" /D "_CONSOLE" /D HAVE_CONFIG_H=1 /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
@ -74,7 +74,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debugicecast\icecast.lib ..\..\curl\lib\Release\libcurl.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\ogg_static.lib ..\..\oggvorbis-win32sdk-1.0.1\lib\vorbis_static.lib ..\..\libxml2\lib\libxml2.lib ..\..\libxslt\lib\libxslt.lib ..\..\iconv\lib\iconv.lib ..\..\pthreads\pthreadVSE.lib ws2_32.lib ..\..\theora\win32\Static_Debug\theora_static_d.lib ../../speex/win32/libspeex/Release/libspeex.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/icecast2console.exe" /pdbtype:sept
# ADD LINK32 Debugicecast\icecast.lib theora_static_d.lib libspeex.lib ogg_static_d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libcurl.lib vorbis_static.lib libxml2.lib libxslt.lib iconv.lib pthreadVSE.lib ws2_32.lib ssleay32MT.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libcmt.lib" /nodefaultlib:"libcmtd.lib" /out:"Debug/icecast2console.exe" /pdbtype:sept
!ENDIF