diff --git a/config.h.vc6 b/config.h.vc6
index b9a0aaac..6f2a0821 100644
--- a/config.h.vc6
+++ b/config.h.vc6
@@ -13,7 +13,7 @@
/* Define to 1 if you have the header file. */
#define HAVE_CURL_CURL_H 1
-/* Define t o1 if you have the 'curl_global_init' function */
+/* Define to 1 if you have the 'curl_global_init' function */
#define HAVE_CURL_GLOBAL_INIT 1
/* Define to 1 if you have the `inet_aton' function. */
@@ -95,7 +95,7 @@
#define PACKAGE_NAME "Icecast"
/* Version number of package */
-#define VERSION "2.3.2-kh1"
+#define VERSION "2.3.2-kh2"
/* Define to the version of this package. */
#define PACKAGE_VERSION VERSION
@@ -162,3 +162,4 @@ typedef unsigned int socklen_t;
/* time format for strftime */
#define ICECAST_TIME_FMT "%a, %d %b %Y %H:%M:%S"
+#define PATH_MAX MAX_PATH
diff --git a/configure.in b/configure.in
index 49d341eb..48753e99 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-AC_INIT([Icecast], [2.3.2-kh1], [karl@xiph.org])
+AC_INIT([Icecast], [2.3.2-kh2], [karl@xiph.org])
AC_PREREQ(2.59)
AC_CONFIG_SRCDIR(src/main.c)
@@ -155,7 +155,7 @@ AC_SUBST(ICECAST_OPTIONAL)
AC_SUBST(HAVE_KATE)
AC_SUBST(KATE_LIBS)
-AC_OUTPUT([Makefile conf/Makefile debian/Makefile src/Makefile src/avl/Makefile
+AC_OUTPUT([Makefile conf/Makefile src/Makefile src/avl/Makefile
src/httpp/Makefile src/thread/Makefile src/log/Makefile
src/net/Makefile src/timing/Makefile doc/Makefile web/Makefile web/images/Makefile
admin/Makefile win32/Makefile win32/res/Makefile])
diff --git a/debian/Makefile.am b/debian/Makefile.am
deleted file mode 100644
index da81405e..00000000
--- a/debian/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-AUTOMAKE_OPTIONS = 1.6 foreign
-
-EXTRA_DIST = README.Debian changelog compat control copyright \
- icecast2.1 icecast2.default icecast2.init icecast2.manpages \
- icecast2.postinst icecast2.postrm icecast2.preinst rules watch
-
diff --git a/debian/README.Debian b/debian/README.Debian
deleted file mode 100644
index a58a065e..00000000
--- a/debian/README.Debian
+++ /dev/null
@@ -1,48 +0,0 @@
-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 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 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 '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.
-
-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
- for the tip.
-
- -- Keegan Quinn
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 6456ea65..00000000
--- a/debian/changelog
+++ /dev/null
@@ -1,410 +0,0 @@
-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 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 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 ).
- * 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 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
- ).
- * 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 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 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 ).
- * Set urgency=high to still push earlier security fix.
-
- -- Jonas Smedegaard 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
- ).
- + 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 ).
-
- -- Jonas Smedegaard 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
- ).
- * Fix logrotate script. Closes: Bug#249404, #255430 (thanks
- to Julien Cristau and Mykola A. Nickishov
- ).
- * 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 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 Thu, 20 May 2004 23:03:22 +0200
-
-icecast2 (2.0.1.debian-1) unstable; urgency=medium
-
- * New upstream release (thanks to Ian Kumlien ):
- + 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 ).
- * 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 ).
- * 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 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
- .
- * Add TODO.Debian with reminder to clean this mess later.
-
- -- Jonas Smedegaard Sun, 28 Mar 2004 16:02:27 +0200
-
-icecast2 (2.0.0-2) unstable; urgency=low
-
- * Add group if non-existing.
-
- -- Jonas Smedegaard 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
- .
- * 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
- and Robin Lee Powell
- .
- * Make sure /etc/icecast2 is owned by icecast2 and not world readable.
- Closes: bug#210860, thanks to Frank Barknecht .
- * 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 .
- * 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 .
- * 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 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 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 Sun, 17 Aug 2003 10:25:16 +0200
-
-icecast2 (1.9+2.0alphasnap2+20030802-1.1) unstable; urgency=low
-
- * NMU by sponsor.
-
- -- Jonas Smedegaard 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.
- * Now uses dh-buildinfo to store information about the package build
- environment. Added a Build-Dependancy to dh-buildinfo.
- * Removed cdbs/autotools-vars.mk, from cdbs CVS, because a new release
- was made.
- * Enabled curl during configure stage, since --disable-curl was recently
- broken upstream, and potentially broken YP support can be disabled at
- runtime.
- * Trimmed ancient upgrade nodes and other cruft, left over from
- pre-Debian versions, from README.Debian.
- * Removed some autotools build cruft, since bugs were fixed upstream.
- * Normalized {preinst,postinst,postrm} filenames to
- icecast2.{preinst,postinst,postrm}. Thanks to Emmanuel le Chevoir
- for this suggestion.
- * Fixed preinst; was stopping /usr/bin/icecast instead of
- /usr/bin/icecast2. Thanks to Emmanuel le Chevoir for this suggestion.
- * Removed prerm, since it was not serving any purpose. Thanks to
- Emmanuel le Chevoir for this suggestion.
- * Cleaned up postinst: removed old comments, fixed a path typo in the
- configuration file location change message.
- * Cleaned up postrm: removed old comments, fixed a typo in the group
- removal test.
- * Thanks to Jonas Smedegaard for sponsoring this package, and
- providing many good suggestions.
-
- -- Keegan Quinn Sat, 2 Aug 2003 20:28:13 -0700
-
-icecast2 (1.9+2.0alphasnap2+20030720-1.1) unstable; urgency=low
-
- * NMU by sponsor (still closes: Bug#178160).
-
- -- Jonas Smedegaard Wed, 23 Jul 2003 06:03:42 +0200
-
-icecast2 (1.9+2.0alphasnap2+20030720-1) unstable; urgency=low
-
- * New daily snapshot build.
- * Added Build-Dependancy to cdbs, and increased debhelper version
- requirement as recommended by cdbs README.
- * Corrected Standards-Version to 3.6.0. This package now generates
- no lintian errors.
- * Updated Recommends for ices to ices2; it was renamed.
- * Added more information to the long description.
- * Thanks to Jonas Smedegaard for sponsoring this package.
- * This revision still closes: #178160 - the last was not uploaded.
-
- -- Keegan Quinn Mon, 21 Jul 2003 08:55:27 -0700
-
-icecast2 (1.9+2.0alphasnap2+20030714-0.2) unstable; urgency=low
-
- * Sponsored upload. Closes: Bug#178160.
- * Switch to cdbs (agreed with maintainer).
- * Use cdbs autotools-vars.mk from CVS to avoid cross-compiling on same
- host.
- * Add build-dependency on libxml2-dev.
- * Explicitly configure without curl support to avoid building broken
- YP stuff.
- * Hack src/Makefile.am to use AM_CFLAGS instead of CFLAGS (which is
- overridden by cdbs), and add clean rule to avoid invoking automake.
- * Disable daemon by default and hint about changing passwords before
- enabling.
- * Avoid moving config files from pre-Debian times - instead just print
- a warning if config exists in old location (better mess as little as
- possible with files not ever claimed to be ours).
- * Update README.Debian to reflect the above, include note about YP
- support not compiled in, and remove note regarding adoption.
- * Strip paths from packaging scripts (they may move around in the
- future, and if PATH is wrong then something else broken anyway).
- * Avoid removing unused /usr/share/icecast2 on purge.
- * Remove icecast group on purge only if empty.
- * Standards-version 3.6 (no changes needed).
- * Fix wrong escaping of sed vars in conf/Makefile.
- * Symlink public files from /usr/share/icecast2 to /etc/icecast2
- (instead of pointing public root dirs below /etc).
- * Use upstream config (paths are properly included now).
-
- -- Jonas Smedegaard Sun, 20 Jul 2003 20:19:30 +0200
-
-icecast2 (1.9+2.0alphasnap2+20030714-0.1) unstable; urgency=low
-
- * New daily snapshot build.
- * Updated versioning scheme to reflect (as well as possible) that the
- source is a daily snapshot now, not CVS.
-
- -- Keegan Quinn Mon, 14 Jul 2003 19:39:58 -0700
-
-icecast2 (1.9+2.0alphacvs030704-0.1) unstable; urgency=low
-
- * Constructed a build script to completely automate the construction
- of the 'pristine' tarball from CVS. This doesn't really effect the
- contents of the package, just makes it easier for me to rebuild.
- * New CVS source.
- * Removed Build-Dependancy on libcurl2-dev; packages built without this
- library present will not have YP functionality, which is okay for
- now since it's badly broken.
- * Updated the default configuration file, including some new options
- recently added upstream.
- * Added a number of tweaks to clean up and rearrange new configuration
- and documentation added to upstream install target.
- * Moved the configuration file from /etc/icecast.xml to
- /etc/icecast2/icecast.xml. See README.Debian.
- * Nice ugly version number to reflect that upstream calls this the 2.0
- alpha branch, without potentially introducing the need for an epoch.
-
- -- Keegan Quinn Thu, 3 Jul 2003 23:46:56 -0700
-
-icecast2 (0.00.cvs030529-0.1) unstable; urgency=low
-
- * New CVS source.
- * Removed unnecessary debconf stuff.
- * Added README.Debian.
- * Path updates:
- - /usr/share/icecast to /usr/share/icecast2,
- - /var/log/icecast to /var/log/icecast2,
- - /usr/bin/icecast to /usr/bin/icecast2,
- - /usr/share/man/man8/icecast.8.gz to /usr/share/man/man8/icecast2.8.gz.
-
- -- Keegan Quinn Wed, 29 May 2003 22:53:21 -0700
-
-icecast2 (0.00.cvs030403-0.2) unstable; urgency=low
-
- * Tried to make the default configuration more understandable.
-
- -- Keegan Quinn Fri, 4 Apr 2003 10:55:27 -0800
-
-icecast2 (0.00.cvs030403-0.1) unstable; urgency=low
-
- * New CVS source.
- * Minor changes to postrm.
-
- -- Keegan Quinn Thu, 3 Apr 2003 16:05:09 -0800
-
-icecast2 (0.00.cvs030401-0.7) unstable; urgency=low
-
- * Minor changes to postinst.
- * Added --background flag to initscript, since this version of icecast
- does not yet run detached.
-
- -- Keegan Quinn Thu, 3 Apr 2003 14:24:19 -0800
-
-icecast2 (0.00.cvs030401-0.6) unstable; urgency=low
-
- * Added Debianized configuration file.
- * Created and set ownership of /var/log/icecast and /usr/share/icecast.
-
- -- Keegan Quinn Thu, 3 Apr 2003 14:15:11 -0800
-
-icecast2 (0.00.cvs030401-0.5) unstable; urgency=low
-
- * Attempt at making debconf work properly.
-
- -- Keegan Quinn Thu, 3 Apr 2003 12:07:16 -0800
-
-icecast2 (0.00.cvs030401-0.4) unstable; urgency=low
-
- * Minor edits to init.d script.
- * Added bits to create and remove system accounts appropriately.
- * Typo fix in the manual page.
-
- -- Keegan Quinn Thu, 3 Apr 2003 11:06:48 -0800
-
-icecast2 (0.00.cvs030401-0.3) unstable; urgency=low
-
- * Finished init.d script and manual page.
- * Updated postinst to handle rc*.d links.
- * Package is now lintian/linda clean.
-
- -- Keegan Quinn Wed, 2 Apr 2003 16:29:18 -0800
-
-icecast2 (0.00.cvs030401-0.2) unstable; urgency=low
-
- * Updated copyright (replacing dh_make template).
- * Fixed duplicate conffiles.
-
- -- Keegan Quinn Wed, 2 Apr 2003 16:18:02 -0800
-
-icecast2 (0.00.cvs030401-0.1) unstable; urgency=low
-
- * New CVS source.
- * Lots of packaging cleanup.
- * Initial stab at manual page and init.d script.
-
- -- Keegan Quinn Wed, 2 Apr 2003 10:25:56 -0800
-
-icecast2 (0.00.cvs030320-0.1) unstable; urgency=low
-
- * New CVS source.
- * Automated CVS original source creation.
-
- -- Keegan Quinn Thu, 20 Mar 2003 12:58:49 -0800
-
-icecast2 (0.00.cvs030315-0.1) unstable; urgency=low
-
- * Initial Release.
-
- -- Keegan Quinn Sun, 16 Mar 2003 13:45:23 -0800
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index b8626c4c..00000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-4
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 18d2b974..00000000
--- a/debian/control
+++ /dev/null
@@ -1,20 +0,0 @@
-Source: icecast2
-Section: sound
-Priority: optional
-Maintainer: Rama
-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: 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.
- .
- Website: http://www.icecast.org/
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index ebcba318..00000000
--- a/debian/copyright
+++ /dev/null
@@ -1,96 +0,0 @@
-This is Icecast 2.x packaged for Debian.
-
-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
-
-Copyright and license; src/httpd/:
-
- licensed under the lgpl
-
- created by jack moffitt
-
-Copyright and license; src/avl/:
-
- * Copyright (C) 1995-1997 by Sam Rushing
- *
- * 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.
-
-Copyright and license; src/thread/:
-
- * Copyright (c) 1999, 2000 the icecast team
- *
- * 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
- *
- * 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
-
-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 ,
- * oddsock ,
- * Karl Heyes
- * 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/'.
diff --git a/debian/icecast2.1 b/debian/icecast2.1
deleted file mode 100644
index dc1b9e90..00000000
--- a/debian/icecast2.1
+++ /dev/null
@@ -1,24 +0,0 @@
-.\" Hey, EMACS: -*- nroff -*-
-.TH ICECAST2 1 "July 28, 2003"
-.SH NAME
-icecast2 \- an MP3/Ogg Vorbis broadcast streaming media server
-.SH SYNOPSIS
-.B icecast2
--c
-.RI config.xml
-.SH DESCRIPTION
-\fBicecast2\fP is an audio broadcasting system that streams music in
-Ogg Vorbis and/or MPEG 1 Layer III format. It accepts stream input
-from sources like ices0 and ices2, and broadcasts to clients like xmms.
-.SH OPTIONS
-\fBicecast2\fP has no command line options, except to specify the location
-of an XML configuration file. All operational aspects of the software
-are controlled by this XML configuration file.
-.SH SEE ALSO
-The example configuration files, provided with the package documentation
-are the only reliable source of information on this software.
-It is still under very active development;
-documentation will be written when it is possible to do so.
-.SH AUTHOR
-icecast2 was created by the icecast team .
-This manual page was written by Keegan Quinn .
diff --git a/debian/icecast2.default b/debian/icecast2.default
deleted file mode 100644
index d45f5d07..00000000
--- a/debian/icecast2.default
+++ /dev/null
@@ -1,19 +0,0 @@
-# Defaults for icecast2 initscript
-# sourced by /etc/init.d/icecast2
-# installed at /etc/default/icecast2 by the maintainer scripts
-
-#
-# This is a POSIX shell fragment
-#
-
-# Full path to the server configuration file
-CONFIGFILE="/etc/icecast2/icecast.xml"
-
-# Name or ID of the user and group the daemon should run under
-USERID=icecast2
-GROUPID=icecast
-
-# Edit /etc/icecast2/icecast.xml and change at least the passwords.
-# Change this to true when done to enable the init.d script
-ENABLE=false
-
diff --git a/debian/icecast2.init b/debian/icecast2.init
deleted file mode 100644
index 6bb85d50..00000000
--- a/debian/icecast2.init
+++ /dev/null
@@ -1,67 +0,0 @@
-#! /bin/sh
-#
-# icecast2
-#
-# Written by Miquel van Smoorenburg .
-# Modified for Debian
-# by Ian Murdock .
-#
-# Further modified by Keegan Quinn
-# for use with Icecast 2
-#
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DAEMON=/usr/bin/icecast2
-NAME=icecast2
-DESC=icecast2
-
-test -x $DAEMON || exit 0
-
-# Defaults
-CONFIGFILE="/etc/icecast2/icecast.xml"
-CONFIGDEFAULTFILE="/etc/default/icecast2"
-USERID=icecast2
-GROUPID=icecast
-ENABLE="false"
-
-# Reads config file (will override defaults above)
-[ -r "$CONFIGDEFAULTFILE" ] && . $CONFIGDEFAULTFILE
-
-if [ "$ENABLE" != "true" ]; then
- echo "$NAME daemon disabled - read $CONFIGDEFAULTFILE."
- exit 0
-fi
-
-set -e
-
-case "$1" in
- start)
- echo -n "Starting $DESC: "
- start-stop-daemon --start --quiet --chuid $USERID:$GROUPID \
- --exec $DAEMON -- -b -c $CONFIGFILE
- echo "$NAME."
- ;;
- stop)
- echo -n "Stopping $DESC: "
- start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
- echo "$NAME."
- ;;
- reload|force-reload)
- echo "Reloading $DESC configuration files."
- start-stop-daemon --stop --signal 1 --quiet --exec $DAEMON
- ;;
- restart)
- echo -n "Restarting $DESC: "
- start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
- sleep 2
- start-stop-daemon --start --quiet --chuid $USERID:$GROUPID \
- --exec $DAEMON -- -b -c $CONFIGFILE
- echo "$NAME."
- ;;
- *)
- echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2
- exit 1
- ;;
-esac
-
-exit 0
diff --git a/debian/icecast2.manpages b/debian/icecast2.manpages
deleted file mode 100644
index 69e59afa..00000000
--- a/debian/icecast2.manpages
+++ /dev/null
@@ -1 +0,0 @@
-debian/icecast2.1
diff --git a/debian/icecast2.postinst b/debian/icecast2.postinst
deleted file mode 100644
index 4476a6fe..00000000
--- a/debian/icecast2.postinst
+++ /dev/null
@@ -1,55 +0,0 @@
-#! /bin/sh
-# postinst script for icecast2
-
-set -e
-
-case "$1" in
- configure)
-
- ;;
-
- abort-upgrade|abort-remove|abort-deconfigure)
- exit 0
- ;;
-
- *)
- echo "postinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-# Move configuration file to current location, if an old one exists
-# and the init.d script configuration file was updated
-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
-
-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
-
-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#
-
-exit 0
-
diff --git a/debian/icecast2.postrm b/debian/icecast2.postrm
deleted file mode 100644
index 33808d81..00000000
--- a/debian/icecast2.postrm
+++ /dev/null
@@ -1,30 +0,0 @@
-#! /bin/sh
-# postrm script for icecast2
-
-set -e
-
-case "$1" in
- purge)
- rm -rf /var/log/icecast2
- rm -rf /usr/share/icecast2/log
-
- 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 || echo "Error occured removing group icecast, please do it manually."
- fi
- ;;
- remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-
- ;;
- *)
- echo "postrm called with unknown argument \`$1'" >&2
- exit 1
-esac
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/icecast2.preinst b/debian/icecast2.preinst
deleted file mode 100644
index 35e111eb..00000000
--- a/debian/icecast2.preinst
+++ /dev/null
@@ -1,27 +0,0 @@
-#! /bin/sh
-# preinst script for icecast2
-
-set -e
-
-case "$1" in
- install|upgrade)
- if [ "$1" = "upgrade" ]
- then
- start-stop-daemon --stop --quiet --oknodo \
- --exec /usr/bin/icecast2 2>/dev/null || true
- fi
- ;;
-
- abort-upgrade)
- ;;
-
- *)
- echo "preinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
-
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index c36f91a1..00000000
--- a/debian/rules
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/make -f
-# -*- mode: makefile; coding: utf-8 -*-
-# Copyright © 2004 Jonas Smedegaard
-include /usr/share/cdbs/1/rules/debhelper.mk
-include /usr/share/cdbs/1/class/autotools.mk
-include debian/cdbs/1/rules/buildinfo.mk
-
-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
-
-# 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
-
-# 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
- 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
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index 5acd78dc..00000000
--- a/debian/watch
+++ /dev/null
@@ -1,3 +0,0 @@
-# Run the "uscan" command to check for upstream updates and more.
-version=2
-http://downloads.us.xiph.org/releases/icecast/icecast-([\d+\.]+|\d+)(\.tar|\.tgz)(\.gz|\.bz2|) 2.2.0 uupdate
diff --git a/src/admin.c b/src/admin.c
index 51111a34..c3cee47f 100644
--- a/src/admin.c
+++ b/src/admin.c
@@ -58,6 +58,7 @@ static void command_reset_stats (client_t *client, source_t *source, int respons
static void command_manageauth(client_t *client, source_t *source,
int response);
static void command_buildm3u(client_t *client, const char *mount);
+static void command_show_image (client_t *client, const char* mount);
static void command_kill_source(client_t *client, source_t *source,
int response);
static void command_updatemetadata(client_t *client, source_t *source,
@@ -355,6 +356,11 @@ int admin_handle_request (client_t *client, const char *uri)
command_buildm3u (client, mount);
return 0;
}
+ if (strcmp (uri, "showimage") == 0)
+ {
+ command_show_image (client, mount);
+ return 0;
+ }
/* This is a mount request, but admin user is allowed */
if (client->authenticated == 0)
@@ -763,6 +769,29 @@ static void command_show_listeners(client_t *client, source_t *source,
xmlFreeDoc(doc);
}
+static void command_show_image (client_t *client, const char *mount)
+{
+ source_t *source;
+
+ avl_tree_rlock (global.source_tree);
+ source = source_find_mount_raw (mount);
+ if (source && source->format && source->format->get_image)
+ {
+ thread_mutex_lock (&source->lock);
+ avl_tree_unlock (global.source_tree);
+ if (source->format->get_image (client, source->format) == 0)
+ {
+ thread_mutex_unlock (&source->lock);
+ fserve_add_client (client, NULL);
+ return;
+ }
+ thread_mutex_unlock (&source->lock);
+ }
+ else
+ avl_tree_unlock (global.source_tree);
+ client_send_404 (client, "No image available");
+}
+
static void command_buildm3u (client_t *client, const char *mount)
{
const char *username = NULL;
@@ -961,6 +990,7 @@ static void command_metadata(client_t *client, source_t *source,
format_plugin_t *plugin;
xmlDocPtr doc;
xmlNodePtr node;
+ int same_ip = 1;
doc = xmlNewDoc(XMLSTR("1.0"));
node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
@@ -977,10 +1007,13 @@ static void command_metadata(client_t *client, source_t *source,
thread_mutex_lock (&source->lock);
plugin = source->format;
+ if (source->client && strcmp (client->con->ip, source->client->con->ip) != 0)
+ if (response == RAW && connection_check_admin_pass (client->parser) == 0)
+ same_ip = 0;
do
{
- if (plugin == NULL)
+ if (same_ip == 0 && plugin == NULL)
break;
if (artwork)
stats_event (source->mount, "artwork", artwork);
@@ -1026,6 +1059,7 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
{
const char *action;
const char *value;
+ int same_ip = 1;
DEBUG0("Got shoutcast metadata update request");
@@ -1046,6 +1080,10 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
return;
}
+ if (source->client && strcmp (client->con->ip, source->client->con->ip) != 0)
+ if (connection_check_admin_pass (client->parser) == 0)
+ same_ip = 0;
+
if (source->format && source->format->set_tag)
{
httpp_set_query_param (client->parser, "mount", client->server_conn->shoutcast_mount);
@@ -1084,7 +1122,7 @@ static void command_stats (client_t *client, const char *filename)
show_mount = httpp_get_query_param (client->parser, "mount");
- stats_get_xml(&doc, 1, show_mount);
+ stats_get_xml(&doc, STATS_ALL, show_mount);
admin_send_response (doc, client, response, filename);
xmlFreeDoc(doc);
}
diff --git a/src/auth_htpasswd.c b/src/auth_htpasswd.c
index 9bf9f0b4..638896fd 100644
--- a/src/auth_htpasswd.c
+++ b/src/auth_htpasswd.c
@@ -108,6 +108,8 @@ static void htpasswd_recheckfile (htpasswd_auth_state *htpasswd)
char *sep;
char line [MAX_LINE_LEN];
+ if (htpasswd->filename == NULL)
+ return;
if (stat (htpasswd->filename, &file_stat) < 0)
{
WARN1 ("failed to check status of %s", htpasswd->filename);
diff --git a/src/cfgfile.h b/src/cfgfile.h
index 92f10d9d..25b0a448 100644
--- a/src/cfgfile.h
+++ b/src/cfgfile.h
@@ -132,6 +132,14 @@ struct _listener_t
int ssl;
};
+typedef struct
+{
+ char *hostname;
+ int port;
+ char *username;
+ char *password;
+} ice_master_details;
+
typedef struct ice_config_tag
{
char *config_filename;
@@ -169,6 +177,8 @@ typedef struct ice_config_tag
listener_t *listen_sock;
unsigned int listen_sock_count;
+ ice_master_details *master;
+
char *master_server;
int master_server_port;
int master_update_interval;
diff --git a/src/connection.c b/src/connection.c
index 614cbfaa..eaf3036c 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1129,7 +1129,7 @@ static void _handle_stats_request (client_t *client, char *uri)
client->respcode = 200;
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
- "HTTP/1.0 200 OK\r\n\r\n");
+ "HTTP/1.0 200 OK\r\ncapability: streamlist\r\n\r\n");
client->refbuf->len = strlen (client->refbuf->data);
fserve_add_client_callback (client, stats_callback, NULL);
}
diff --git a/src/format.c b/src/format.c
index 2c2f82d5..7b18cd5f 100644
--- a/src/format.c
+++ b/src/format.c
@@ -378,4 +378,3 @@ static int format_prepare_headers (source_t *source, client_t *client)
return 0;
}
-
diff --git a/src/format.h b/src/format.h
index 24e59e9f..7a715c1d 100644
--- a/src/format.h
+++ b/src/format.h
@@ -53,6 +53,7 @@ typedef struct _format_plugin_tag
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);
+ int (*get_image)(client_t *client, struct _format_plugin_tag *format);
/* for internal state management */
void *_state;
diff --git a/src/format_kate.c b/src/format_kate.c
index 5f9e93d0..73a60ff8 100644
--- a/src/format_kate.c
+++ b/src/format_kate.c
@@ -38,12 +38,12 @@ typedef struct source_tag source_t;
typedef struct _kate_codec_tag
{
- int headers_done;
+ unsigned int headers_done;
#ifdef HAVE_KATE
kate_info ki;
kate_comment kc;
#endif
- int num_headers;
+ unsigned int num_headers;
int granule_shift;
ogg_int64_t last_iframe;
ogg_int64_t prev_granulepos;
diff --git a/src/format_mp3.c b/src/format_mp3.c
index 3eec8b0e..cb9ec072 100644
--- a/src/format_mp3.c
+++ b/src/format_mp3.c
@@ -484,12 +484,12 @@ static int complete_read (source_t *source)
}
return 0;
}
+ rate_add (format->in_bitrate, bytes, global.time);
}
source_mp3->read_count += bytes;
refbuf = source_mp3->read_data;
refbuf->len = source_mp3->read_count;
format->read_bytes += bytes;
- rate_add (format->in_bitrate, bytes, global.time);
if (source_mp3->read_count < source_mp3->queue_block_size)
{
@@ -586,7 +586,6 @@ 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 */
diff --git a/src/format_ogg.c b/src/format_ogg.c
index ffc33be9..251d4582 100644
--- a/src/format_ogg.c
+++ b/src/format_ogg.c
@@ -54,6 +54,7 @@ static void format_ogg_free_plugin (format_plugin_t *plugin);
static int create_ogg_client_data(source_t *source, client_t *client);
static void free_ogg_client_data (client_t *client);
+static int get_image (client_t *client, struct _format_plugin_tag *format);
static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
static refbuf_t *ogg_get_buffer (source_t *source);
static int write_buf_to_client (client_t *client);
@@ -152,8 +153,7 @@ static void free_ogg_codecs (ogg_state_t *ogg_info)
while (codec)
{
ogg_codec_t *next = codec->next;
- if (codec->possible_start)
- refbuf_release (codec->possible_start);
+ refbuf_release (codec->possible_start);
codec->codec_free (ogg_info, codec);
codec = next;
}
@@ -177,6 +177,7 @@ int format_ogg_get_plugin (source_t *source)
plugin->write_buf_to_file = write_ogg_to_file;
plugin->create_client_data = create_ogg_client_data;
plugin->free_plugin = format_ogg_free_plugin;
+ plugin->get_image = get_image;
plugin->set_tag = NULL;
plugin->apply_settings = apply_ogg_settings;
if (strcmp (httpp_getvar (source->parser, "content-type"), "application/x-ogg") == 0)
@@ -623,4 +624,25 @@ static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf)
write_ogg_data (source, refbuf);
}
+static int get_image (client_t *client, struct _format_plugin_tag *format)
+{
+ const char *serialp = httpp_get_query_param (client->parser, "serial");
+ ogg_state_t *ogg_info = format->_state;
+ ogg_codec_t *codec = ogg_info->codecs;
+ long serial;
+ if (serialp)
+ serial = atoll (serialp);
+ while (codec)
+ {
+ if (serialp == NULL || serial == codec->os.serialno)
+ {
+ int ret = 0;
+ if (codec->get_image)
+ ret = codec->get_image (client, codec);
+ return ret;
+ }
+ codec = codec->next;
+ }
+ return 0;
+}
diff --git a/src/format_ogg.h b/src/format_ogg.h
index 6320bab9..f22d6e63 100644
--- a/src/format_ogg.h
+++ b/src/format_ogg.h
@@ -65,6 +65,7 @@ typedef struct ogg_codec_tag
refbuf_t *(*process_page)(ogg_state_t *ogg_info,
struct ogg_codec_tag *codec, ogg_page *page);
void (*codec_free)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec);
+ int (*get_image)(client_t *client, struct ogg_codec_tag *codec);
} ogg_codec_t;
diff --git a/src/format_theora.c b/src/format_theora.c
index f1b4db58..1149f6bf 100644
--- a/src/format_theora.c
+++ b/src/format_theora.c
@@ -31,7 +31,20 @@ typedef struct source_tag source_t;
#define CATMODULE "format-theora"
#include "logging.h"
+#ifdef WITH_VIDEO_PREVIEW
+#include
+typedef struct _video_preview_struct
+{
+ png_byte* rgb_image ;
+ int png_compression_level ;
+
+ int video_width ;
+ int video_height ;
+ int x_crop_offset ;
+ int y_crop_offset ;
+} video_preview_t;
+#endif
typedef struct _theora_codec_tag
{
@@ -40,9 +53,255 @@ typedef struct _theora_codec_tag
int granule_shift;
ogg_int64_t last_iframe;
ogg_int64_t prev_granulepos;
+#ifdef WITH_VIDEO_PREVIEW
+ theora_state td;
+ video_preview_t *video_preview;
+ int frame_count;
+#endif
} theora_codec_t;
+ /* video_preview.c
+ * Copyright 2005, Silvano Galliani aka kysucix
+ */
+
+
+#include "logging.h"
+
+
+#ifdef WITH_VIDEO_PREVIEW
+struct preview_details
+{
+ unsigned int total_length;
+ refbuf_t **last;
+};
+
+static void yuv2rgb (yuv_buffer *_yuv, video_preview_t *video_preview);
+static int clip (int x);
+
+
+static void user_write_data (png_structp png_ptr, png_bytep data, png_size_t length)
+{
+#define BUFFER_BLOCK_SZ 316*1024
+ struct preview_details *details = png_get_io_ptr (png_ptr);
+ unsigned int offset = 0;
+ int c = 0;
+
+ if (*details->last == NULL)
+ {
+ /* special case */
+ refbuf_t *r = refbuf_new (BUFFER_BLOCK_SZ);
+ r->len = 0;
+ *details->last = r;
+ c++;
+ }
+ while (length)
+ {
+ unsigned int amount;
+ refbuf_t *buffer = *details->last;
+ if (buffer->len == BUFFER_BLOCK_SZ)
+ {
+ refbuf_t *last = buffer;
+ buffer = refbuf_new (BUFFER_BLOCK_SZ);
+ buffer->len = 0;
+ last->next = buffer;
+ details->last = &last->next;
+ c++;
+ }
+ amount = BUFFER_BLOCK_SZ - buffer->len;
+ if (amount > length)
+ amount = length;
+ memcpy (buffer->data+buffer->len, data+offset, amount);
+ buffer->len += amount;
+ offset += amount;
+ length -= amount;
+ }
+ details->total_length += offset;
+}
+
+
+static void user_flush_data (png_structp png_ptr)
+{
+}
+
+static void user_error (png_structp png_ptr, png_const_charp c)
+{
+ longjmp (png_ptr->jmpbuf, 1);
+}
+
+
+void free_video_preview (video_preview_t *video_preview)
+{
+ DEBUG0 ("freeing video preview");
+ free (video_preview->rgb_image);
+ free (video_preview);
+}
+
+
+video_preview_t *init_video_preview (int width, int height, int _x_crop_offset, int _y_crop_offset)
+{
+ DEBUG0("init video preview");
+
+ video_preview_t *video_preview = calloc (1, sizeof (video_preview_t));
+
+ /* init structure */
+ video_preview -> rgb_image = NULL;
+ video_preview -> png_compression_level = Z_BEST_SPEED;
+
+ video_preview -> video_width = width;
+ video_preview -> video_height = height;
+ video_preview -> x_crop_offset = _x_crop_offset;
+ video_preview -> y_crop_offset = _y_crop_offset;
+
+ /* malloc rgb image */
+ video_preview -> rgb_image = (png_byte *)calloc (1, video_preview -> video_width * video_preview -> video_height * 4 );
+ if (video_preview->rgb_image == NULL)
+ {
+ ERROR0 ("Can't allocate memory for rgb_image");
+ free_video_preview (video_preview);
+ return NULL;
+ }
+ return video_preview;
+}
+
+
+static int write_video_preview (client_t *client, video_preview_t *video_preview)
+{
+ int i;
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ struct preview_details preview;
+
+ preview.total_length = 0;
+ preview.last = &client->refbuf->next;
+
+ png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
+ if (png_ptr == NULL)
+ return -1;
+
+ info_ptr = png_create_info_struct (png_ptr);
+ if (info_ptr == NULL)
+ {
+ png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
+ return -1;
+ }
+
+ if (setjmp (png_ptr->jmpbuf))
+ {
+ png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
+ return -1;
+ }
+
+ png_set_error_fn (png_ptr, NULL, user_error, user_error);
+ png_set_write_fn (png_ptr, &preview, user_write_data, user_flush_data);
+
+ png_set_filter( png_ptr, 0, PNG_FILTER_NONE );
+
+ /* set the zlib compression level */
+ png_set_compression_level (png_ptr, video_preview->png_compression_level);
+
+ png_set_IHDR (png_ptr, info_ptr, video_preview->video_width , video_preview->video_height,
+ 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ png_write_info (png_ptr, info_ptr);
+ ERROR0("finished writing png header");
+
+ /* write image to hard disk */
+ for ( i = 0; i < video_preview -> video_height; i++)
+ png_write_row (png_ptr,
+ video_preview->rgb_image + i * (video_preview->video_width) * 4 );
+
+ png_write_end (png_ptr, info_ptr);
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+
+ snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\n"
+ "Content-Length: %d\r\nContentType: image/png\r\n\r\n", preview.total_length);
+ client->refbuf->len = strlen (client->refbuf->data);
+ client->respcode = 200;
+
+ return 0;
+}
+
+
+static int get_image (client_t *client, struct ogg_codec_tag *codec)
+{
+ theora_codec_t *theora = codec->specific;
+ return write_video_preview (client, theora->video_preview);
+}
+
+
+/* ok it has to be optimized but for now it's clean, and it's ok ;) */
+static void yuv2rgb (yuv_buffer *_yuv, video_preview_t *video_preview)
+{
+ int i,j;
+ int crop_offset;
+
+ int y_offset;
+ int ypp_offset;
+ int uv_offset;
+
+ /* rgba surface pointer */
+ unsigned char *prgb;
+ yuv_buffer *yuv;
+
+ unsigned char y;
+ unsigned char ypp;
+ unsigned char u;
+ unsigned char v;
+
+ yuv = _yuv;
+
+ crop_offset = (video_preview -> x_crop_offset) +
+ (yuv -> y_stride) * (video_preview -> y_crop_offset);
+ prgb = (unsigned char *)video_preview -> rgb_image;
+
+ for (i = 0; i < video_preview -> video_height; i++ ) {
+ for ( j = 0; j < video_preview -> video_width / 2; j++ ) {
+
+ y_offset = yuv -> y_stride * i + j*2 + crop_offset;
+ ypp_offset = yuv -> y_stride * i + j*2 + crop_offset + 1;
+ uv_offset = yuv -> uv_stride * (i/2) + j + crop_offset;
+
+ y = *(yuv->y + y_offset);
+ ypp = *(yuv->y + ypp_offset);
+ u = *(yuv->u + uv_offset);
+ v = *(yuv->v + uv_offset);
+
+ /* R G B A */
+ *prgb = clip (y + 1.402f * (v-128)) ;
+ prgb++;
+ *prgb = clip (y - 0.34414f * (u-128) - 0.71414f * (v-128)) ;
+ prgb++;
+ *prgb = clip (y + 1.772 * (u-128)) ;
+ prgb++;
+ *prgb = 255;
+ prgb++;
+
+ /* R G B A */
+ *prgb = clip (ypp + 1.402f * (v-128)) ;
+ prgb++;
+ *prgb = clip (ypp - 0.34414f * (u-128) - 0.71414f * (v-128)) ;
+ prgb++;
+ *prgb = clip (ypp + 1.772 * (u-128)) ;
+ prgb++;
+ *prgb = 255;
+ prgb++;
+ }
+ }
+}
+
+static int clip (int x)
+{
+ if (x > 255)
+ return 255;
+ else if (x < 0)
+ return 0;
+ return x;
+}
+#endif
+
+
static void theora_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
{
theora_codec_t *theora = codec->specific;
@@ -52,6 +311,14 @@ static void theora_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
stats_event (ogg_info->mount, "video_quality", NULL);
stats_event (ogg_info->mount, "frame_rate", NULL);
stats_event (ogg_info->mount, "frame_size", NULL);
+#ifdef WITH_VIDEO_PREVIEW
+ stats_event (ogg_info->mount, "video_preview", NULL);
+ if (theora->video_preview)
+ {
+ free_video_preview (theora->video_preview);
+ theora_clear (&theora->td);
+ }
+#endif
theora_info_clear (&theora->ti);
theora_comment_clear (&theora->tc);
ogg_stream_clear (&codec->os);
@@ -113,7 +380,30 @@ static refbuf_t *process_theora_page (ogg_state_t *ogg_info, ogg_codec_t *codec,
return NULL;
}
if (theora_packet_iskeyframe (&packet))
+ {
has_keyframe = 1;
+#ifdef WITH_VIDEO_PREVIEW
+ if (theora->video_preview)
+ {
+ if (theora->frame_count == -1)
+ {
+ theora_decode_init (&theora->td, &theora->ti);
+ theora->frame_count = 0;
+ }
+ if (theora->frame_count % 16)
+ theora->frame_count++;
+ else
+ {
+ yuv_buffer yuv;
+
+ theora_decode_packetin (&theora->td, &packet);
+ theora_decode_YUVout (&theora->td, &yuv);
+ yuv2rgb (&yuv, theora->video_preview);
+ theora->frame_count = 1;
+ }
+ }
+#endif
+ }
}
if (header_page)
{
@@ -126,8 +416,7 @@ static refbuf_t *process_theora_page (ogg_state_t *ogg_info, ogg_codec_t *codec,
if (granulepos != theora->prev_granulepos || granulepos == 0)
{
- if (codec->possible_start)
- refbuf_release (codec->possible_start);
+ refbuf_release (codec->possible_start);
refbuf_addref (refbuf);
codec->possible_start = refbuf;
}
@@ -182,6 +471,20 @@ ogg_codec_t *initial_theora_page (format_plugin_t *plugin, ogg_page *page)
codec->filtered = 1;
codec->name = "Theora";
+#ifdef WITH_VIDEO_PREVIEW
+ /* check for video_preview config and video_preview initialization */
+ if (1)
+ {
+ theora_info *ti = &theora_codec->ti;
+ theora_codec->video_preview = init_video_preview (ti->width, ti->height, ti->offset_x, ti->offset_y);
+ theora_codec->frame_count = -1;
+ codec->get_image = get_image;
+ stats_event_args (ogg_info->mount, "video_preview", "/admin/showimage?mount=%s&serial=%ld", ogg_info->mount, codec->os.serialno);
+ //theora_decode_init (&theora_codec->td, &theora_codec->ti);
+ // theora_codec->frame_count = -1;
+ }
+#endif
+
format_ogg_attach_header (codec, page);
if (codec->filtered == 0)
ogg_info->codec_sync = codec;
diff --git a/src/fserve.c b/src/fserve.c
index 62574c0f..f4a42d1d 100644
--- a/src/fserve.c
+++ b/src/fserve.c
@@ -296,8 +296,9 @@ static void *fserv_thread_function(void *arg)
client_tree_changed = 1;
continue;
}
- client_set_queue (client, refbuf->next);
- refbuf = client->refbuf;
+ refbuf = refbuf->next;
+ refbuf_release (client->refbuf);
+ client->refbuf = refbuf;
bytes = refbuf->len;
}
refbuf->len = bytes;
diff --git a/src/global.c b/src/global.c
index 39053192..5b0bbe97 100644
--- a/src/global.c
+++ b/src/global.c
@@ -45,8 +45,7 @@ void global_initialize(void)
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);
+ global.out_bitrate = rate_setup (151, 1000);
}
void global_shutdown(void)
@@ -70,10 +69,8 @@ void global_unlock(void)
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);
+ rate_add (rate, value, global.time_ms);
thread_spin_unlock (&global.spinlock);
}
@@ -88,7 +85,7 @@ unsigned long global_getrate_avg (struct rate_calc *rate)
{
unsigned long v;
thread_spin_lock (&global.spinlock);
- v = rate_avg (rate)*10L;
+ v = rate_avg (rate);
thread_spin_unlock (&global.spinlock);
return v;
}
diff --git a/src/refbuf.c b/src/refbuf.c
index 5e22f3f7..dd6d918d 100644
--- a/src/refbuf.c
+++ b/src/refbuf.c
@@ -35,7 +35,7 @@ void refbuf_shutdown(void)
{
}
-refbuf_t *refbuf_new(size_t size)
+refbuf_t *refbuf_new (unsigned int size)
{
refbuf_t *refbuf;
diff --git a/src/refbuf.h b/src/refbuf.h
index f2d72c29..4fd0e31b 100644
--- a/src/refbuf.h
+++ b/src/refbuf.h
@@ -35,7 +35,7 @@ typedef struct _refbuf_tag
void refbuf_initialize(void);
void refbuf_shutdown(void);
-refbuf_t *refbuf_new(size_t size);
+refbuf_t *refbuf_new(unsigned int size);
void refbuf_addref(refbuf_t *self);
void refbuf_release(refbuf_t *self);
diff --git a/src/slave.c b/src/slave.c
index 2848bc9f..5e9a54a1 100644
--- a/src/slave.c
+++ b/src/slave.c
@@ -1075,12 +1075,17 @@ static void *_slave_thread(void *arg)
update_from_master (config);
+ thread_mutex_lock (&(config_locks()->relay_lock));
cleanup_relays = update_relays (&global.relays, config->relay);
config_release_config();
}
+ else
+ thread_mutex_lock (&(config_locks()->relay_lock));
+
relay_check_streams (global.relays, cleanup_relays, skip_timer);
relay_check_streams (global.master_relays, NULL, skip_timer);
+ thread_mutex_unlock (&(config_locks()->relay_lock));
if (update_settings)
{
diff --git a/src/source.c b/src/source.c
index 7a6a2813..9fbf80b3 100644
--- a/src/source.c
+++ b/src/source.c
@@ -254,6 +254,8 @@ void source_clear_source (source_t *source)
{
refbuf_t *to_go = p;
p = to_go->next;
+ if (to_go->_count > 1)
+ WARN1 ("buffer is %d", to_go->_count);
refbuf_release (to_go);
}
/* the source holds 2 references on the very latest so that one
@@ -434,9 +436,9 @@ static void update_source_stats (source_t *source)
unsigned long kbytes_read = source->bytes_read_since_update/1024;
source->format->sent_bytes += kbytes_sent*1024;
- stats_event_args (source->mount, "outgoing_bitrate", "%ld",
+ stats_event_args (source->mount, "outgoing_kbitrate", "%ld",
(8 * rate_avg (source->format->out_bitrate))/1000);
- stats_event_args (source->mount, "incoming_bitrate", "%ld", incoming_rate/1000);
+ stats_event_args (source->mount, "incoming_bitrate", "%ld", incoming_rate);
stats_event_args (source->mount, "total_bytes_read",
"%"PRIu64, source->format->read_bytes);
stats_event_args (source->mount, "total_bytes_sent",
@@ -682,7 +684,7 @@ static int send_to_listener (source_t *source, client_t *client, int deletion_ex
total_written += bytes;
}
- rate_add (source->format->out_bitrate, total_written, global.time);
+ rate_add (source->format->out_bitrate, total_written, global.time_ms);
source->bytes_sent_since_update += total_written;
global_add_bitrates (global.out_bitrate, total_written);
@@ -778,7 +780,7 @@ 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);
+ rate_reduce (source->format->out_bitrate, 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 */
@@ -874,8 +876,8 @@ static void source_init (source_t *source)
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);
+ source->format->in_bitrate = rate_setup (source->avg_bitrate_duration+1, 1);
+ source->format->out_bitrate = rate_setup (120, 1000);
source->running = 1;
}
@@ -1212,10 +1214,17 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
source->avg_bitrate_duration = 60;
/* needs a better mechanism, probably via a client_t handle */
+ free (source->dumpfilename);
+ source->dumpfilename = NULL;
if (mountinfo && mountinfo->dumpfile)
{
- free (source->dumpfilename);
- source->dumpfilename = strdup (mountinfo->dumpfile);
+ time_t now = time(NULL);
+ struct tm local;
+ char buffer[PATH_MAX];
+
+ localtime_r (&now, &local);
+ strftime (buffer, sizeof (buffer), mountinfo->dumpfile, &local);
+ source->dumpfilename = strdup (buffer);
}
/* handle changes in intro file setting */
if (source->intro_file)
@@ -1268,7 +1277,7 @@ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy
/* skip if source is a fallback to file */
if (source->running && source->client == NULL)
{
- stats_event_hidden (source->mount, NULL, 1);
+ stats_event_hidden (source->mount, NULL, NULL, STATS_HIDDEN);
return;
}
/* set global settings first */
@@ -1294,13 +1303,6 @@ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy
if (mountinfo)
{
- 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)
@@ -1309,12 +1311,21 @@ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy
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);
+ stats_event_hidden (source->mount, "cluster_password", mountinfo->cluster_password, STATS_SLAVE);
+ if (mountinfo->hidden)
+ {
+ stats_event_hidden (source->mount, NULL, NULL, STATS_HIDDEN);
+ DEBUG0 ("hidden from public");
+ }
+ else
+ stats_event_hidden (source->mount, NULL, NULL, STATS_PUBLIC);
}
else
{
DEBUG0 ("max listeners is not specified");
stats_event (source->mount, "max_listeners", "unlimited");
- stats_event_hidden (source->mount, NULL, 0);
+ stats_event_hidden (source->mount, "cluster_password", NULL, STATS_SLAVE);
+ stats_event_hidden (source->mount, NULL, NULL, STATS_PUBLIC);
}
DEBUG1 ("public set to %d", source->yp_public);
DEBUG1 ("queue size to %u", source->queue_size_limit);
@@ -1459,7 +1470,7 @@ static void *source_fallback_file (void *arg)
httpp_setvar (parser, "content-type", type);
free (type);
- stats_event_hidden (source->mount, NULL, 1);
+ stats_event_hidden (source->mount, NULL, NULL, STATS_HIDDEN);
source->yp_public = 0;
source->intro_file = file;
source->parser = parser;
@@ -1522,7 +1533,7 @@ void source_recheck_mounts (int update_all)
}
else if (update_all)
{
- stats_event_hidden (mount->mountname, NULL, mount->hidden);
+ stats_event_hidden (mount->mountname, NULL, NULL, mount->hidden?STATS_HIDDEN:0);
stats_event_args (mount->mountname, "listenurl", "http://%s:%d%s",
config->hostname, config->port, mount->mountname);
stats_event (mount->mountname, "listeners", "0");
diff --git a/src/stats.c b/src/stats.c
index dcd4a8e0..28065abc 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -53,7 +53,7 @@
#define STATS_EVENT_ADD 3
#define STATS_EVENT_SUB 4
#define STATS_EVENT_REMOVE 5
-#define STATS_EVENT_HIDDEN 6
+#define STATS_EVENT_HIDDEN 0x80
typedef struct _stats_node_tag
{
@@ -80,43 +80,47 @@ typedef struct _stats_source_tag
avl_tree *stats_tree;
} stats_source_t;
-typedef struct _event_queue_tag
-{
- volatile stats_event_t *head;
- volatile stats_event_t **tail;
-} event_queue_t;
-
-#define event_queue_init(qp) { (qp)->head = NULL; (qp)->tail = &(qp)->head; }
-
typedef struct _event_listener_tag
{
- event_queue_t queue;
- mutex_t mutex;
+ client_t *client;
int master;
+ int hidden_level;
char *source;
+ /* queue for unwritten stats to stats clients */
+ refbuf_t *queue, **queue_recent_p;
+ unsigned int content_len;
+
struct _event_listener_tag *next;
} event_listener_t;
+
+typedef struct _stats_tag
+{
+ avl_tree *global_tree;
+ avl_tree *source_tree;
+
+ /* list of listeners for stats */
+ event_listener_t *event_listeners, *listeners_removed;
+
+} stats_t;
+
static volatile int _stats_running = 0;
-static volatile int _stats_threads = 0;
static stats_t _stats;
static mutex_t _stats_mutex;
-static volatile event_listener_t *_event_listeners;
-
static int _compare_stats(void *a, void *b, void *arg);
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, 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);
static void process_event (stats_event_t *event);
+static void _add_stats_to_stats_client (event_listener_t *listener, const char *fmt, va_list ap);
+static void stats_listener_send (int flags, const char *fmt, ...);
+static void process_event_unlocked (stats_event_t *event);
/* simple helper function for creating an event */
@@ -125,8 +129,10 @@ static void build_event (stats_event_t *event, const char *source, const char *n
event->source = (char *)source;
event->name = (char *)name;
event->value = (char *)value;
+ event->hidden = STATS_PUBLIC;
+ if (source) event->hidden |= STATS_SLAVE;
if (value)
- event->action = 0;
+ event->action = STATS_EVENT_SET;
else
event->action = STATS_EVENT_REMOVE;
}
@@ -134,75 +140,52 @@ static void build_event (stats_event_t *event, const char *source, const char *n
void stats_initialize(void)
{
- _event_listeners = NULL;
+ if (_stats_running)
+ return;
/* set up global struct */
_stats.global_tree = avl_tree_new(_compare_stats, NULL);
_stats.source_tree = avl_tree_new(_compare_source_stats, NULL);
+ _stats.event_listeners = NULL;
+ _stats.listeners_removed = NULL;
+
/* set up global mutex */
thread_mutex_create("stats", &_stats_mutex);
- /* fire off the stats thread */
_stats_running = 1;
stats_event_time (NULL, "server_start");
/* global currently active stats */
- stats_event (NULL, "clients", "0");
- stats_event (NULL, "connections", "0");
- stats_event (NULL, "sources", "0");
- stats_event (NULL, "stats", "0");
+ stats_event_hidden (NULL, "clients", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "connections", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "sources", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "stats", "0", STATS_COUNTERS);
stats_event (NULL, "listeners", "0");
/* global accumulating stats */
- stats_event (NULL, "client_connections", "0");
- stats_event (NULL, "source_client_connections", "0");
- stats_event (NULL, "source_relay_connections", "0");
- 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");
+ stats_event_hidden (NULL, "client_connections", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "source_client_connections", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "source_relay_connections", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "source_total_connections", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "stats_connections", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "listener_connections", "0", STATS_COUNTERS);
+ stats_event_hidden (NULL, "outgoing_kbitrate", "0", STATS_COUNTERS);
}
void stats_shutdown(void)
{
- int n;
-
if(!_stats_running) /* We can't shutdown if we're not running. */
return;
- /* wait for thread to exit */
_stats_running = 0;
- /* wait for other threads to shut down */
- do {
- thread_sleep(300000);
- thread_mutex_lock(&_stats_mutex);
- n = _stats_threads;
- thread_mutex_unlock(&_stats_mutex);
- } while (n > 0);
- INFO0("stats thread finished");
-
- /* free the queues */
-
thread_mutex_destroy(&_stats_mutex);
avl_tree_free(_stats.source_tree, _free_source_stats);
avl_tree_free(_stats.global_tree, _free_stats);
}
-stats_t *stats_get_stats(void)
-{
- /* lock global stats
-
- copy stats
-
- unlock global stats
-
- return copied stats */
-
- return NULL;
-}
/* simple name=tag stat create/update */
void stats_event(const char *source, const char *name, const char *value)
@@ -259,29 +242,27 @@ void stats_event_conv(const char *mount, const char *name, const char *value, co
/* make stat hidden (non-zero). name can be NULL if it applies to a whole
* source stats tree. */
-void stats_event_hidden (const char *source, const char *name, int hidden)
+void stats_event_hidden (const char *source, const char *name, const char *value, int hidden)
{
- const char *str = NULL;
stats_event_t event;
- if (hidden)
- str = "";
- build_event (&event, source, name, NULL);
- event.action = STATS_EVENT_HIDDEN;
+ build_event (&event, source, name, value);
+ event.hidden = hidden;
+ event.action |= STATS_EVENT_HIDDEN;
process_event (&event);
}
/* printf style formatting for stat create/update */
void stats_event_args(const char *source, char *name, char *format, ...)
{
- char buf[1024];
va_list val;
int ret;
+ char buf[1024];
if (name == NULL)
return;
va_start(val, format);
- ret = vsnprintf(buf, 1024, format, val);
+ ret = vsnprintf(buf, sizeof (buf), format, val);
va_end(val);
if (ret < 0 || (unsigned int)ret >= sizeof (buf))
@@ -420,38 +401,16 @@ static stats_source_t *_find_source(avl_tree *source_tree, const char *source)
return NULL;
}
-static stats_event_t *_copy_event(stats_event_t *event)
-{
- stats_event_t *copy = (stats_event_t *)calloc(1, sizeof(stats_event_t));
- if (event->source)
- copy->source = (char *)strdup(event->source);
- else
- copy->source = NULL;
- if (event->name)
- copy->name = (char *)strdup(event->name);
- if (event->value)
- copy->value = (char *)strdup(event->value);
- else
- copy->value = NULL;
- copy->hidden = event->hidden;
- copy->next = NULL;
-
- return copy;
-}
-
/* helper to apply specialised changes to a stats node */
static void modify_node_event (stats_node_t *node, stats_event_t *event)
{
if (node == NULL || event == NULL)
return;
- if (event->action == STATS_EVENT_HIDDEN)
+ if (event->action & STATS_EVENT_HIDDEN)
{
- if (event->value)
- node->hidden = 1;
- else
- node->hidden = 0;
- return;
+ node->hidden = event->hidden;
+ event->action &= ~STATS_EVENT_HIDDEN;
}
if (event->action != STATS_EVENT_SET)
{
@@ -483,7 +442,7 @@ static void modify_node_event (stats_node_t *node, stats_event_t *event)
static void process_global_event (stats_event_t *event)
{
- stats_node_t *node;
+ stats_node_t *node = NULL;
/* DEBUG3("global event %s %s %d", event->name, event->value, event->action); */
if (event->action == STATS_EVENT_REMOVE)
@@ -491,16 +450,18 @@ static void process_global_event (stats_event_t *event)
/* we're deleting */
node = _find_node(_stats.global_tree, event->name);
if (node != NULL)
+ {
+ stats_listener_send (node->hidden, "DELETE global %s\n", event->name);
avl_delete(_stats.global_tree, (void *)node, _free_stats);
+ }
return;
}
node = _find_node(_stats.global_tree, event->name);
if (node)
{
modify_node_event (node, event);
- DEBUG3 ("update node on %s \"%s\" (%s)",
- event->source ? event->source : "global",
- node->name, node->value);
+ stats_listener_send (node->hidden, "EVENT global %s %s\n", node->name, node->value);
+ DEBUG2 ("update node on global \"%s\" (%s)", node->name, node->value);
}
else
{
@@ -508,8 +469,10 @@ static void process_global_event (stats_event_t *event)
node = (stats_node_t *)calloc(1, sizeof(stats_node_t));
node->name = (char *)strdup(event->name);
node->value = (char *)strdup(event->value);
+ node->hidden = event->hidden;
avl_insert(_stats.global_tree, (void *)node);
+ stats_listener_send (node->hidden, "EVENT global %s %s\n", event->name, event->value);
}
}
@@ -517,6 +480,8 @@ static void process_global_event (stats_event_t *event)
static void process_source_event (stats_event_t *event)
{
stats_source_t *snode = _find_source(_stats.source_tree, event->source);
+ stats_node_t *node = NULL;
+
if (snode == NULL)
{
if (event->action == STATS_EVENT_REMOVE)
@@ -527,16 +492,13 @@ static void process_source_event (stats_event_t *event)
DEBUG1 ("new source stat %s", event->source);
snode->source = (char *)strdup(event->source);
snode->stats_tree = avl_tree_new(_compare_stats, NULL);
- if (event->action == STATS_EVENT_HIDDEN)
- snode->hidden = 1;
- else
- snode->hidden = 0;
+ snode->hidden = STATS_SLAVE|STATS_GENERAL|STATS_HIDDEN;
avl_insert(_stats.source_tree, (void *)snode);
}
if (event->name)
{
- stats_node_t *node = _find_node(snode->stats_tree, event->name);
+ node = _find_node (snode->stats_tree, event->name);
if (node == NULL)
{
if (event->action == STATS_EVENT_REMOVE)
@@ -548,8 +510,11 @@ static void process_source_event (stats_event_t *event)
node = (stats_node_t *)calloc(1,sizeof(stats_node_t));
node->name = (char *)strdup(event->name);
node->value = (char *)strdup(event->value);
- node->hidden = snode->hidden;
+ node->hidden = event->hidden;
+ if (snode->hidden | STATS_HIDDEN)
+ node->hidden |= STATS_HIDDEN;
+ stats_listener_send (node->hidden, "EVENT %s %s %s\n", event->source, event->name, event->value);
avl_insert(snode->stats_tree, (void *)node);
}
return;
@@ -557,24 +522,42 @@ static void process_source_event (stats_event_t *event)
if (event->action == STATS_EVENT_REMOVE)
{
DEBUG1 ("delete node %s", event->name);
+ stats_listener_send (node->hidden, "DELETE %s %s\n", event->source, event->name);
avl_delete(snode->stats_tree, (void *)node, _free_stats);
return;
}
modify_node_event (node, event);
return;
}
- if (event->action == STATS_EVENT_HIDDEN)
+ /* change source hidden status */
+ if (event->action & STATS_EVENT_HIDDEN)
{
avl_node *node = avl_get_first (snode->stats_tree);
+ int visible = 0;
- if (event->value)
- snode->hidden = 1;
+ if ((event->hidden&STATS_HIDDEN) == (snode->hidden&STATS_HIDDEN))
+ return;
+ if (snode->hidden & STATS_HIDDEN)
+ {
+ snode->hidden &= ~STATS_HIDDEN;
+ stats_listener_send (snode->hidden, "NEW %s\n", snode->source);
+ visible = 1;
+ }
else
- snode->hidden = 0;
+ {
+ stats_listener_send (snode->hidden, "DELETE %s\n", snode->source);
+ snode->hidden |= STATS_HIDDEN;
+ }
while (node)
{
stats_node_t *stats = (stats_node_t*)node->key;
- stats->hidden = snode->hidden;
+ if (visible)
+ {
+ stats->hidden &= ~STATS_HIDDEN;
+ stats_listener_send (stats->hidden, "EVENT %s %s %s\n", snode->source, stats->name, stats->value);
+ }
+ else
+ stats->hidden |= STATS_HIDDEN;
node = avl_get_next (node);
}
return;
@@ -582,6 +565,7 @@ static void process_source_event (stats_event_t *event)
if (event->action == STATS_EVENT_REMOVE)
{
DEBUG1 ("delete source node %s", event->source);
+ stats_listener_send (snode->hidden, "DELETE %s\n", event->source);
avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
}
}
@@ -595,39 +579,98 @@ void stats_event_time (const char *mount, const char *name)
localtime_r (&now, &local);
strftime (buffer, sizeof (buffer), ICECAST_TIME_FMT, &local);
- stats_event (mount, name, buffer);
+ stats_event_hidden (mount, name, buffer, STATS_GENERAL);
}
-void stats_listener_send (stats_event_t *event)
+
+static void stats_listeners_send (event_listener_t *listener)
{
- event_listener_t *listener = (event_listener_t *)_event_listeners;
+ int loop = 6;
+ int ret;
+ client_t *client = listener->client;
+
+ while (loop)
+ {
+ if (format_advance_queue (NULL, client) < 0)
+ break;
+ ret = format_generic_write_to_client (client);
+ if (ret < 0)
+ break;
+ listener->content_len -= ret;
+ loop--;
+ }
+}
+
+static void clear_stats_queue (event_listener_t *listener)
+{
+ while (listener->queue && listener->queue->_count == 1)
+ {
+ refbuf_t *to_go = listener->queue;
+ listener->queue = to_go->next;
+ refbuf_release (to_go);
+ }
+}
+
+
+static void stats_listener_send (int hidden_level, const char *fmt, ...)
+{
+ va_list ap;
+ event_listener_t *listener = _stats.event_listeners,
+ **trail = &_stats.event_listeners;
+
+ va_start(ap, fmt);
while (listener)
{
- int send_it = 1;
+ client_t *client = listener->client;
- if (event->source && listener->source &&
- strcmp (event->source, listener->source) != 0)
- send_it = 0;
-
- if (send_it)
+ if (listener->hidden_level & hidden_level)
{
- stats_event_t *copy = _copy_event(event);
- thread_mutex_lock (&listener->mutex);
- _add_event_to_queue (copy, &listener->queue);
- thread_mutex_unlock (&listener->mutex);
+ _add_stats_to_stats_client (listener, fmt, ap);
}
+ stats_listeners_send (listener);
+ if (client->con->error || listener->content_len > 10000)
+ {
+ stats_event_t stats_count;
+ char buffer [20];
+ *trail = listener->next;
+
+ build_event (&stats_count, NULL, "stats_connections", buffer);
+ stats_count.action = STATS_EVENT_DEC;
+ process_event_unlocked (&stats_count);
+
+ /* moved this listener so that final cleanup can be done outside of lock */
+ listener->next = _stats.listeners_removed;
+ _stats.listeners_removed = listener;
+
+ listener = *trail;
+ continue;
+ }
+ /* reduce queue if unsued */
+ clear_stats_queue (listener);
+
+ trail = &listener->next;
listener = listener->next;
}
+ va_end(ap);
}
void stats_global (ice_config_t *config)
{
- stats_event (NULL, "server_id", config->server_id);
- stats_event (NULL, "host", config->hostname);
+ stats_event_hidden (NULL, "server_id", config->server_id, STATS_GENERAL);
+ stats_event_hidden (NULL, "host", config->hostname, STATS_GENERAL);
stats_event (NULL, "location", config->location);
stats_event (NULL, "admin", config->admin);
+#if 0
+ /* restart a master stats connection */
+ config->master = calloc (1, sizeof ice_master_details);
+ config->master->hostname = xmlCharStrdup ("127.0.0.1");
+ config->master->port = 8000;
+ config->master->username = xmlCharStrdup ("relay");
+ config->master->password = xmlCharStrdup ("relayme");
+ _stats.sock = sock_connect_wto_bind (server, port, bind, 10);
+#endif
}
static void process_event_unlocked (stats_event_t *event)
@@ -637,10 +680,6 @@ static void process_event_unlocked (stats_event_t *event)
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 */
- stats_listener_send (event);
}
static void process_event (stats_event_t *event)
@@ -652,300 +691,230 @@ static void process_event (stats_event_t *event)
thread_mutex_unlock (&_stats_mutex);
}
-/* you must have the _stats_mutex locked here */
-static void _unregister_listener(event_listener_t *listener)
+
+static int _append_to_bufferv (refbuf_t *refbuf, int max_len, const char *fmt, va_list ap)
{
- event_listener_t **prev = (event_listener_t **)&_event_listeners,
- *current = *prev;
- stats_event_t stats_count, *event;
- char buffer [VAL_BUFSIZE];
+ char *buf = (char*)refbuf->data + refbuf->len;
+ int len = max_len - refbuf->len;
+ int ret;
+ va_list vl;
- while (current)
- {
- if (current == listener)
- {
- *prev = current->next;
- break;
- }
- prev = ¤t->next;
- current = *prev;
- }
-
- /* remove this listener before sending this change */
- build_event (&stats_count, NULL, "stats_connections", buffer);
- stats_count.action = STATS_EVENT_DEC;
- process_event_unlocked (&stats_count);
-
- /* flush any extra that sneaked on at the last moment */
- while ((event = _get_event_from_queue (&listener->queue)) != NULL)
- _free_event (event);
-}
-
-
-static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
-{
- stats_event_t *event = (stats_event_t *)malloc(sizeof(stats_event_t));
-
- if (source != NULL)
- event->source = (char *)strdup(source);
- else
- event->source = NULL;
- event->name = (char *)strdup(node->name);
- event->value = (char *)strdup(node->value);
- event->hidden = node->hidden;
- event->action = STATS_EVENT_SET;
- event->next = NULL;
-
- return event;
-}
-
-
-static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue)
-{
- *queue->tail = event;
- queue->tail = (volatile stats_event_t **)&event->next;
-}
-
-
-static stats_event_t *_get_event_from_queue (event_queue_t *queue)
-{
- stats_event_t *event = NULL;
-
- if (queue && queue->head)
- {
- event = (stats_event_t *)queue->head;
- queue->head = event->next;
- if (queue->head == NULL)
- queue->tail = &queue->head;
- }
-
- return event;
-}
-
-static int _send_event_to_client(stats_event_t *event, client_t *client)
-{
- int len;
- char buf [200];
-
- /* send data to the client!!!! */
- len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
- (event->source != NULL) ? event->source : "global",
- event->name ? event->name : "null",
- event->value ? event->value : "null");
- if (len > 0 && len < (int)sizeof (buf))
- {
- client_send_bytes (client, buf, len);
- if (client->con->error)
- return -1;
- }
+ va_copy (vl, ap);
+ ret = vsnprintf (buf, len, fmt, vl);
+ if (ret < 0 || ret >= len)
+ return -1;
+ refbuf->len += ret;
return 0;
}
-void _dump_stats_to_queue (event_queue_t *queue)
+static int _append_to_buffer (refbuf_t *refbuf, int max_len, const char *fmt, ...)
{
- avl_node *node;
- avl_node *node2;
- stats_event_t *event;
- stats_source_t *source;
+ int ret;
+ va_list va;
+
+ va_start (va, fmt);
+ ret = _append_to_bufferv (refbuf, max_len, fmt, va);
+ va_end(va);
+ return ret;
+}
+
+
+static void _add_node_to_stats_client (event_listener_t *listener, refbuf_t *refbuf)
+{
+ *listener->queue_recent_p = refbuf;
+ listener->queue_recent_p = &refbuf->next;
+ listener->content_len += refbuf->len;
+}
+
+
+static void _add_stats_to_stats_client (event_listener_t *listener,const char *fmt, va_list ap)
+{
+ unsigned int size = 50;
+ while (size < 300)
+ {
+ refbuf_t *refbuf = refbuf_new (size);
+ refbuf->len = 0;
+
+ if (_append_to_bufferv (refbuf, size, fmt, ap) == 0)
+ {
+ _add_node_to_stats_client (listener, refbuf);
+ return;
+ }
+ refbuf_release (refbuf);
+ size += 100;
+ }
+}
+
+
+static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden)
+{
+ avl_node *avlnode;
+ xmlNodePtr ret = NULL;
thread_mutex_lock(&_stats_mutex);
- /* first we fill our queue with the current stats */
- /* start with the global stats */
- node = avl_get_first(_stats.global_tree);
- while (node) {
- event = _make_event_from_node((stats_node_t *)node->key, NULL);
- _add_event_to_queue(event, queue);
-
- node = avl_get_next(node);
+ /* general stats first */
+ avlnode = avl_get_first(_stats.global_tree);
+ while (avlnode)
+ {
+ stats_node_t *stat = avlnode->key;
+ if (stat->hidden & hidden)
+ xmlNewTextChild (root, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
+ avlnode = avl_get_next (avlnode);
}
+ /* now per mount stats */
+ avlnode = avl_get_first(_stats.source_tree);
+ while (avlnode)
+ {
+ stats_source_t *source = (stats_source_t *)avlnode->key;
+ if (source->hidden & hidden &&
+ (show_mount == NULL || strcmp (show_mount, source->source) == 0))
+ {
+ avl_node *avlnode2 = avl_get_first (source->stats_tree);
+ xmlNodePtr xmlnode = xmlNewTextChild (root, NULL, XMLSTR("source"), NULL);
- /* now the stats for each source */
- node = avl_get_first(_stats.source_tree);
- while (node) {
- source = (stats_source_t *)node->key;
- node2 = avl_get_first(source->stats_tree);
- while (node2) {
- event = _make_event_from_node((stats_node_t *)node2->key, source->source);
- _add_event_to_queue(event, queue);
-
- node2 = avl_get_next(node2);
+ xmlSetProp (xmlnode, XMLSTR("mount"), XMLSTR(source->source));
+ if (ret == NULL)
+ ret = xmlnode;
+ while (avlnode2)
+ {
+ stats_node_t *stat = avlnode2->key;
+ xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
+ avlnode2 = avl_get_next (avlnode2);
+ }
}
-
- node = avl_get_next(node);
+ avlnode = avl_get_next (avlnode);
}
thread_mutex_unlock(&_stats_mutex);
+ return ret;
}
+
/* factoring out code for stats loops
** this function copies all stats to queue, and registers
-** the queue for all new events atomically.
-** note: mutex must already be created!
*/
static void _register_listener (event_listener_t *listener)
{
avl_node *node;
- avl_node *node2;
- stats_event_t *event;
- stats_source_t *source;
stats_event_t stats_count;
+ refbuf_t *refbuf;
+ size_t size = 8192;
char buffer[20];
build_event (&stats_count, NULL, "stats_connections", buffer);
stats_count.action = STATS_EVENT_INC;
process_event_unlocked (&stats_count);
- /* first we fill our queue with the current stats */
-
- /* start with the global stats */
- node = avl_get_first(_stats.global_tree);
- while (node) {
- event = _make_event_from_node((stats_node_t *)node->key, NULL);
- _add_event_to_queue (event, &listener->queue);
-
- node = avl_get_next(node);
- }
-
- /* now the stats for each source */
- node = avl_get_first(_stats.source_tree);
- while (node) {
- source = (stats_source_t *)node->key;
- node2 = avl_get_first(source->stats_tree);
- while (node2) {
- event = _make_event_from_node((stats_node_t *)node2->key, source->source);
- _add_event_to_queue (event, &listener->queue);
-
- node2 = avl_get_next(node2);
- }
-
- node = avl_get_next(node);
- }
-
- /* now we register to receive future event notices */
- listener->next = (event_listener_t *)_event_listeners;
- _event_listeners = listener;
-}
-
-static void check_uri (event_listener_t *listener, client_t *client)
-{
- const char *mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
- if (strcmp (mount, "/") != 0)
- listener->source = strdup (mount);
-}
-
-
-void *stats_connection(void *arg)
-{
- client_t *client = (client_t *)arg;
- stats_event_t *event;
- event_listener_t listener;
-
- INFO0 ("stats client starting");
-
- memset (&listener, 0, sizeof (listener));
- event_queue_init (&listener.queue);
- check_uri (&listener, client);
-
- /* increment the thread count */
- thread_mutex_lock(&_stats_mutex);
- _stats_threads++;
- thread_mutex_create("stats local event", &listener.mutex);
-
- _register_listener (&listener);
- thread_mutex_unlock(&_stats_mutex);
-
- while (_stats_running) {
- thread_mutex_lock (&listener.mutex);
- event = _get_event_from_queue (&listener.queue);
- thread_mutex_unlock (&listener.mutex);
- if (event != NULL) {
- if (_send_event_to_client(event, client) < 0) {
- _free_event(event);
- break;
- }
- _free_event(event);
- continue;
- }
- thread_sleep (500000);
- }
-
- thread_mutex_lock(&_stats_mutex);
- _unregister_listener (&listener);
- _stats_threads--;
- thread_mutex_unlock(&_stats_mutex);
-
- thread_mutex_destroy (&listener.mutex);
- free (listener.source);
- client_destroy (client);
- INFO0 ("stats client finished");
-
- return NULL;
-}
-
-
-void stats_callback (client_t *client, void *notused)
-{
- if (client->con->error)
+ while (size < 50000) /* use a large limit */
{
- client_destroy (client);
- return;
- }
- client_set_queue (client, NULL);
- thread_create("Stats Connection", stats_connection, (void *)client, THREAD_DETACHED);
-}
+ /* first we fill our queue with the current stats */
+ refbuf = refbuf_new (size);
+ refbuf->len = 0;
-
-typedef struct _source_xml_tag {
- char *mount;
- xmlNodePtr node;
-
- struct _source_xml_tag *next;
-} source_xml_t;
-
-static xmlNodePtr _find_xml_node(const char *mount, source_xml_t **list, xmlNodePtr root)
-{
- source_xml_t *node, *node2;
- int found = 0;
-
- /* search for existing node */
- node = *list;
- while (node) {
- if (strcmp(node->mount, mount) == 0) {
- found = 1;
+ /* starts with the http response header */
+ if (_append_to_buffer (refbuf, size, "HTTP/1.0 200 OK\r\ncapability: streamlist\r\n\r\n") < 0)
+ {
+ refbuf_release (refbuf);
break;
}
- node = node->next;
+ /* now the global stats */
+ node = avl_get_first(_stats.global_tree);
+ while (node)
+ {
+ stats_node_t *stat = node->key;
+
+ node = avl_get_next(node);
+ if ((stat->hidden & listener->hidden_level) == 0)
+ continue;
+ if (_append_to_buffer (refbuf, size, "EVENT global %s %s\n", stat->name, stat->value) < 0)
+ {
+ size += 8192;
+ refbuf_release (refbuf);
+ break;
+ }
+ }
+ if (node) continue; /* catch buffer full case */
+
+ /* now the stats for each source */
+ node = avl_get_first(_stats.source_tree);
+ while (node)
+ {
+ avl_node *node2;
+ stats_source_t *snode = (stats_source_t *)node->key;
+ node = avl_get_next(node);
+ if ((snode->hidden & listener->hidden_level) == 0)
+ continue;
+ if (_append_to_buffer (refbuf, size, "NEW %s\n", snode->source) < 0)
+ {
+ size += 8192;
+ refbuf_release (refbuf);
+ break;
+ }
+ node2 = avl_get_first(snode->stats_tree);
+ while (node2)
+ {
+ stats_node_t *stat = node2->key;
+ node2 = avl_get_next(node2);
+
+ if ((stat->hidden & listener->hidden_level) == 0)
+ continue;
+ if (_append_to_buffer (refbuf, size, "EVENT %s %s %s\n", snode->source, stat->name, stat->value) < 0)
+ {
+ size += 8192;
+ refbuf_release (refbuf);
+ break;
+ }
+ }
+ }
+ if (node) continue; /* catch buffer full case */
+ break;
}
- if (found) return node->node;
+ client_set_queue (listener->client, refbuf);
+ _add_node_to_stats_client (listener, refbuf);
- /* if we didn't find it, we must build it and add it to the list */
-
- /* build node */
- node = (source_xml_t *)malloc(sizeof(source_xml_t));
- node->mount = strdup(mount);
- node->node = xmlNewChild(root, NULL, XMLSTR("source"), NULL);
- xmlSetProp(node->node, XMLSTR("mount"), XMLSTR(mount));
- node->next = NULL;
-
- /* add node */
- if (*list == NULL) {
- *list = node;
- } else {
- node2 = *list;
- while (node2->next) node2 = node2->next;
- node2->next = node;
- }
-
- return node->node;
+ /* now we register to receive future event notices */
+ listener->next = _stats.event_listeners;
+ _stats.event_listeners = listener;
}
+static void check_uri (event_listener_t *listener, const char *mount)
+{
+ listener->hidden_level = STATS_PUBLIC;
+ if (strncmp (mount, "/admin/", 7) == 0)
+ {
+ if (strcmp (mount+7, "streams") == 0)
+ listener->hidden_level = STATS_SLAVE|STATS_GENERAL;
+ }
+ /* else
+ * check for reserved mountpoint, show hidden source stats */
+}
+
+
+
+void stats_callback (client_t *client, void *arg)
+{
+ event_listener_t * listener;
+ const char *mount = arg;
+
+ client_set_queue (client, NULL);
+ listener = calloc (1, sizeof (event_listener_t));
+ listener->client = client;
+ listener->queue_recent_p = &listener->queue;
+ check_uri (listener, mount);
+
+ thread_mutex_lock(&_stats_mutex);
+ _register_listener (listener);
+ thread_mutex_unlock(&_stats_mutex);
+}
+
+
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, mount);
+ stats_get_xml(&doc, STATS_PUBLIC, mount);
xslt_transform(doc, xslpath, client);
@@ -953,71 +922,28 @@ void stats_transform_xslt(client_t *client, const char *uri)
free (xslpath);
}
-void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount)
+void stats_get_xml(xmlDocPtr *doc, int hidden, const char *show_mount)
{
- stats_event_t *event;
- event_queue_t queue;
- xmlNodePtr node, srcnode;
- source_xml_t *src_nodes = NULL;
- source_xml_t *next;
-
- event_queue_init (&queue);
- _dump_stats_to_queue (&queue);
+ xmlNodePtr node;
*doc = xmlNewDoc (XMLSTR("1.0"));
node = xmlNewDocNode (*doc, NULL, XMLSTR("icestats"), NULL);
xmlDocSetRootElement(*doc, node);
- event = _get_event_from_queue(&queue);
- while (event)
- {
- if (event->hidden <= show_hidden)
- {
- 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;
+ node = _dump_stats_to_doc (node, show_mount, hidden);
- 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_mount)
- {
+ if (show_mount && node)
+ {
+ source_t *source;
/* show each listener */
- source_xml_t *src = src_nodes;
avl_tree_rlock (global.source_tree);
- while (src)
- {
- source_t *source = source_find_mount_raw (src->mount);
+ source = source_find_mount_raw (show_mount);
- if (source)
- admin_source_listeners (source, src->node);
+ if (source)
+ admin_source_listeners (source, node);
- src = src->next;
- }
avl_tree_unlock (global.source_tree);
}
- while (src_nodes) {
- next = src_nodes->next;
- free(src_nodes->mount);
- free(src_nodes);
- src_nodes = next;
- }
}
static int _compare_stats(void *arg, void *a, void *b)
@@ -1056,15 +982,6 @@ static int _free_source_stats(void *key)
return 1;
}
-static void _free_event(stats_event_t *event)
-{
- if (event->source) free(event->source);
- if (event->name) free(event->name);
- if (event->value) free(event->value);
- free(event);
-}
-
-
/* return a list of blocks which contain lines of text. Each line is a mountpoint
* reference that a slave will use for relaying. The prepend setting is to indicate
@@ -1149,19 +1066,35 @@ void stats_clear_virtual_mounts (void)
void stats_global_calc (void)
{
+ event_listener_t *listener;
+ static time_t next_update = 0;
stats_event_t event;
char buffer [VAL_BUFSIZE];
- static time_t next_update = 0;
if (global.time < next_update)
return;
- next_update = global.time + 1;
+
+ next_update = global.time + 2;
build_event (&event, NULL, "outgoing_kbitrate", buffer);
+ event.hidden = STATS_COUNTERS|STATS_HIDDEN;
thread_mutex_lock (&_stats_mutex);
snprintf (buffer, sizeof(buffer), "%" PRIu64,
- (int64_t)(global_getrate_avg (global.out_bitrate) * 8 / 1000.0 + 0.5));
+ (int64_t)global_getrate_avg (global.out_bitrate) * 8 / 1000);
process_event_unlocked (&event);
+ /* retrieve the list of closing down clients */
+ listener = _stats.listeners_removed;
+ _stats.listeners_removed = NULL;
thread_mutex_unlock (&_stats_mutex);
+
+ /* flush out any closed stats clients */
+ while (listener)
+ {
+ event_listener_t *to_go = listener;
+ listener = listener->next;
+ client_destroy (to_go->client);
+ clear_stats_queue (to_go);
+ free (to_go);
+ }
}
diff --git a/src/stats.h b/src/stats.h
index c66ea692..41e60d58 100644
--- a/src/stats.h
+++ b/src/stats.h
@@ -21,36 +21,17 @@
#include
#include
-
-typedef struct _stats_tag
-{
- avl_tree *global_tree;
-
- /* global stats
- start_time
- total_users
- max_users
- total_sources
- max_sources
- total_user_connections
- total_source_connections
- */
-
- avl_tree *source_tree;
-
- /* stats by source, and for stats
- start_time
- total_users
- max_users
- */
-
-} stats_t;
+#define STATS_HIDDEN 1
+#define STATS_SLAVE 2
+#define STATS_GENERAL 4
+#define STATS_COUNTERS 8
+#define STATS_PUBLIC (STATS_GENERAL|STATS_COUNTERS)
+#define STATS_ALL ~0
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);
refbuf_t *stats_get_streams (int prepend);
void stats_clear_virtual_mounts (void);
@@ -63,11 +44,11 @@ void stats_event_inc(const char *source, const char *name);
void stats_event_add(const char *source, const char *name, unsigned long value);
void stats_event_sub(const char *source, const char *name, unsigned long value);
void stats_event_dec(const char *source, const char *name);
-void stats_event_hidden (const char *source, const char *name, int hidden);
+void stats_event_hidden (const char *source, const char *name, const char *value, int hidden);
void stats_event_time (const char *mount, const char *name);
void *stats_connection(void *arg);
-void stats_callback (client_t *client, void *notused);
+void stats_callback (client_t *client, void *mount);
void stats_global_calc(void);
void stats_transform_xslt(client_t *client, const char *uri);
diff --git a/src/util.c b/src/util.c
index 429017f8..80b7b47c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -47,18 +47,18 @@
struct rate_calc_node
{
- time_t time;
+ uint64_t index;
long value;
- struct rate_calc_node *next, *prev;
+ struct rate_calc_node *next;
};
struct rate_calc
{
uint64_t total;
- unsigned int seconds;
- unsigned int blocks;
- unsigned int recalc_total;
struct rate_calc_node *current;
+ unsigned int samples;
+ unsigned int ssec;
+ unsigned int blocks;
};
@@ -695,102 +695,90 @@ char *util_conv_string (const char *string, const char *in_charset, const char *
/* setup a rate block of so many seconds, so that an average can be
* determined of that range
*/
-struct rate_calc *rate_setup (unsigned int seconds)
+struct rate_calc *rate_setup (unsigned int samples, unsigned int ssec)
{
struct rate_calc *calc = calloc (1, sizeof (struct rate_calc));
- struct rate_calc_node *start = NULL;
- unsigned int i;
- if (calc == NULL || seconds == 0)
+ if (calc == NULL || samples < 2 || ssec == 0)
{
free (calc);
return NULL;
}
- for (i=0 ; icurrent)
- {
- calc->current->next = node;
- node->next = start;
- }
- else
- start = node;
- node->prev = calc->current;
- calc->current = node;
- }
- calc->current->next = start;
- start->prev = calc->current;
- calc->seconds = seconds;
+ calc->samples = samples;
+ calc->ssec = ssec;
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.
*/
-void rate_add (struct rate_calc *calc, long value, time_t t)
+void rate_add (struct rate_calc *calc, long value, uint64_t sid)
{
- if (t == 0)
+ if (calc->current == NULL || sid != calc->current->index)
{
- calc->blocks = 0;
- return;
- }
- if (t != calc->current->time)
- {
- if (calc->recalc_total)
+ if (calc->blocks == calc->samples)
{
- /* here we keep the number of blocks and recalculate the total */
+ calc->total += calc->current->value;
calc->current = calc->current->next;
- rate_recalc_total (calc);
- calc->recalc_total--;
+ calc->total -= calc->current->value;
+ calc->current->value = value;
+ calc->current->index = sid;
+ return;
}
else
{
- /* common case */
- calc->total += calc->current->value;
- calc->current = calc->current->next;
- if (calc->blocks == calc->seconds)
- calc->total -= calc->current->value;
+ struct rate_calc_node *node = calloc (1, sizeof (*node));
+ node->index = sid;
+ calc->blocks++;
+ if (calc->current)
+ {
+ node->next = calc->current->next;
+ calc->current->next = node;
+ calc->total += calc->current->value;
+ }
else
- calc->blocks++;
+ {
+ node->next = node;
+ }
+ calc->current = node;
}
- calc->current->value = 0;
- calc->current->time = t;
}
calc->current->value += value;
}
-
/* return the average sample value over all the blocks except the
* current one, as that may be incomplete
*/
long rate_avg (struct rate_calc *calc)
{
+ uint64_t range;
+
if (calc == NULL || calc->blocks < 2)
return 0;
- return (long)(calc->total / (calc->blocks-1));
+ range = (calc->current->index - calc->current->next->index) / calc->ssec;
+ if (range < 1)
+ range = 1;
+ return (long)(calc->total / range);
}
/* reduce the samples used to calculate average */
-void rate_reduce (struct rate_calc *calc, unsigned long count)
+void rate_reduce (struct rate_calc *calc, unsigned int count)
{
- if (calc && count < calc->blocks && count >= 2)
+ if (calc && count < calc->blocks)
{
- calc->recalc_total = count*2;
- calc->blocks = count;
+ struct rate_calc_node *list = calc->current->next;
+ for (; calc->blocks > count; calc->blocks--)
+ {
+ struct rate_calc_node *to_go = list;
+ list = to_go->next;
+ calc->total -= to_go->value;
+ free (to_go);
+ }
+ if (calc->blocks)
+ calc->current->next = list;
+ else
+ calc->current = NULL;
}
}
diff --git a/src/util.h b/src/util.h
index f1453a13..4da6cd33 100644
--- a/src/util.h
+++ b/src/util.h
@@ -58,11 +58,11 @@ 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);
+struct rate_calc *rate_setup (unsigned int samples, unsigned int ssec);
+void rate_add (struct rate_calc *calc, long value, uint64_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);
+void rate_reduce (struct rate_calc *calc, unsigned int count);
int get_line(FILE *file, char *buf, size_t siz);
diff --git a/src/yp.c b/src/yp.c
index 521b225a..7f87e9e4 100644
--- a/src/yp.c
+++ b/src/yp.c
@@ -925,12 +925,10 @@ void yp_remove (const char *mount)
void yp_touch (const char *mount)
{
struct yp_server *server = (struct yp_server *)active_yps;
- time_t trigger;
ypdata_t *search_list = NULL;
thread_rwlock_rlock (&yp_lock);
- /* do update in 3 secs, give stats chance to update */
- trigger = time(NULL) + 3;
+
if (server)
search_list = server->mounts;
@@ -945,9 +943,9 @@ void yp_touch (const char *mount)
search_list = yp->next;
continue;
}
- /* only force if touch */
- if (yp->process == do_yp_touch)
- yp->next_update = trigger;
+ /* don't update the directory if there is a touch scheduled soon */
+ if (yp->process == do_yp_touch && now + yp->touch_interval - yp->next_update > 60)
+ yp->next_update = now;
}
server = server->next;
if (server)
diff --git a/web/status.xsl b/web/status.xsl
index 5e15f7ef..65228396 100755
--- a/web/status.xsl
+++ b/web/status.xsl
@@ -79,6 +79,17 @@
Stream URL: | |
+
+
+
+Preview: | |
+
+
+Preview: | |
+
+
+
+
Current Song: |
- |
|