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:
parent
5e08a0f6d5
commit
8d78b6e75f
@ -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
232
NEWS
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}&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}&id={@id}">Kick</a></td>
|
||||
</tr>
|
||||
</xsl:for-each>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<br />
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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 />
|
||||
|
||||
|
@ -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
73
admin/xspf.xsl
Normal 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>
|
@ -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,
|
||||
|
@ -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>
|
||||
|
46
configure.in
46
configure.in
@ -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
40
debian/README.Debian
vendored
@ -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
214
debian/changelog
vendored
@ -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
24
debian/control
vendored
@ -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
104
debian/copyright
vendored
@ -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/'.
|
||||
|
2
debian/icecast2.default
vendored
2
debian/icecast2.default
vendored
@ -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.
|
||||
|
8
debian/icecast2.init
vendored
8
debian/icecast2.init
vendored
@ -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."
|
||||
;;
|
||||
*)
|
||||
|
26
debian/icecast2.postinst
vendored
26
debian/icecast2.postinst
vendored
@ -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#
|
||||
|
||||
|
7
debian/icecast2.postrm
vendored
7
debian/icecast2.postrm
vendored
@ -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
41
debian/rules
vendored
@ -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
4
debian/watch
vendored
@ -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
|
||||
|
@ -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@
|
||||
|
812
src/admin.c
812
src/admin.c
File diff suppressed because it is too large
Load Diff
13
src/admin.h
13
src/admin.h
@ -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__ */
|
||||
|
616
src/auth.c
616
src/auth.c
@ -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");
|
||||
}
|
||||
|
||||
|
40
src/auth.h
40
src/auth.h
@ -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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
309
src/auth_url.c
309
src/auth_url.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
479
src/cfgfile.c
479
src/cfgfile.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
62
src/client.c
62
src/client.c
@ -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;
|
||||
|
16
src/client.h
16
src/client.h
@ -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);
|
||||
|
41
src/compat.h
41
src/compat.h
@ -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__ */
|
||||
|
||||
|
681
src/connection.c
681
src/connection.c
File diff suppressed because it is too large
Load Diff
@ -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__ */
|
||||
|
17
src/event.c
17
src/event.c
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
371
src/fnmatch.c
Normal 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
65
src/fnmatch.h
Normal 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
1210
src/fnmatch_loop.c
Normal file
File diff suppressed because it is too large
Load Diff
24
src/format.c
24
src/format.c
@ -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;
|
||||
|
@ -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);
|
||||
|
188
src/format_mp3.c
188
src/format_mp3.c
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
346
src/fserve.c
346
src/fserve.c
@ -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
|
||||
|
@ -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
|
||||
|
33
src/global.c
33
src/global.c
@ -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;
|
||||
}
|
||||
|
||||
|
16
src/global.h
16
src/global.h
@ -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__ */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
71
src/main.c
71
src/main.c
@ -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();
|
||||
|
@ -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++;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
494
src/slave.c
494
src/slave.c
@ -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 = ©->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();
|
||||
|
18
src/slave.h
18
src/slave.h
@ -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);
|
||||
|
431
src/source.c
431
src/source.c
@ -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");
|
||||
|
20
src/source.h
20
src/source.h
@ -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;
|
||||
|
||||
|
254
src/stats.c
254
src/stats.c
@ -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);
|
||||
}
|
||||
|
||||
|
12
src/stats.h
12
src/stats.h
@ -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__ */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
134
src/util.c
134
src/util.c
@ -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;
|
||||
}
|
||||
|
||||
|
21
src/util.h
21
src/util.h
@ -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__ */
|
||||
|
12
src/xslt.c
12
src/xslt.c
@ -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
114
src/yp.c
@ -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);
|
||||
}
|
||||
|
||||
|
2
src/yp.h
2
src/yp.h
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
12
web/index.html
Normal 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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
@ -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;
|
||||
|
@ -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>
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user