[xiph-commits] r17198 - in icecast/branches/kh/icecast: . src win32

karl at svn.xiph.org karl at svn.xiph.org
Fri May 7 20:59:17 PDT 2010


Author: karl
Date: 2010-05-07 20:59:17 -0700 (Fri, 07 May 2010)
New Revision: 17198

Modified:
   icecast/branches/kh/icecast/NEWS
   icecast/branches/kh/icecast/config.h.vc6
   icecast/branches/kh/icecast/configure.in
   icecast/branches/kh/icecast/src/admin.c
   icecast/branches/kh/icecast/src/auth.c
   icecast/branches/kh/icecast/src/auth_url.c
   icecast/branches/kh/icecast/src/cfgfile.c
   icecast/branches/kh/icecast/src/cfgfile.h
   icecast/branches/kh/icecast/src/client.c
   icecast/branches/kh/icecast/src/connection.c
   icecast/branches/kh/icecast/src/connection.h
   icecast/branches/kh/icecast/src/format_mp3.c
   icecast/branches/kh/icecast/src/fserve.c
   icecast/branches/kh/icecast/src/fserve.h
   icecast/branches/kh/icecast/src/global.c
   icecast/branches/kh/icecast/src/global.h
   icecast/branches/kh/icecast/src/main.c
   icecast/branches/kh/icecast/src/mpeg.c
   icecast/branches/kh/icecast/src/mpeg.h
   icecast/branches/kh/icecast/src/slave.c
   icecast/branches/kh/icecast/src/source.c
   icecast/branches/kh/icecast/src/stats.c
   icecast/branches/kh/icecast/src/xslt.c
   icecast/branches/kh/icecast/win32/icecast2.iss
Log:
kh23.  mostly fixes. The new feature is the automatic banning of IPs if certain
mountpoints are accessed (useful for those /mount/index.html).  The rest is
mainly pointer corruption fixes for relay updating/restarting/removal, stream
parsing for non-ogg streams and stats updating.



Modified: icecast/branches/kh/icecast/NEWS
===================================================================
--- icecast/branches/kh/icecast/NEWS	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/NEWS	2010-05-08 03:59:17 UTC (rev 17198)
@@ -16,6 +16,23 @@
 any extra tags are show in the conf/icecast.xml.dist file
 
 
+2.3.2-kh23
+. if an inactive on-demand relay detects listeners on fallback then start relay
+  but do not move listeners until the connection is complete.
+. fix race condition with restarting non-ogg relays and stats being generated
+. make auth queue limit 150 before rejection.
+. A number of relay related fixes. these typically occur when a relay is either
+  restarted or was removed (eg disappeared from a master).  There is also a
+  possible case of long held locking that is now gone.
+. add <ban-client>N</ban-client> mount option to add client IP to the internal
+  banned list for N seconds. subsequent attempts that have not timed out extend
+  the ban period. A stat count of banned entries is produced each second
+. reload from admin page was not restarting listen thread if signalfd used.
+. increase default queue block size on non-ogg streams to 2900
+. mpeg parsing fix for odd cases. This could appear as corrupt or stalled streams
+. fix listing of stats and listener details on fallback files
+. fix Location header reference from auth url
+
 2.3.2-kh22
 . Add mp3/aac sync marker alignment code. This makes the internal buffers contain
   whole frames. allows for better transistion when moving listeners.

Modified: icecast/branches/kh/icecast/config.h.vc6
===================================================================
--- icecast/branches/kh/icecast/config.h.vc6	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/config.h.vc6	2010-05-08 03:59:17 UTC (rev 17198)
@@ -95,7 +95,7 @@
 #define PACKAGE_NAME "Icecast"
 
 /* Version number of package */
-#define VERSION "2.3.2-kh22"
+#define VERSION "2.3.2-kh23"
 
 /* Define to the version of this package. */
 #define PACKAGE_VERSION VERSION

Modified: icecast/branches/kh/icecast/configure.in
===================================================================
--- icecast/branches/kh/icecast/configure.in	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/configure.in	2010-05-08 03:59:17 UTC (rev 17198)
@@ -1,4 +1,4 @@
-AC_INIT([Icecast], [2.3.2-kh22], [karl at xiph.org])
+AC_INIT([Icecast], [2.3.2-kh23], [karl at xiph.org])
 
 LT_INIT
 AC_PREREQ(2.59)

Modified: icecast/branches/kh/icecast/src/admin.c
===================================================================
--- icecast/branches/kh/icecast/src/admin.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/admin.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -286,7 +286,7 @@
         avl_tree_unlock(global.source_tree);
         if (strncmp (cmd->request, "stats", 5) == 0)
         {
-            fserve_list_clients (client, mount, cmd->response, 0);
+            command_stats (client, uri);
             return;
         }
         if (strncmp (cmd->request, "listclients", 11) == 0)
@@ -511,6 +511,10 @@
     }
     if (strcmp (function, "updatecfg") == 0)
     {
+#ifdef HAVE_SIGNALFD
+        connection_running = 0;
+        connection_close_sigfd();
+#endif
         global . schedule_config_reread = 1;
         snprintf (buf, len, "Requesting reread of configuration file");
         return 0;

Modified: icecast/branches/kh/icecast/src/auth.c
===================================================================
--- icecast/branches/kh/icecast/src/auth.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/auth.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -242,7 +242,7 @@
         }
     }
     if (auth_postprocess_listener (auth_user) < 0)
-        INFO0 ("listener connection failed");
+        DEBUG0 ("listener connection failed");
 }
 
 
@@ -535,8 +535,10 @@
     {
         if (mountinfo->skip_accesslog)
             client->flags |= CLIENT_SKIP_ACCESSLOG;
-        if (mountinfo->no_mount)
+        if (mountinfo->ban_client || mountinfo->no_mount)
         {
+            if (mountinfo->ban_client)
+                connection_add_banned_ip (client->connection.ip, mountinfo->ban_client);
             config_release_config ();
             client_send_403 (client, "mountpoint unavailable");
             return;
@@ -546,7 +548,7 @@
     {
         auth_client *auth_user;
 
-        if (mountinfo->auth->running == 0 || mountinfo->auth->pending_count > 1000)
+        if (mountinfo->auth->running == 0 || mountinfo->auth->pending_count > 150)
         {
             config_release_config ();
             WARN0 ("too many clients awaiting authentication");

Modified: icecast/branches/kh/icecast/src/auth_url.c
===================================================================
--- icecast/branches/kh/icecast/src/auth_url.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/auth_url.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -439,7 +439,7 @@
     }
     if (atd->location)
     {
-        client_send_302 (client, atd->location+10);
+        client_send_302 (client, atd->location);
         auth_user->client = NULL;
         free (atd->location);
         atd->location = NULL;

Modified: icecast/branches/kh/icecast/src/cfgfile.c
===================================================================
--- icecast/branches/kh/icecast/src/cfgfile.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/cfgfile.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -791,6 +791,7 @@
         { "allow-url-ogg-metadata",
                                 config_get_bool,    &mount->url_ogg_meta },
         { "no-mount",           config_get_bool,    &mount->no_mount },
+        { "ban-client",         config_get_int,     &mount->ban_client },
         { "hidden",             config_get_bool,    &mount->hidden },
         { "authentication",     auth_get_authenticator, &mount->auth },
         { "on-connect",         config_get_str,     &mount->on_connect },
@@ -841,7 +842,7 @@
     if (mount->url_ogg_meta)
         mount->ogg_passthrough = 0;
     if (mount->queue_block_size < 100)
-        mount->queue_block_size = 1400;
+        mount->queue_block_size = 2900;
 
     mount->next = config->mounts;
     config->mounts = mount;

Modified: icecast/branches/kh/icecast/src/cfgfile.h
===================================================================
--- icecast/branches/kh/icecast/src/cfgfile.h	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/cfgfile.h	2010-05-08 03:59:17 UTC (rev 17198)
@@ -98,6 +98,7 @@
 
     int fallback_override; /* When this source arrives, do we steal back
                               clients from the fallback? */
+    int ban_client;     /* do we add a client on this to the ban list automatically */
     int no_mount; /* Do we permit direct requests of this mountpoint? (or only
                      indirect, through fallbacks) */
     int burst_size; /* amount to send to a new client if possible, -1 take

Modified: icecast/branches/kh/icecast/src/client.c
===================================================================
--- icecast/branches/kh/icecast/src/client.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/client.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -55,7 +55,7 @@
     if (sock != SOCK_ERROR)
     {
         refbuf_t *r;
-        if (connection_init (&client->connection, sock) < 0)
+        if (connection_init (&client->connection, sock, NULL) < 0)
         {
             free (client);
             return NULL;

Modified: icecast/branches/kh/icecast/src/connection.c
===================================================================
--- icecast/branches/kh/icecast/src/connection.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/connection.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -89,13 +89,20 @@
 static int  _handle_source_request (client_t *client);
 static int  _handle_stats_request (client_t *client);
 
+struct banned_entry
+{
+    char ip[16]; // may want to expand later for ipv6
+    time_t timeout;
+};
 
 typedef struct
 {
-    char *filename;
     time_t file_recheck;
     time_t file_mtime;
     avl_tree *contents;
+    int  (*compare)(void *arg, void *a, void *b);
+    void (*add_new_entry)(avl_tree *t, const char *ip, time_t now);
+    char *filename;
 } cache_file_contents;
 
 static spin_t _connection_lock;
@@ -141,13 +148,15 @@
 
 /* filtering client connection based on IP */
 cache_file_contents banned_ip, allowed_ip;
+struct banned_entry *ban_entry_removal;
+
 /* filtering listener connection based on useragent */
 cache_file_contents useragents;
 
 int connection_running = 0;
 
 
-static int compare_line (void *arg, void *a, void *b)
+static int compare_pattern (void *arg, void *a, void *b)
 {
     const char *value = (const char *)a;
     const char *pattern = (const char *)b;
@@ -170,6 +179,15 @@
 #endif
 }
 
+static int compare_banned_ip (void *arg, void *a, void *b)
+{
+    struct banned_entry *this = (struct banned_entry *)a;
+    struct banned_entry *that = (struct banned_entry *)b;
+    if (that->timeout)
+        if (ban_entry_removal == NULL || that->timeout < ban_entry_removal->timeout)
+            ban_entry_removal = that; // identify possible removal
+    return compare_pattern (NULL, &this->ip[0] , &that->ip[0]);
+}
 
 static int free_filtered_line (void*x)
 {
@@ -320,7 +338,42 @@
     return bytes;
 }
 
+static void add_generic_text (avl_tree *t, const char *ip, time_t now)
+{
+    char *str = strdup (ip);
+    if (str)
+        avl_insert (t, str);
+}
 
+static void add_banned_ip (avl_tree *t, const char *ip, time_t now)
+{
+    struct banned_entry *entry = calloc (1, sizeof (struct banned_entry));
+    snprintf (&entry->ip[0], sizeof (entry->ip), "%s", ip);
+    if (now)
+        entry->timeout = now;
+    avl_insert (t, entry);
+}
+
+void connection_add_banned_ip (const char *ip, int duration)
+{
+    time_t timeout = -1;
+    if (duration > 0)
+        timeout = time(NULL) + duration;
+
+    if (banned_ip.contents)
+    {
+        global_lock();
+        add_banned_ip (banned_ip.contents, ip, timeout);
+        global_unlock();
+    }
+}
+
+void connection_stats (void)
+{
+    if (banned_ip.contents)
+        stats_event_args (NULL, "banned_IPs", "%ld", (long)banned_ip.contents->length);
+}
+
 /* function to handle the re-populating of the avl tree containing IP addresses
  * for deciding whether a connection of an incoming request is to be dropped.
  */
@@ -361,17 +414,14 @@
             return;
         }
 
-        new_ips = avl_tree_new (compare_line, NULL);
+        new_ips = avl_tree_new (cache->compare, NULL);
 
         while (get_line (file, line, MAX_LINE_LEN))
         {
-            char *str;
             if(!line[0] || line[0] == '#')
                 continue;
             count++;
-            str = strdup (line);
-            if (str)
-                avl_insert (new_ips, str);
+            cache->add_new_entry (new_ips, line, 0);
         }
         fclose (file);
         INFO2 ("%d entries read from file \"%s\"", count, cache->filename);
@@ -389,16 +439,41 @@
     time_t now = time(NULL);
 
     recheck_cached_file (&banned_ip, now);
-    recheck_cached_file (&allowed_ip, now);
 
     if (banned_ip.contents)
     {
+        ban_entry_removal = NULL;
         if (avl_get_by_key (banned_ip.contents, ip, &result) == 0)
         {
-            DEBUG1 ("%s is banned", ip);
-            return 0;
+            struct banned_entry *entry = result;
+            if (entry->timeout)
+            {
+                if (entry->timeout > now)
+                {
+                    /* we may need to extend the timeout, for repeat offenders */
+                    if (now+900 > entry->timeout)
+                        entry->timeout = now + 900;
+                    return 0;
+                }
+            }
+            else
+            {
+                DEBUG1 ("%s is banned", ip);
+                return 0;
+            }
         }
+        if (ban_entry_removal)
+        {
+            /* we have identified the entry with the earliest timeout, but has it expired */
+            if (ban_entry_removal->timeout <= now)
+            {
+                INFO1 ("removing %s from ban list for now", &ban_entry_removal->ip[0]);
+                avl_delete (banned_ip.contents, &ban_entry_removal->ip[0], free_filtered_line);
+            }
+            ban_entry_removal = NULL;
+        }
     }
+    recheck_cached_file (&allowed_ip, now);
     if (allowed_ip.contents)
     {
         if (avl_get_by_key (allowed_ip.contents, ip, &result) == 0)
@@ -416,7 +491,7 @@
 }
 
 
-int connection_init (connection_t *con, sock_t sock)
+int connection_init (connection_t *con, sock_t sock, const char *addr)
 {
     if (con)
     {
@@ -429,6 +504,11 @@
         con->sock = sock;
         if (sock == SOCK_ERROR)
             return 0;
+        if (addr)
+        {
+            con->ip = strdup (addr);
+            return 0;
+        }
         if (getpeername (sock, (struct sockaddr *)&sa, &slen) == 0)
         {
             char *ip;
@@ -468,6 +548,13 @@
 #endif
 }
 
+#ifdef HAVE_SIGNALFD
+void connection_close_sigfd (void)
+{
+    close (sigfd);
+}
+#endif
+
 static sock_t wait_for_serversock (void)
 {
 #ifdef HAVE_POLL
@@ -627,6 +714,7 @@
     }
     global_unlock ();
     sock_close (sock);
+    thread_sleep (1000);
     return NULL;
 }
 
@@ -841,10 +929,16 @@
 #endif
     banned_ip.filename = NULL;
     banned_ip.file_mtime = 0;
+    banned_ip.add_new_entry = add_banned_ip;
+    banned_ip.compare = compare_banned_ip;
     allowed_ip.filename = NULL;
     allowed_ip.file_mtime = 0;
+    allowed_ip.add_new_entry = add_generic_text;
+    allowed_ip.compare = compare_pattern;
     useragents.filename = NULL;
     useragents.file_mtime = 0;
+    useragents.add_new_entry = add_generic_text;
+    useragents.compare = compare_pattern;
 
     connection_running = 1;
     INFO0 ("connection thread started");
@@ -1147,13 +1241,12 @@
     return 1;
 }
 
-static void check_for_filtering (ice_config_t *config, client_t *client)
+static void check_for_filtering (ice_config_t *config, client_t *client, char *uri)
 {
-    const char *uri = httpp_getvar (client->parser, HTTPP_VAR_URI);
     char *pattern = config->access_log.exclude_ext;
     char *extension = strrchr (uri, '.');
 
-    if (extension == NULL || uri == NULL || pattern == NULL)
+    if (extension == NULL || pattern == NULL)
         return;
 
     extension++;
@@ -1189,7 +1282,7 @@
     }
     DEBUG1 ("start with %s", uri);
     config = config_get_config();
-    check_for_filtering (config, client);
+    check_for_filtering (config, client, uri);
     port = config->port;
     if (client->server_conn)
     {

Modified: icecast/branches/kh/icecast/src/connection.h
===================================================================
--- icecast/branches/kh/icecast/src/connection.h	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/connection.h	2010-05-08 03:59:17 UTC (rev 17198)
@@ -57,9 +57,11 @@
 void connection_thread_shutdown();
 int  connection_setup_sockets (struct ice_config_tag *config);
 void connection_close(connection_t *con);
-int  connection_init (connection_t *con, sock_t sock);
+int  connection_init (connection_t *con, sock_t sock, const char *addr);
 int  connection_complete_source (struct source_tag *source, http_parser_t *parser);
 void connection_uses_ssl (connection_t *con);
+void connection_add_banned_ip (const char *ip, int duration);
+void connection_stats (void);
 #ifdef HAVE_OPENSSL
 int  connection_read_ssl (connection_t *con, void *buf, size_t len);
 int  connection_send_ssl (connection_t *con, const void *buf, size_t len);
@@ -72,6 +74,8 @@
 int connection_check_relay_pass(http_parser_t *parser);
 int connection_check_admin_pass(http_parser_t *parser);
 
+void connection_close_sigfd (void);
+
 extern int connection_running;
 
 #endif  /* __CONNECTION_H__ */

Modified: icecast/branches/kh/icecast/src/format_mp3.c
===================================================================
--- icecast/branches/kh/icecast/src/format_mp3.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/format_mp3.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -545,11 +545,8 @@
         return -1;
     if (unprocessed > 0)
     {
-        /* make sure the new block has a minimum of queue_block_size */
-        size_t len = unprocessed > source_mp3->queue_block_size ? unprocessed : source_mp3->queue_block_size;
-        refbuf_t *leftover = refbuf_new (len);
-        memcpy (leftover->data, refbuf->data + refbuf->len, unprocessed);
-        leftover->len = unprocessed;
+        size_t len;
+        refbuf_t *leftover;
 
         if (source_mp3->inline_metadata_interval > 0)
         {
@@ -557,17 +554,22 @@
             if (source_mp3->build_metadata_len == 0 && source_mp3->offset > unprocessed)
             {
                 source_mp3->offset -= unprocessed;
-                source_mp3->read_data = leftover;
-                source_mp3->read_count = unprocessed;
             }
             else
+            {
+                leftover = refbuf_new (unprocessed);
+                memcpy (leftover->data, refbuf->data + refbuf->len, unprocessed);
+                leftover->len = unprocessed;
                 mpeg_data_insert (mpeg_sync, leftover); /* will need to merge this after metadata */
+                return 0;
+            }
         }
-        else
-        {
-            source_mp3->read_data = leftover;
-            source_mp3->read_count = unprocessed;
-        }
+        /* make sure the new block has a minimum of queue_block_size */
+        len = unprocessed > source_mp3->queue_block_size ? unprocessed : source_mp3->queue_block_size;
+        leftover = refbuf_new (len);
+        memcpy (leftover->data, refbuf->data + refbuf->len, unprocessed);
+        source_mp3->read_data = leftover;
+        source_mp3->read_count = unprocessed;
     }
     return 0;
 }

Modified: icecast/branches/kh/icecast/src/fserve.c
===================================================================
--- icecast/branches/kh/icecast/src/fserve.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/fserve.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -75,6 +75,8 @@
     fbinfo finfo;
     mutex_t lock;
     int refcount;
+    int peak;
+    int max;
     FILE *fp;
     time_t stats_update;
     format_plugin_t *format;
@@ -198,7 +200,6 @@
     }
     if (fh->fp)
         fclose (fh->fp);
-    stats_event (fh->finfo.mount, NULL, NULL);
     if (fh->format)
     {
         format_plugin_clear (fh->format, NULL);
@@ -238,7 +239,14 @@
         if (client)
         {
             if (finfo->mount && (finfo->flags & FS_FALLBACK))
+            {
                 stats_event_args (result->finfo.mount, "listeners", "%ld", result->refcount);
+                if (result->refcount > result->peak)
+                {
+                    result->peak = result->refcount;
+                    stats_event_flags (result->finfo.mount, "listener_peak", "%ld", result->peak);
+                }
+            }
             avl_insert (result->clients, client);
             if (result->format)
             {
@@ -281,7 +289,7 @@
         {
             char *contenttype = fserve_content_type (fullpath);
 
-            stats_event_flags (finfo->mount, "file", fullpath, STATS_HIDDEN);
+            stats_event (finfo->mount, "fallback", "file");
             fh->format = calloc (1, sizeof (format_plugin_t));
             fh->format->type = format_get_type (contenttype);
             free (contenttype);
@@ -305,10 +313,14 @@
     thread_mutex_lock (&fh->lock);
     fh->clients = avl_tree_new (client_compare, NULL);
     fh->refcount = 1;
+    fh->peak = 1;
     if (client)
     {
         if (finfo->mount && (finfo->flags & FS_FALLBACK))
+        {
             stats_event_flags (fh->finfo.mount, "listeners", "1", STATS_HIDDEN);
+            stats_event_flags (fh->finfo.mount, "listener_peak", "1", STATS_HIDDEN);
+        }
         avl_insert (fh->clients, client);
     }
     fh->finfo.mount = strdup (finfo->mount);
@@ -600,6 +612,8 @@
         if (fh->finfo.flags & FS_FALLBACK)
             stats_event_dec (NULL, "listeners");
         remove_from_fh (fh, client);
+        if (fh->refcount == 1)
+            stats_event (fh->finfo.mount, NULL, NULL);
         fh_release (fh);
     }
     if (client->respcode == 200)
@@ -645,6 +659,8 @@
     client->shared_data = NULL;
     thread_mutex_lock (&fh->lock);
     remove_from_fh (fh, client);
+    if (fh->refcount == 1)
+        stats_event (fh->finfo.mount, NULL, NULL);
     f.flags = fh->finfo.flags;
     f.limit = fh->finfo.limit;
     f.mount = fh->finfo.fallback;
@@ -824,7 +840,7 @@
     if (fh->stats_update <= now)
     {
         stats_event_args (fh->finfo.mount, "outgoing_kbitrate", "%ld",
-                            (8 * rate_avg (fh->format->out_bitrate))/1024);
+                (long)((8 * rate_avg (fh->format->out_bitrate))/1024));
         fh->stats_update = now + 5;
     }
     if (client->pos == refbuf->len)
@@ -1086,22 +1102,61 @@
 }
 
 
+int fserve_list_clients_xml (xmlNodePtr srcnode, fbinfo *finfo)
+{
+    int ret = 0;
+    fh_node *fh = open_fh (finfo, NULL);
+
+    if (fh)
+    {
+        avl_node *anode = avl_get_first (fh->clients);
+
+        while (anode)
+        {
+            client_t *listener = (client_t *)anode->key;
+            char buf [100];
+
+            xmlNodePtr node = xmlNewChild (srcnode, NULL, XMLSTR("listener"), NULL);
+            const char *useragent;
+            snprintf (buf, sizeof (buf), "%lu", listener->connection.id);
+            xmlSetProp (node, XMLSTR("id"), XMLSTR(buf));
+
+            xmlNewChild (node, NULL, XMLSTR("ip"), XMLSTR(listener->connection.ip));
+            useragent = httpp_getvar (listener->parser, "user-agent");
+            if (useragent)
+            {
+                xmlChar *str = xmlEncodeEntitiesReentrant (srcnode->doc, XMLSTR(useragent));
+                xmlNewChild (node, NULL, XMLSTR("useragent"), str);
+                xmlFree (str);
+            }
+            xmlNewChild (node, NULL, XMLSTR("lag"), XMLSTR( "0"));
+            snprintf (buf, sizeof (buf), "%lu",
+                    (unsigned long)(listener->worker->current_time.tv_sec - listener->connection.con_time));
+            xmlNewChild (node, NULL, XMLSTR("connected"), XMLSTR(buf));
+            if (listener->username)
+            {
+                xmlChar *str = xmlEncodeEntitiesReentrant (srcnode->doc, XMLSTR(listener->username));
+                xmlNewChild (node, NULL, XMLSTR("username"), str);
+                xmlFree (str);
+            }
+
+            ret++;
+            anode = avl_get_next (anode);
+        }
+        fh_release (fh);
+    }
+    return ret;
+}
+
+
 void fserve_list_clients (client_t *client, const char *mount, int response, int show_listeners)
 {
-    int c = 2;
-    unsigned int entries = 0;
-    const char *type = httpp_get_query_param (client->parser, "type");
+    int ret;
     fbinfo finfo;
     xmlDocPtr doc;
     xmlNodePtr node, srcnode;
-    char buf[100];
 
-    finfo.flags = 0;
-    if (type && strcmp (type, "fallback") == 0)
-    {
-        finfo.flags = FS_FALLBACK;
-        c = 1;
-    }
+    finfo.flags = FS_FALLBACK;
     finfo.mount = (char*)mount;
     finfo.limit = 0;
     finfo.fallback = NULL;
@@ -1112,56 +1167,17 @@
     srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL);
     xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(mount));
 
-    while (c)
+    ret = fserve_list_clients_xml (srcnode, &finfo);
+    if (ret == 0)
     {
-        fh_node *fh = open_fh (&finfo, NULL);
-        if (fh)
-        {
-            avl_node *node = avl_get_first (fh->clients);
-
-            while (node)
-            {
-                client_t *listener = (client_t *)node->key;
-
-                if (show_listeners)
-                {
-                    xmlNodePtr node = xmlNewChild (srcnode, NULL, XMLSTR("listener"), NULL);
-                    const char *useragent;
-                    snprintf (buf, sizeof (buf), "%lu", listener->connection.id);
-                    xmlSetProp (node, XMLSTR("id"), XMLSTR(buf));
-
-                    xmlNewChild (node, NULL, XMLSTR("ip"), XMLSTR(listener->connection.ip));
-                    useragent = httpp_getvar (listener->parser, "user-agent");
-                    if (useragent)
-                    {
-                        xmlChar *str = xmlEncodeEntitiesReentrant (srcnode->doc, XMLSTR(useragent));
-                        xmlNewChild (node, NULL, XMLSTR("useragent"), str);
-                        xmlFree (str);
-                    }
-                    xmlNewChild (node, NULL, XMLSTR("lag"), XMLSTR( "0"));
-                    snprintf (buf, sizeof (buf), "%lu",
-                            (unsigned long)(listener->worker->current_time.tv_sec - listener->connection.con_time));
-                    xmlNewChild (node, NULL, XMLSTR("connected"), XMLSTR(buf));
-                    if (listener->username)
-                    {
-                        xmlChar *str = xmlEncodeEntitiesReentrant (srcnode->doc, XMLSTR(listener->username));
-                        xmlNewChild (node, NULL, XMLSTR("username"), str);
-                        xmlFree (str);
-                    }
-                }
-
-                entries++;
-                node = avl_get_next (node);
-            }
-            fh_release (fh);
-        }
-        c--;
-        finfo.flags = FS_FALLBACK;
+        finfo.flags = 0;
+        ret = fserve_list_clients_xml (srcnode, &finfo);
     }
-    if (entries)
+    if (ret)
     {
-        snprintf (buf, sizeof(buf), "%u", entries);
-        xmlNewChild(srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf));
+        char buf[20];
+        snprintf (buf, sizeof(buf), "%u", ret);
+        xmlNewChild (srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf));
         admin_send_response (doc, client, response, "listclients.xsl");
     }
     else

Modified: icecast/branches/kh/icecast/src/fserve.h
===================================================================
--- icecast/branches/kh/icecast/src/fserve.h	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/fserve.h	2010-05-08 03:59:17 UTC (rev 17198)
@@ -40,6 +40,7 @@
 int  fserve_setup_client_fb (client_t *client, fbinfo *finfo);
 void fserve_set_override (const char *mount, const char *dest);
 void fserve_list_clients (client_t *client, const char *mount, int response, int show_listeners);
+int  fserve_list_clients_xml (xmlNodePtr srcnode, fbinfo *finfo);
 void fserve_kill_client (client_t *client, const char *mount, int response);
 
 extern int fserve_running;

Modified: icecast/branches/kh/icecast/src/global.c
===================================================================
--- icecast/branches/kh/icecast/src/global.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/global.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -47,7 +47,6 @@
 #endif
     thread_mutex_create(&_global_mutex);
     thread_spin_create (&global.spinlock);
-    thread_rwlock_create (&global.shutdown_lock);
     global.out_bitrate = rate_setup (20000, 1000);
 }
 
@@ -55,7 +54,6 @@
 {
     thread_mutex_destroy(&_global_mutex);
     thread_spin_destroy (&global.spinlock);
-    thread_rwlock_destroy (&global.shutdown_lock);
     avl_tree_free(global.source_tree, NULL);
 #ifdef MY_ALLOC
     avl_tree_free(global.alloc_tree, free_alloc_node);

Modified: icecast/branches/kh/icecast/src/global.h
===================================================================
--- icecast/branches/kh/icecast/src/global.h	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/global.h	2010-05-08 03:59:17 UTC (rev 17198)
@@ -39,7 +39,6 @@
     int schedule_config_reread;
 
     avl_tree *source_tree;
-    rwlock_t shutdown_lock;
 
 #ifdef MY_ALLOC
     avl_tree *alloc_tree;

Modified: icecast/branches/kh/icecast/src/main.c
===================================================================
--- icecast/branches/kh/icecast/src/main.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/main.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -118,6 +118,7 @@
     slave_shutdown();
     fserve_shutdown();
     stats_shutdown();
+    stop_logging();
 
     config_shutdown();
     refbuf_shutdown();
@@ -222,7 +223,6 @@
     slave_initialize();
 
     connection_setup_sockets (NULL);
-    stop_logging();
 }
 
 /* chroot the process. Watch out - we need to do this before starting other

Modified: icecast/branches/kh/icecast/src/mpeg.c
===================================================================
--- icecast/branches/kh/icecast/src/mpeg.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/mpeg.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -113,23 +113,33 @@
 }
 
 
+/* return -1 for no valid frame at this specified address, 0 for more data needed */
 static int get_initial_frame (struct mpeg_sync *mp, unsigned char *p, unsigned remaining)
 {
     if (p[1] < 0xE0) 
-        return 0;
+        return -1;
     mp->layer = (p[1] & 0x6) >> 1;
     //nocrc = p[1] & 0x1;
     if (mp->layer == 0 && (p[1] >= 0xF0))
     {
-        int frame_len;
         int samplerate_idx = (p[2] & 0x3C) >> 2,
             v = (p[2] << 8) + p[3],
             channels_idx = (v & 0x1C0) >> 6;
         int id =  p[1] & 0x8;
+        int checking = 3;
+        unsigned char *fh = p;
 
-        frame_len = get_aac_frame_len (p);
-        if (frame_len >= remaining || p[frame_len] != 255)
-            return -1;
+        while (checking)
+        {
+            int frame_len = get_aac_frame_len (fh);
+            if (frame_len+5 >= remaining)
+                return 0;
+            if (fh[frame_len] != 255 || fh[frame_len+1] != p[1])
+                return -1;
+            remaining -= frame_len;
+            fh += frame_len;
+            checking--;
+        }
         // profile = p[1] & 0xC0;
         mp->samplerate = aacp_sample_freq [samplerate_idx];
         mp->channels = aacp_num_channels [channels_idx];
@@ -140,7 +150,6 @@
         }
         mp->syncbytes = 3;
         memcpy (&mp->fixed_headerbits[0], p, 3);
-        //mp->samplerate <<= 1;
         DEBUG3 ("detected AAC MPEG-%s, rate %d, channels %d", id ? "2" : "4", mp->samplerate, mp->channels);
         mp->process_frame = handle_aac_frame;
         return 1;
@@ -152,7 +161,8 @@
         mp->ver = (p[1] & 0x18) >> 3;
         if (mp->layer && version [mp->ver] && layer[mp->layer]) 
         {
-            int frame_len;
+            int checking = 4;
+            unsigned char *fh = p;
             int samplerates [4][4] = {
                 { 11025, 0, 22050, 44100},
                 { 12000, 0, 24000, 48000 },
@@ -163,24 +173,31 @@
             mp->samplerate = samplerates [(p[2]&0xC) >> 2][mp->ver];
             if (mp->samplerate == 0)
                 return -1;
-            frame_len = get_frame_samples (mp, p);
-            if (frame_len > 0)
+            while (checking)
             {
-                if (frame_len >= remaining || p[frame_len] != 255)
+                int frame_len = get_frame_samples (mp, fh);
+                if (frame_len <= 0)
                     return -1;
-                if  (((p[3] & 0xC0) >> 6) == 3)
-                    mp->channels = 1;
-                else
-                    mp->channels = 2;
-                mp->syncbytes = 2;
-                memcpy (&mp->fixed_headerbits[0], p, 2);
-                //DEBUG4 ("%s %s detected (%d, %d)", version [mp->ver], layer[mp->layer], mp->samplerate, mp->channels);
-                mp->process_frame = handle_mpeg_frame;
-                return 1;
+                if (frame_len+4 >= remaining)
+                    return 0;
+                if (fh[frame_len] != 255 || fh[frame_len+1] != p[1])
+                    return -1;
+                remaining -= frame_len;
+                fh += frame_len;
+                checking--;
             }
+            if  (((p[3] & 0xC0) >> 6) == 3)
+                mp->channels = 1;
+            else
+                mp->channels = 2;
+            mp->syncbytes = 2;
+            memcpy (&mp->fixed_headerbits[0], p, 2);
+            DEBUG4 ("%s %s detected (%d, %d)", version [mp->ver], layer[mp->layer], mp->samplerate, mp->channels);
+            mp->process_frame = handle_mpeg_frame;
+            return 1;
         }
     }
-    return 0;
+    return -1;
 }
 
 
@@ -219,11 +236,11 @@
     }
     start = (unsigned char *)new_block->data + offset;
     remaining = new_block->len - offset;
-    end = start + remaining;
+    end = (unsigned char*)new_block->data + new_block->len;
     while (1)
     {
         remaining = end - start;
-        if (remaining < 10)
+        if (remaining < 10) /* make sure we have some bytes to check */
             break;
         if (*start != 255)
         {
@@ -232,21 +249,26 @@
             if (ret == remaining)
                 break; // no sync in the rest, so dump it
             new_block->len -= (remaining - ret);
-            remaining = ret;
-            end = start + remaining;
+            end = (unsigned char*)new_block->data + new_block->len;
+            continue;
         }
         if (mp->syncbytes == 0)
         {
-            if (get_initial_frame (mp, start, remaining) <= 0)
+            int ret = get_initial_frame (mp, start, remaining);
+            if (ret < 0)
             {
-                // no a complete frame not here, try again
-                start++;
+                // failed to detect a complete frame, try again
+                memmove (start, start+1, remaining-1);
+                end--;
                 continue;
             }
+            if (ret == 0)
+                break;
         }
         if (memcmp (start, &mp->fixed_headerbits[0], mp->syncbytes) != 0)
         {
-            start++;
+            memmove (start, start+1, remaining-1);
+            end--;
             continue;
         }
         frame_len = mp->process_frame (mp, start, remaining);
@@ -256,7 +278,13 @@
         completed++;
     }
     if (completed == 0)
-        return -1;
+    {
+        /* none found, so either shrink it or drop it */
+        if (remaining > 1500)
+            remaining = 1500;
+        else
+            return -1;
+    }
     new_block->len -= remaining;
     return remaining;
 }

Modified: icecast/branches/kh/icecast/src/mpeg.h
===================================================================
--- icecast/branches/kh/icecast/src/mpeg.h	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/mpeg.h	2010-05-08 03:59:17 UTC (rev 17198)
@@ -19,7 +19,7 @@
 typedef struct mpeg_sync
 {
     unsigned char fixed_headerbits[3];
-    int syncbytes;
+    char syncbytes;
     int (*process_frame) (struct mpeg_sync *mp, unsigned char *p, int len);
     refbuf_t *surplus;
     int ver;

Modified: icecast/branches/kh/icecast/src/slave.c
===================================================================
--- icecast/branches/kh/icecast/src/slave.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/slave.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -112,7 +112,7 @@
 {
     relay_server *next = relay->next;
 
-    DEBUG1("freeing relay %s", relay->localmount);
+    DEBUG2("freeing relay %s (%p)", relay->localmount, relay);
     if (relay->source)
        source_free_source (relay->source);
     while (relay->masters)
@@ -165,6 +165,7 @@
         copy->enable = r->enable;
         copy->source = r->source;
         r->source = NULL;
+        DEBUG1 ("copy relay at %p", copy);
     }
     return copy;
 }
@@ -293,27 +294,65 @@
 }
 
 
+static http_parser_t *get_relay_response (connection_t *con, const char *mount,
+        const char *server, int ask_for_metadata, const char *auth_header)
+{
+    ice_config_t *config = config_get_config ();
+    char *server_id = strdup (config->server_id);
+    http_parser_t *parser = NULL;
+    char response [4096];
+
+    config_release_config ();
+
+    /* At this point we may not know if we are relaying an mp3 or vorbis
+     * stream, but only send the icy-metadata header if the relay details
+     * state so (the typical case).  It's harmless in the vorbis case. If
+     * we don't send in this header then relay will not have mp3 metadata.
+     */
+    sock_write (con->sock, "GET %s HTTP/1.0\r\n"
+            "User-Agent: %s\r\n"
+            "Host: %s\r\n"
+            "%s"
+            "%s"
+            "\r\n",
+            mount,
+            server_id,
+            server,
+            ask_for_metadata ? "Icy-MetaData: 1\r\n" : "",
+            auth_header ? auth_header : "");
+
+    free (server_id);
+    memset (response, 0, sizeof(response));
+    if (util_read_header (con->sock, response, 4096, READ_ENTIRE_HEADER) == 0)
+    {
+        INFO0 ("Header read failure");
+        return NULL;
+    }
+    parser = httpp_create_parser();
+    httpp_initialize (parser, NULL);
+    if (! httpp_parse_response (parser, response, strlen(response), mount))
+    {
+        INFO0 ("problem parsing response from relay");
+        httpp_destroy (parser);
+        return NULL;
+    }
+    return parser;
+}
+
+
 /* Actually open the connection and do some http parsing, handle any 302
  * responses within here.
  */
 static int open_relay_connection (client_t *client, relay_server *relay, relay_server_master *master)
 {
     int redirects = 0;
-    char *server_id = NULL;
-    ice_config_t *config;
     http_parser_t *parser = NULL;
     connection_t *con = &client->connection;
     char *server = strdup (master->ip);
     char *mount = strdup (master->mount);
-    int port = master->port;
-    char *bind = NULL;
-    char *auth_header;
-    char header[4096];
+    int port = master->port, timeout = master->timeout, ask_for_metadata = relay->mp3metadata;
+    char *auth_header = NULL;
 
-    config = config_get_config ();
-    server_id = strdup (config->server_id);
-    config_release_config ();
-
     if (relay->username && relay->password)
     {
         char *esc_authorisation;
@@ -329,57 +368,42 @@
                 "Authorization: Basic %s\r\n", esc_authorisation);
         free(esc_authorisation);
     }
-    else
-        auth_header = strdup ("");
 
     while (redirects < 10)
     {
         sock_t streamsock;
+        char *bind = NULL;
 
         /* policy decision, we assume a source bind even after redirect, possible option */
         if (master->bind)
-            bind = master->bind;
+            bind = strdup (master->bind);
 
         if (bind)
             INFO4 ("connecting to %s:%d for %s, bound to %s", server, port, relay->localmount, bind);
         else
             INFO3 ("connecting to %s:%d for %s", server, port, relay->localmount);
 
-        streamsock = sock_connect_wto_bind (server, port, bind, master->timeout);
-        if (streamsock == SOCK_ERROR)
+        thread_mutex_unlock (&client->worker->lock);
+        streamsock = sock_connect_wto_bind (server, port, bind, timeout);
+        free (bind);
+        if (streamsock == SOCK_ERROR || connection_init (con, streamsock, server) < 0)
         {
-            WARN3 ("Failed to connect to %s:%d for %s", server, port, relay->localmount);
+            WARN2 ("Failed to connect to %s:%d", server, port);
+            thread_mutex_lock (&client->worker->lock);
             break;
         }
-        connection_init (con, streamsock);
 
-        /* At this point we may not know if we are relaying an mp3 or vorbis
-         * stream, but only send the icy-metadata header if the relay details
-         * state so (the typical case).  It's harmless in the vorbis case. If
-         * we don't send in this header then relay will not have mp3 metadata.
-         */
-        sock_write(streamsock, "GET %s HTTP/1.0\r\n"
-                "User-Agent: %s\r\n"
-                "Host: %s\r\n"
-                "%s"
-                "%s"
-                "\r\n",
-                mount,
-                server_id,
-                server,
-                relay->mp3metadata ? "Icy-MetaData: 1\r\n" : "",
-                auth_header);
-        memset (header, 0, sizeof(header));
-        if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0)
+        parser = get_relay_response (con, mount, server, ask_for_metadata, auth_header);
+
+        thread_mutex_lock (&client->worker->lock);
+        if (relay != client->shared_data)   /* unusual but possible */
         {
-            ERROR4 ("Header read failed for %s (%s:%d%s)", relay->localmount, server, port, mount);
+            INFO0 ("detected relay change, retrying");
             break;
         }
-        parser = httpp_create_parser();
-        httpp_initialize (parser, NULL);
-        if (! httpp_parse_response (parser, header, strlen(header), relay->localmount))
+        if (parser == NULL)
         {
-            ERROR4("Error parsing relay request for %s (%s:%d%s)", relay->localmount,
+            ERROR4 ("Problem trying to start relay on %s (%s:%d%s)", relay->localmount,
                     server, port, mount);
             break;
         }
@@ -414,6 +438,8 @@
         }
         else
         {
+            http_parser_t *old_parser = client->parser;
+
             if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE))
             {
                 ERROR2("Error from relay request: %s (%s)", relay->localmount,
@@ -422,10 +448,11 @@
             }
             sock_set_blocking (streamsock, 0);
             client->parser = parser;
+            if (old_parser)
+                httpp_destroy (old_parser);
             client_set_queue (client, NULL);
             free (server);
             free (mount);
-            free (server_id);
             free (auth_header);
 
             return 0;
@@ -435,7 +462,6 @@
     /* failed, better clean up */
     free (server);
     free (mount);
-    free (server_id);
     free (auth_header);
     connection_close (con);
     if (parser)
@@ -460,6 +486,8 @@
         ret = open_relay_connection (client, relay, master);
         thread_mutex_lock (&src->lock);
 
+        if (relay != client->shared_data) // relay data changed, retry with new data
+            return open_relay (client->shared_data);
         if (ret < 0)
             continue;
         source_clear_source (src); // clear any old data
@@ -477,9 +505,9 @@
 
 static void *start_relay_stream (void *arg)
 {
-    relay_server *relay = arg;
-    source_t *src = relay->source;
-    client_t *client = src->client;
+    client_t *client = arg;
+    relay_server *relay;
+    source_t *src;
     int failed = 1, sources;
 
     global_lock();
@@ -490,7 +518,10 @@
     {
         ice_config_t *config = config_get_config();
 
-        thread_rwlock_rlock (&global.shutdown_lock);
+        thread_mutex_lock (&client->worker->lock);
+        relay = client->shared_data;
+        src = relay->source;
+
         thread_mutex_lock (&src->lock);
         src->flags |= SOURCE_PAUSE_LISTENERS;
         if (sources > config->source_limit)
@@ -509,6 +540,7 @@
         failed = 0;
     } while (0);
 
+    relay = client->shared_data; // relay may of changed during open_relay
     relay->running = 1;
     client->ops = &relay_client_ops;
     client->schedule_ms = timing_get_time();
@@ -533,7 +565,6 @@
     relays_connecting--;
     thread_spin_unlock (&relay_start_lock);
 
-    thread_mutex_lock (&client->worker->lock);
     client->flags |= CLIENT_ACTIVE;
     thread_cond_signal (&client->worker->cond);
     thread_mutex_unlock (&client->worker->lock);
@@ -922,11 +953,11 @@
         if (curl_easy_perform (handle) != 0)
             WARN2 ("Failed URL access \"%s\" (%s)", url, error);
     }
-    /* process retrieved relays */
+    /* merge retrieved relays */
     thread_mutex_lock (&(config_locks()->relay_lock));
     cleanup_relays = update_relays (&global.master_relays, master->new_relays);
 
-    relay_check_streams (global.master_relays, cleanup_relays, 0);
+    relay_check_streams (global.master_relays, cleanup_relays, 1);
     relay_check_streams (NULL, master->new_relays, 0);
 
     thread_mutex_unlock (&(config_locks()->relay_lock));
@@ -1095,8 +1126,6 @@
     global.master_relays = NULL;
     redirector_clearall();
 
-    thread_rwlock_wlock (&global.shutdown_lock);
-    thread_rwlock_unlock (&global.shutdown_lock);
     INFO0 ("Slave thread shutdown complete");
 }
 
@@ -1247,37 +1276,39 @@
     }
     DEBUG1 ("all listeners have now been checked on %s", relay->localmount);
     source->flags &= ~SOURCE_TERMINATING;
-    client->ops = &relay_startup_ops;
     if (relay->running && relay->enable)
     {
         INFO1 ("standing by to restart relay on %s", relay->localmount);
         connection_close (&client->connection);
-        if (client->parser)
-            httpp_destroy (client->parser);
-        client->parser = NULL;
         thread_mutex_unlock (&source->lock);
         ret = 0;
     }
     else
     {
+        if (source->listeners)
+        {
+            INFO1 ("listeners on terminating relay %s, rechecking", relay->localmount);
+            thread_mutex_unlock (&source->lock);
+            return 0; /* listeners may be paused, recheck and let them leave this stream */
+        }
         INFO1 ("shutting down relay %s", relay->localmount);
         if (relay->enable == 0)
         {
             source_clear_source (source);
-            source_clear_listeners (source);
             relay->running = 0;
             ret = 0;
         }
+        source_clear_listeners (source);
         thread_mutex_unlock (&source->lock);
         stats_event (relay->localmount, NULL, NULL); // needed???
         slave_update_all_mounts();
     }
+    client->ops = &relay_startup_ops;
     global_lock();
     global.sources--;
     stats_event_args (NULL, "sources", "%d", global.sources);
     global_unlock();
     global_reduce_bitrate_sampling (global.out_bitrate);
-    thread_rwlock_unlock (&global.shutdown_lock);
     return ret;
 }
 
@@ -1295,7 +1326,13 @@
     relay_server *relay = client->shared_data;
 
     if (relay->cleanup)
+    {
+        source_t *source = relay->source;
+        thread_mutex_lock (&source->lock);
+        source_clear_listeners (source);
+        thread_mutex_unlock (&source->lock);
         return -1;
+    }
     if (global.running != ICE_RUNNING)
         return 0; /* wait for cleanup */
     if (relay->enable == 0 || relay->start > client->worker->current_time.tv_sec)
@@ -1307,15 +1344,31 @@
     if (relay->on_demand)
     {
         source_t *src = relay->source;
+        int start_relay = src->listeners; // 0 or non-zero
+
         src->flags |= SOURCE_ON_DEMAND;
         if (client->worker->current_time.tv_sec % 10 == 0)
         {
             mount_proxy * mountinfo = config_find_mount (config_get_config(), src->mount);
             if (mountinfo && mountinfo->fallback_mount)
-                source_set_override (mountinfo->fallback_mount, src->mount);
+            {
+                source_t *fallback;
+                avl_tree_rlock (global.source_tree);
+                fallback = source_find_mount (mountinfo->fallback_mount);
+                if (fallback)
+                {
+                    if (strcmp (fallback->mount, src->mount) != 0)
+                    {
+                        // if there are listeners not already being moved
+                        if (fallback->listeners && fallback->fallback.mount == NULL)
+                            start_relay = 1;
+                    }
+                }
+                avl_tree_unlock (global.source_tree);
+            }
             config_release_config();
         }
-        if (src->listeners == 0)
+        if (start_relay == 0)
         {
             client->schedule_ms = client->worker->time_ms + 1000;
             return 0;
@@ -1328,14 +1381,14 @@
     if (relays_connecting > 3)
     {
         thread_spin_unlock (&relay_start_lock);
-        client->schedule_ms = client->worker->time_ms + 2000;
+        client->schedule_ms = client->worker->time_ms + 1000;
         return 0;
     }
     relays_connecting++;
     thread_spin_unlock (&relay_start_lock);
 
     client->flags &= ~CLIENT_ACTIVE;
-    thread_create ("Relay Thread", start_relay_stream, relay, THREAD_DETACHED);
+    thread_create ("Relay Thread", start_relay_stream, client, THREAD_DETACHED);
     return 0;
 }
 

Modified: icecast/branches/kh/icecast/src/source.c
===================================================================
--- icecast/branches/kh/icecast/src/source.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/source.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -350,7 +350,7 @@
 
     /* There should be no listeners on this mount */
     if (source->listeners)
-        WARN1("active listeners on mountpoint %s", source->mount);
+        WARN3("active listeners on mountpoint %s (%ld, %ld)", source->mount, source->listeners, source->termination_count);
     avl_tree_free (source->clients, NULL);
 
     thread_mutex_destroy (&source->lock);
@@ -368,7 +368,7 @@
 {
     INFO1 ("source %s to be freed", source->mount);
     avl_tree_wlock (global.source_tree);
-    INFO1 ("removing source %s from tree", source->mount);
+    DEBUG1 ("removing source %s from tree", source->mount);
     avl_delete (global.source_tree, source, _free_source);
     avl_tree_unlock (global.source_tree);
 }
@@ -443,6 +443,8 @@
             source->fallback.mount = NULL;
             source->flags &= ~SOURCE_TEMPORARY_FALLBACK;
         }
+        if (source->listeners == 0)
+            rate_add (source->format->out_bitrate, 0, client->worker->time_ms);
         if (source->prev_listeners != source->listeners)
         {
             INFO2("listener count on %s now %lu", source->mount, source->listeners);
@@ -850,13 +852,8 @@
     int ret = 0;
 
     if (client->connection.error)
-    {
-        /* if listener disconnects at the same time as the source does then we need
-         * to account for it as the source thinks it is still connected */
-        if (source->termination_count)
-            source->termination_count--;
         return -1;
-    }
+
     if (source->fallback.mount)
     {
         int move_failed;
@@ -875,10 +872,10 @@
     }
     if (source->flags & SOURCE_TERMINATING)
     {
-        source->termination_count--;
         DEBUG2 ("termination count on %s now %lu", source->mount, source->termination_count);
         if ((source->flags & SOURCE_PAUSE_LISTENERS) && global.running == ICE_RUNNING)
         {
+            source->termination_count--;
             if (client->refbuf && (client->refbuf->flags & SOURCE_QUEUE_BLOCK))
                 client_set_queue (client, NULL);
             client->ops = &listener_pause_ops;
@@ -1085,7 +1082,7 @@
 void source_set_fallback (source_t *source, const char *dest_mount)
 {
     int bitrate = (int)(rate_avg (source->format->in_bitrate) * 1.02);
-    if (dest_mount == NULL || bitrate == 0)
+    if (dest_mount == NULL)
         return;
 
     source->fallback.flags = FS_FALLBACK;
@@ -1443,7 +1440,6 @@
         global_unlock();
         return -1;
     }
-    thread_rwlock_rlock (&global.shutdown_lock);
 
     agent = httpp_getvar (source->client->parser, "user-agent");
     if (agent)
@@ -1644,7 +1640,6 @@
     thread_mutex_unlock (&source->lock);
 
     source_free_source (source);
-    thread_rwlock_unlock (&global.shutdown_lock);
     slave_update_all_mounts();
     client_destroy (client);
 }
@@ -1661,6 +1656,11 @@
     if (source->listeners == 0)
         rate_reduce (source->format->out_bitrate, 1000);
 
+    /* if listener disconnects at the same time as the source does then we need
+     * to account for it as the source thinks it is still connected */
+    if (source->termination_count)
+        source->termination_count--;
+
     stats_event_dec (NULL, "listeners");
     stats_event_args (source->mount, "listeners", "%lu", source->listeners);
     /* change of listener numbers, so reduce scope of global sampling */

Modified: icecast/branches/kh/icecast/src/stats.c
===================================================================
--- icecast/branches/kh/icecast/src/stats.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/stats.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -1021,9 +1021,20 @@
             thread_mutex_lock (&source->lock);
             admin_source_listeners (source, node);
             thread_mutex_unlock (&source->lock);
+            avl_tree_unlock (global.source_tree);
         }
+        else
+        {
+            fbinfo finfo;
 
-        avl_tree_unlock (global.source_tree);
+            avl_tree_unlock (global.source_tree);
+            finfo.flags = FS_FALLBACK;
+            finfo.mount = (char*)show_mount;
+            finfo.limit = 0;
+            finfo.fallback = NULL;
+
+            fserve_list_clients_xml (node, &finfo);
+        }
     }
     return doc;
 }
@@ -1135,10 +1146,10 @@
 
         if (source == NULL)
         {
-            stats_node_t *node = _find_node (src->stats_tree, "file");
+            stats_node_t *node = _find_node (src->stats_tree, "fallback");
             if (node == NULL)
             {
-                /* no source_t and no fallbakc file stat, so delete */
+                /* no source_t and no fallback file stat, so delete */
                 snode = avl_get_next (snode);
                 avl_delete (_stats.source_tree, src, _free_source_stats);
                 continue;
@@ -1158,6 +1169,7 @@
     avl_node *anode;
     char buffer [VAL_BUFSIZE];
 
+    connection_stats ();
     anode = avl_get_first(_stats.global_tree);
     while (anode)
     {

Modified: icecast/branches/kh/icecast/src/xslt.c
===================================================================
--- icecast/branches/kh/icecast/src/xslt.c	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/src/xslt.c	2010-05-08 03:59:17 UTC (rev 17198)
@@ -125,7 +125,8 @@
         }
     }
 
-    xsltFreeStylesheet(cache[oldest].stylesheet);
+    if (cache[oldest].stylesheet)
+        xsltFreeStylesheet(cache[oldest].stylesheet);
     free(cache[oldest].filename);
 
     return oldest;

Modified: icecast/branches/kh/icecast/win32/icecast2.iss
===================================================================
--- icecast/branches/kh/icecast/win32/icecast2.iss	2010-05-05 23:28:15 UTC (rev 17197)
+++ icecast/branches/kh/icecast/win32/icecast2.iss	2010-05-08 03:59:17 UTC (rev 17198)
@@ -3,7 +3,7 @@
 
 [Setup]
 AppName=Icecast2-KH
-AppVerName=Icecast v2.3.2-kh22
+AppVerName=Icecast v2.3.2-kh23
 AppPublisherURL=http://www.icecast.org
 AppSupportURL=http://www.icecast.org
 AppUpdatesURL=http://www.icecast.org
@@ -13,7 +13,7 @@
 LicenseFile=..\COPYING
 InfoAfterFile=..\README
 OutputDir=.
-OutputBaseFilename=icecast2_win32_v2.3.2-kh22_setup
+OutputBaseFilename=icecast2_win32_v2.3.2-kh23_setup
 WizardImageFile=icecast2logo2.bmp
 WizardImageStretch=no
 VersionInfoVersion=2.3.2
@@ -72,3 +72,6 @@
 
 
 
+
+
+



More information about the commits mailing list