From 55ba59f3f77b8010361e3bbf886364fdbbac63f2 Mon Sep 17 00:00:00 2001 From: Karl Heyes Date: Mon, 10 May 2004 16:17:56 +0000 Subject: [PATCH] Update of the YP code. This should resolve several YP issues that have been reported, the main one being icecast instability when there is a YP server outage. svn path=/icecast/trunk/icecast/; revision=6646 --- configure.in | 2 +- src/Makefile.am | 4 +- src/admin.c | 12 +- src/format_mp3.c | 1 + src/format_vorbis.c | 19 +- src/geturl.c | 195 ------- src/geturl.h | 58 -- src/main.c | 12 +- src/source.c | 196 ++----- src/source.h | 1 - src/yp.c | 1249 ++++++++++++++++++++++++++----------------- src/yp.h | 44 +- 12 files changed, 843 insertions(+), 950 deletions(-) delete mode 100644 src/geturl.c delete mode 100644 src/geturl.h diff --git a/configure.in b/configure.in index c572a931..fe1b852f 100644 --- a/configure.in +++ b/configure.in @@ -92,7 +92,7 @@ then XIPH_PATH_CURL([ AC_CHECK_DECL([CURLOPT_NOSIGNAL], [ AC_DEFINE([USE_YP], 1, [Define to compile in YP support code]) - ICECAST_OPTIONAL="$ICECAST_OPTIONAL geturl.o yp.o" + ICECAST_OPTIONAL="$ICECAST_OPTIONAL yp.o" XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$CURL_CFLAGS]) XIPH_VAR_PREPEND([XIPH_LIBS],[$CURL_LIBS]) ], [ AC_MSG_NOTICE([Your curl dev files are too old (7.10 or above required), YP disabled]) diff --git a/src/Makefile.am b/src/Makefile.am index d6d02e05..23ac1e66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,11 +8,11 @@ bin_PROGRAMS = icecast noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h global.h\ util.h slave.h source.h stats.h refbuf.h client.h format.h format_vorbis.h\ - compat.h format_mp3.h fserve.h xslt.h geturl.h yp.h event.h auth.h md5.h + compat.h format_mp3.h fserve.h xslt.h yp.h event.h auth.h md5.h icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c\ util.c slave.c source.c stats.c refbuf.c client.c format.c format_vorbis.c\ format_mp3.c xslt.c fserve.c event.c admin.c auth.c md5.c -EXTRA_icecast_SOURCES = geturl.c yp.c +EXTRA_icecast_SOURCES = yp.c icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \ httpp/libicehttpp.la log/libicelog.la avl/libiceavl.la timing/libicetiming.la diff --git a/src/admin.c b/src/admin.c index 92eabb3c..dd76a871 100644 --- a/src/admin.c +++ b/src/admin.c @@ -779,10 +779,6 @@ static void command_metadata(client_t *client, source_t *source) char *action; char *value; mp3_state *state; -#ifdef USE_YP - int i; - time_t current_time; -#endif DEBUG0("Got metadata update request"); @@ -813,15 +809,9 @@ static void command_metadata(client_t *client, source_t *source) source->mount, value); stats_event(source->mount, "title", value); -#ifdef USE_YP /* If we get an update on the mountpoint, force a yp touch */ - current_time = time(NULL); - for (i=0; inum_yp_directories; i++) { - source->ypdata[i]->yp_last_touch = current_time - - source->ypdata[i]->yp_touch_interval + 2; - } -#endif + yp_touch (source->mount); html_success(client, "Metadata update successful"); } diff --git a/src/format_mp3.c b/src/format_mp3.c index 4231d58b..23c05291 100644 --- a/src/format_mp3.c +++ b/src/format_mp3.c @@ -357,6 +357,7 @@ static int format_mp3_get_buffer(format_plugin_t *self, char *data, state->metadata_buffer = NULL; state->metadata_age++; thread_mutex_unlock(&(state->lock)); + yp_touch (self->mount); } state->offset = 0; diff --git a/src/format_vorbis.c b/src/format_vorbis.c index af8263d8..3d4be8a2 100644 --- a/src/format_vorbis.c +++ b/src/format_vorbis.c @@ -122,9 +122,6 @@ int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long le refbuf_t *refbuf, *source_refbuf; vstate_t *state = (vstate_t *)self->_state; source_t *source; -#ifdef USE_YP - time_t current_time; -#endif if (data) { /* write the data to the buffer */ @@ -199,21 +196,7 @@ int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long le } thread_mutex_unlock(&source->queue_mutex); -#ifdef USE_YP - /* If we get an update on the mountpoint, force a - yp touch */ - - if (source) { - /* If we get an update on the mountpoint, force a - yp touch */ - current_time = time(NULL); - for (i=0; inum_yp_directories; i++) { - source->ypdata[i]->yp_last_touch = current_time - - source->ypdata[i]->yp_touch_interval + 2; - } - } -#endif - + yp_touch (self->mount); } } diff --git a/src/geturl.c b/src/geturl.c deleted file mode 100644 index b20f28b2..00000000 --- a/src/geturl.c +++ /dev/null @@ -1,195 +0,0 @@ -/* Icecast - * - * 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). - */ - -/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */ -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include - -#include "connection.h" -#include "refbuf.h" -#include "client.h" -#include "logging.h" -#include "format.h" -#include "geturl.h" -#include "source.h" -#include "cfgfile.h" - -#include -#include -#include - - -#define CATMODULE "geturl" - -static curl_connection curl_connections[NUM_CONNECTIONS]; -static mutex_t _curl_mutex; - -size_t curl_write_memory_callback(void *ptr, size_t size, - size_t nmemb, void *data) -{ - register int realsize = size * nmemb; - - struct curl_memory_struct *mem = (struct curl_memory_struct *)data; - - if ((realsize + mem->size) < YP_RESPONSE_SIZE-1) { - strncat(mem->memory, ptr, realsize); - } - - return realsize; -} - -size_t curl_header_memory_callback(void *ptr, size_t size, - size_t nmemb, void *data) -{ - char *p1 = 0; - char *p2 = 0; - int copylen = 0; - register int realsize = size * nmemb; - struct curl_memory_struct2 *mem = (struct curl_memory_struct2 *)data; - - if (!strncmp(ptr, "SID: ", strlen("SID: "))) { - p1 = (char *)ptr + strlen("SID: "); - p2 = strchr((const char *)p1, '\r'); - memset(mem->sid, '\000', sizeof(mem->sid)); - if (p2) { - if (p2-p1 > sizeof(mem->sid)-1) { - copylen = sizeof(mem->sid)-1; - } - else { - copylen = p2-p1; - } - strncpy(mem->sid, p1, copylen); - } - else { - strncpy(mem->sid, p1, sizeof(mem->sid)-1); - } - } - if (!strncmp(ptr, "YPMessage: ", strlen("YPMessage: "))) { - p1 = (char *)ptr + strlen("YPMessage: "); - p2 = strchr((const char *)p1, '\r'); - memset(mem->message, '\000', sizeof(mem->message)); - if (p2) { - if (p2-p1 > sizeof(mem->message)-1) { - copylen = sizeof(mem->message)-1; - } - else { - copylen = p2-p1; - } - strncpy(mem->message, p1, copylen); - } - else { - strncpy(mem->message, p1, sizeof(mem->message)-1); - } - } - if (!strncmp(ptr, "TouchFreq: ", strlen("TouchFreq: "))) { - p1 = (char *)ptr + strlen("TouchFreq: "); - mem->touch_interval = atoi(p1); - } - if (!strncmp(ptr, "YPResponse: ", strlen("YPResponse: "))) { - p1 = (char *)ptr + strlen("YPResponse: "); - mem->response = atoi(p1); - } - return realsize; -} -int curl_initialize() -{ - int i = 0; - thread_mutex_create(&_curl_mutex); - - memset(&curl_connections, 0, sizeof(curl_connections)); - for (i=0; i (%s)", mem->sid); - DEBUG1("Message -> (%s)", mem->message); - DEBUG1("Touch Freq -> (%d)", mem->touch_interval); - DEBUG1("Response -> (%d)", mem->response); -} - - -CURL *curl_get_handle(int which) -{ - return curl_connections[which].curl_handle; -} - -struct curl_memory_struct *curl_get_result(int which) -{ - return &(curl_connections[which].result); -} - -struct curl_memory_struct2 *curl_get_header_result(int which) -{ - return &(curl_connections[which].header_result); -} diff --git a/src/geturl.h b/src/geturl.h deleted file mode 100644 index 37ec8f3f..00000000 --- a/src/geturl.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Icecast - * - * 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). - */ - -/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */ -#ifndef __GETURL_H__ -#define __GETURL_H__ - -#include - -#include -#include -#include - -#define NUM_CONNECTIONS 10 -#define NAK 0 -#define ACK 1 -#define YP_RESPONSE_SIZE 2046 -#define YP_SID_SIZE 255 - -struct curl_memory_struct { - char memory[YP_RESPONSE_SIZE]; - size_t size; -}; -struct curl_memory_struct2 { - char sid[YP_SID_SIZE]; - char message[YP_RESPONSE_SIZE]; - int touch_interval; - int response; - size_t size; -}; - -typedef struct tag_curl_connection { - struct curl_memory_struct result; - struct curl_memory_struct2 header_result; - CURL *curl_handle; - int in_use; -} curl_connection; - - -int curl_initialize(); -void curl_shutdown(); -CURL *curl_get_handle(int which); -struct curl_memory_struct *curl_get_result(int which); -struct curl_memory_struct2 *curl_get_header_result(int which); -void curl_print_header_result(struct curl_memory_struct2 *mem); -int curl_get_connection(); -int curl_release_connection(int which); - -#endif diff --git a/src/main.c b/src/main.c index 79e5c2f4..3b6e82b4 100644 --- a/src/main.c +++ b/src/main.c @@ -48,10 +48,7 @@ #include "logging.h" #include "xslt.h" #include "fserve.h" -#ifdef USE_YP -#include "geturl.h" #include "yp.h" -#endif #include @@ -103,9 +100,6 @@ static void _initialize_subsystems(void) global_initialize(); refbuf_initialize(); xslt_initialize(); -#ifdef USE_YP - curl_initialize(); -#endif } static void _shutdown_subsystems(void) @@ -114,10 +108,8 @@ static void _shutdown_subsystems(void) xslt_shutdown(); refbuf_shutdown(); slave_shutdown(); + yp_shutdown(); stats_shutdown(); -#ifdef USE_YP - curl_shutdown(); -#endif /* Now that these are done, we can stop the loggers. */ _stop_logging(); @@ -478,10 +470,8 @@ int main(int argc, char **argv) /* let her rip */ global.running = ICE_RUNNING; -#ifdef USE_YP /* Startup yp thread */ yp_initialize(); -#endif /* Do this after logging init */ slave_initialize(); diff --git a/src/source.c b/src/source.c index c041cbfd..ad207953 100644 --- a/src/source.c +++ b/src/source.c @@ -45,9 +45,6 @@ #include "logging.h" #include "cfgfile.h" #include "util.h" -#ifdef USE_YP -#include "geturl.h" -#endif #include "source.h" #include "format.h" #include "auth.h" @@ -62,7 +59,7 @@ mutex_t move_clients_mutex; /* avl tree helper */ static int _compare_clients(void *compare_arg, void *a, void *b); static int _free_client(void *key); -static int _parse_audio_info(source_t *source, char *s); +static void _parse_audio_info (source_t *source, const char *s); /* Allocate a new source with the stated mountpoint, if one already * exists with that mountpoint in the global source tree then return @@ -195,9 +192,6 @@ int source_compare_sources(void *arg, void *a, void *b) void source_clear_source (source_t *source) { refbuf_t *refbuf; -#ifdef USE_YP - int i; -#endif DEBUG1 ("clearing source \"%s\"", source->mount); client_destroy(source->client); source->client = NULL; @@ -233,17 +227,9 @@ void source_clear_source (source_t *source) source->format->free_plugin (source->format); } source->format = NULL; -#ifdef USE_YP - for (i=0; inum_yp_directories; i++) - { - yp_destroy_ypdata(source->ypdata[i]); - source->ypdata[i] = NULL; - } - source->num_yp_directories = 0; + if (source->yp_public) + yp_remove (source->mount); - util_dict_free (source->audio_info); - source->audio_info = NULL; -#endif source->queue_size_limit = 0; source->listeners = 0; source->no_mount = 0; @@ -380,108 +366,8 @@ void source_move_clients (source_t *source, source_t *dest) static void source_init (source_t *source) { ice_config_t *config = config_get_config(); - char *listenurl; + char *listenurl, *str; int listen_url_size; -#ifdef USE_YP - char *s; - time_t current_time; - int i; - char *ai; - - for (i=0;inum_yp_directories;i++) { - if (config->yp_url[i]) { - source->ypdata[source->num_yp_directories] = yp_create_ypdata(); - source->ypdata[source->num_yp_directories]->yp_url = - strdup (config->yp_url[i]); - source->ypdata[source->num_yp_directories]->yp_url_timeout = - config->yp_url_timeout[i]; - source->ypdata[source->num_yp_directories]->yp_touch_interval = 0; - source->num_yp_directories++; - } - } - - source->audio_info = util_dict_new(); - /* ice-* is icecast, icy-* is shoutcast */ - if ((s = httpp_getvar(source->parser, "ice-url"))) { - add_yp_info(source, "server_url", s, YP_SERVER_URL); - } - if ((s = httpp_getvar(source->parser, "ice-name"))) { - add_yp_info(source, "server_name", s, YP_SERVER_NAME); - } - if ((s = httpp_getvar(source->parser, "icy-name"))) { - add_yp_info(source, "server_name", s, YP_SERVER_NAME); - } - if ((s = httpp_getvar(source->parser, "ice-url"))) { - add_yp_info(source, "server_url", s, YP_SERVER_URL); - } - if ((s = httpp_getvar(source->parser, "icy-url"))) { - add_yp_info(source, "server_url", s, YP_SERVER_URL); - } - if ((s = httpp_getvar(source->parser, "ice-genre"))) { - add_yp_info(source, "genre", s, YP_SERVER_GENRE); - } - if ((s = httpp_getvar(source->parser, "icy-genre"))) { - add_yp_info(source, "genre", s, YP_SERVER_GENRE); - } - if ((s = httpp_getvar(source->parser, "ice-bitrate"))) { - add_yp_info(source, "bitrate", s, YP_BITRATE); - } - if ((s = httpp_getvar(source->parser, "icy-br"))) { - add_yp_info(source, "bitrate", s, YP_BITRATE); - } - if ((s = httpp_getvar(source->parser, "ice-description"))) { - add_yp_info(source, "server_description", s, YP_SERVER_DESC); - } - if ((s = httpp_getvar(source->parser, "ice-public"))) { - stats_event(source->mount, "public", s); - source->yp_public = atoi(s); - } - if ((s = httpp_getvar(source->parser, "icy-pub"))) { - stats_event(source->mount, "public", s); - source->yp_public = atoi(s); - } - if ((s = httpp_getvar(source->parser, "ice-audio-info"))) { - stats_event(source->mount, "audio_info", s); - if (_parse_audio_info(source, s)) { - ai = util_dict_urlencode(source->audio_info, '&'); - add_yp_info(source, "audio_info", - ai, - YP_AUDIO_INFO); - if (ai) { - free(ai); - } - } - } - for (i=0;inum_yp_directories;i++) { - add_yp_info(source, "server_type", - source->format->format_description, - YP_SERVER_TYPE); - if (source->ypdata[i]->listen_url) { - free(source->ypdata[i]->listen_url); - } - /* 6 for max size of port */ - listen_url_size = strlen("http://") + strlen(config->hostname) + - strlen(":") + 6 + strlen (source->mount) + 1; - source->ypdata[i]->listen_url = malloc (listen_url_size); - sprintf (source->ypdata[i]->listen_url, "http://%s:%d%s", - config->hostname, config->port, source->mount); - } - - if(source->yp_public) { - - current_time = time(NULL); - - for (i=0;inum_yp_directories;i++) { - /* Give the source 5 seconds to update the metadata - before we do our first touch */ - /* Don't permit touch intervals of less than 30 seconds */ - if (source->ypdata[i]->yp_touch_interval <= 30) { - source->ypdata[i]->yp_touch_interval = 30; - } - source->ypdata[i]->yp_last_touch = 0; - } - } -#endif /* 6 for max size of port */ listen_url_size = strlen("http://") + strlen(config->hostname) + @@ -494,6 +380,23 @@ static void source_init (source_t *source) source->burst_on_connect = config->burst_on_connect; config_release_config(); + /* maybe better in connection.c */ + if ((str = httpp_getvar(source->parser, "ice-public"))) + source->yp_public = atoi(str); + if ((str = httpp_getvar(source->parser, "icy-pub"))) + source->yp_public = atoi(str); + if (str == NULL) + str = "0"; + stats_event (source->mount, "public", str); + + str = httpp_getvar(source->parser, "ice-audio-info"); + source->audio_info = util_dict_new(); + if (str) + { + _parse_audio_info (source, str); + stats_event (source->mount, "audio_info", str); + } + stats_event (source->mount, "listenurl", listenurl); if (listenurl) { @@ -546,6 +449,8 @@ static void source_init (source_t *source) avl_tree_unlock(global.source_tree); } + if (source->yp_public) + yp_add (source); } @@ -858,12 +763,6 @@ done: source->running = 0; INFO1("Source \"%s\" exiting", source->mount); -#ifdef USE_YP - if(source->yp_public) { - yp_remove(source); - } -#endif - /* we have de-activated the source now, so no more clients will be * added, now move the listeners we have to the fallback (if any) */ @@ -931,35 +830,36 @@ static int _free_client(void *key) return 1; } -static int _parse_audio_info(source_t *source, char *s) +static void _parse_audio_info (source_t *source, const char *s) { - char *token = NULL; - char *pvar = NULL; - char *variable = NULL; - char *value = NULL; + const char *start = s; + unsigned len; - while ((token = strtok(s,";")) != NULL) { - pvar = strchr(token, '='); - if (pvar) { - variable = (char *)malloc(pvar-token+1); - strncpy(variable, token, pvar-token); - variable[pvar-token] = 0; - pvar++; - if (strlen(pvar)) { - value = util_url_unescape(pvar); - util_dict_set(source->audio_info, variable, value); - stats_event(source->mount, variable, value); - if (value) { - free(value); - } - } - if (variable) { - free(variable); + while (start != NULL && *start != '\0') + { + if ((s = strchr (start, ';')) == NULL) + len = strlen (start); + else + { + len = (int)(s - start); + s++; /* skip passed the ';' */ + } + if (len) + { + char name[100], value[100]; + char *esc; + + sscanf (start, "%199[^=]=%199[^;\r\n]", name, value); + esc = util_url_unescape (value); + if (esc) + { + util_dict_set (source->audio_info, name, esc); + stats_event (source->mount, name, value); + free (esc); } } - s = NULL; + start = s; } - return 1; } diff --git a/src/source.h b/src/source.h index 805f9747..314cf22d 100644 --- a/src/source.h +++ b/src/source.h @@ -43,7 +43,6 @@ typedef struct source_tag avl_tree *pending_tree; rwlock_t *shutdown_rwlock; - ypdata_t *ypdata[MAX_YP_DIRECTORIES]; util_dict *audio_info; char *dumpfilename; /* Name of a file to dump incoming stream to */ diff --git a/src/yp.c b/src/yp.c index 6f1a1ffe..18bce842 100644 --- a/src/yp.c +++ b/src/yp.c @@ -26,421 +26,659 @@ #include "client.h" #include "logging.h" #include "format.h" -#include "geturl.h" #include "source.h" #include "cfgfile.h" #include "stats.h" +#include #define CATMODULE "yp" -static int yp_url_timeout [MAX_YP_DIRECTORIES]; +struct yp_server +{ + char *url; + unsigned url_timeout; + unsigned touch_interval; + int remove; + + CURL *curl; + struct ypdata_tag *mounts, *pending_mounts; + struct yp_server *next; + char curl_error[CURL_ERROR_SIZE]; +}; + + + +typedef struct ypdata_tag +{ + int remove; + int cmd_ok; + + char *sid; + char *mount; + char *url; + char *listen_url; + char *server_name; + char *server_desc; + char *server_genre; + char *cluster_password; + char *bitrate; + char *audio_info; + char *server_type; + char *current_song; + + struct yp_server *server; + time_t next_update; + unsigned touch_interval; + char *error_msg; + unsigned (*process)(struct ypdata_tag *yp, char *s, unsigned len); + + struct ypdata_tag *next; +} ypdata_t; + + +static rwlock_t yp_lock; +static mutex_t yp_pending_lock; + +static struct yp_server *active_yps = NULL, *pending_yps = NULL; +static int yp_update = 0; +static int yp_running; +static time_t now; +static thread_type *yp_thread; + +static void *yp_update_thread(void *arg); +static void add_yp_info (ypdata_t *yp, char *stat_name, void *info, int type); +static unsigned do_yp_remove (ypdata_t *yp, char *s, unsigned len); +static unsigned do_yp_add (ypdata_t *yp, char *s, unsigned len); +static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len); +static void yp_destroy_ypdata(ypdata_t *ypdata); + + +/* curl callback used to parse headers coming back from the YP server */ +static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream) +{ + ypdata_t *yp = stream; + unsigned bytes = size * nmemb; + + /* DEBUG2 ("header from YP is \"%.*s\"", bytes, ptr); */ + if (strncmp (ptr, "YPResponse: 1", 13) == 0) + yp->cmd_ok = 1; + + if (strncmp (ptr, "YPMessage: ", 11) == 0) + { + unsigned len = bytes - 11; + free (yp->error_msg); + yp->error_msg = calloc (1, len); + if (yp->error_msg) + sscanf (ptr, "YPMessage: %[^\r\n]", yp->error_msg); + } + + if (yp->process == do_yp_add) + { + if (strncmp (ptr, "SID: ", 5) == 0) + { + unsigned len = bytes - 5; + free (yp->sid); + yp->sid = calloc (1, len); + if (yp->sid) + sscanf (ptr, "SID: %[^\r\n]", yp->sid); + } + if (strncmp (ptr, "TouchFreq: ", 11) == 0) + { + unsigned secs; + sscanf (ptr, "TouchFreq: %u", &secs); + if (secs < 30) + secs = 30; + DEBUG1 ("server touch interval is %u", secs); + yp->touch_interval = secs; + } + } + return (int)bytes; +} + + +/* capture returned data, but don't do anything with it, shouldn't be any */ +static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream) +{ + return (int)(size*nmemb); +} + + +/* search the active and pending YP server lists */ +static struct yp_server *find_yp_server (const char *url) +{ + struct yp_server *server; + + server = active_yps; + while (server) + { + if (strcmp (server->url, url) == 0) + return server; + server = server->next; + } + server = pending_yps; + while (server) + { + if (strcmp (server->url, url) == 0) + break; + server = server->next; + } + return server; +} + + +static void destroy_yp_server (struct yp_server *server) +{ + if (server == NULL) + return; + DEBUG1 ("Removing YP server entry for %s", server->url); + if (server->curl) + curl_easy_cleanup (server->curl); + if (server->mounts) WARN0 ("active ypdata not freed up"); + if (server->pending_mounts) WARN0 ("pending ypdata not freed up"); + free (server->url); + free (server); +} + + + +/* search for a ypdata entry corresponding to a specific mountpoint */ +static ypdata_t *find_yp_mount (struct yp_server *server, const char *mount) +{ + ypdata_t *yp = server->mounts; + while (yp) + { + if (strcmp (yp->mount, mount) == 0) + break; + yp = yp->next; + } + return yp; +} + void yp_recheck_config (ice_config_t *config) { - memcpy (&config->yp_url_timeout[0], yp_url_timeout, (sizeof yp_url_timeout)); + int i; + struct yp_server *server; + + DEBUG0("Updating YP configuration"); + thread_rwlock_rlock (&yp_lock); + + server = active_yps; + while (server) + { + server->remove = 1; + server = server->next; + } + /* for each yp url in config, check to see if one exists + if not, then add it. */ + for (i=0 ; i < config->num_yp_directories; i++) + { + server = find_yp_server (config->yp_url[i]); + if (server == NULL) + { + server = calloc (1, sizeof (struct yp_server)); + + if (server == NULL) + { + destroy_yp_server (server); + break; + } + server->url = strdup (config->yp_url[i]); + server->url_timeout = config->yp_url_timeout[i]; + server->touch_interval = config->touch_interval; + server->curl = curl_easy_init(); + if (server->curl == NULL) + { + destroy_yp_server (server); + break; + } + if (server->touch_interval < 30) + server->touch_interval = 30; + curl_easy_setopt (server->curl, CURLOPT_URL, server->url); + curl_easy_setopt (server->curl, CURLOPT_HEADERFUNCTION, handle_returned_header); + curl_easy_setopt (server->curl, CURLOPT_WRITEFUNCTION, handle_returned_data); + curl_easy_setopt (server->curl, CURLOPT_WRITEDATA, server->curl); + curl_easy_setopt (server->curl, CURLOPT_TIMEOUT, server->url_timeout); + curl_easy_setopt (server->curl, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt (server->curl, CURLOPT_ERRORBUFFER, &(server->curl_error[0])); + server->next = pending_yps; + pending_yps = server; + INFO3 ("Adding new YP server \"%s\" (timeout %ds, default interval %ds)", + server->url, server->url_timeout, server->touch_interval); + } + else + { + server->remove = 0; + } + } + thread_rwlock_unlock (&yp_lock); + yp_update = 1; } + void yp_initialize() { ice_config_t *config = config_get_config(); + thread_rwlock_create (&yp_lock); + thread_mutex_create (&yp_pending_lock); yp_recheck_config (config); config_release_config (); - thread_create("YP Touch Thread", yp_touch_thread, - (void *)NULL, THREAD_DETACHED); -} -static int yp_submit_url(int curl_con, char *yp_url, char *url, char *type, - int timeout) -{ - int ret = 0; - /* If not specified, use a reasonable timeout - of 30 seconds */ - if (timeout == 0) { - timeout = 30; - } - curl_easy_setopt(curl_get_handle(curl_con), CURLOPT_URL, yp_url); - curl_easy_setopt(curl_get_handle(curl_con), CURLOPT_POSTFIELDS, url); - curl_easy_setopt(curl_get_handle(curl_con), CURLOPT_TIMEOUT, timeout); - /* This is to force libcurl to not use signals for timeouts */ - curl_easy_setopt(curl_get_handle(curl_con), CURLOPT_NOSIGNAL, 1); - /* get it! */ - memset(curl_get_result(curl_con), 0, sizeof(struct curl_memory_struct)); - memset(curl_get_header_result(curl_con), 0, - sizeof(struct curl_memory_struct2)); - - curl_easy_perform(curl_get_handle(curl_con)); - - curl_print_header_result(curl_get_header_result(curl_con)); - - if (curl_get_header_result(curl_con)->response == ACK) { - INFO2("Successfull ACK from %s (%s)", type, yp_url); - ret = 1; - } - else { - if (strlen(curl_get_header_result(curl_con)->message) > 0) { - ERROR3("Got a NAK from %s(%s) (%s)", type, - curl_get_header_result(curl_con)->message, yp_url); - } - else { - ERROR2("Got a NAK from %s(Unknown) (%s)", type, yp_url); - } - ret = 0; - } - return ret; + yp_thread = thread_create("YP Touch Thread", yp_update_thread, + (void *)NULL, THREAD_ATTACHED); } -void *yp_touch_thread(void *arg) + + +/* handler for curl, checks if successful handling occurred */ +static int send_to_yp (const char *cmd, ypdata_t *yp, char *post) { - yp_touch(); + int curlcode; + struct yp_server *server = yp->server; + + /* DEBUG2 ("send YP (%s):%s", cmd, post); */ + yp->cmd_ok = 0; + curl_easy_setopt (server->curl, CURLOPT_POSTFIELDS, post); + curl_easy_setopt (server->curl, CURLOPT_WRITEHEADER, yp); + curlcode = curl_easy_perform (server->curl); + if (curlcode) + { + yp->process = do_yp_add; + yp->next_update += 60; + ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error); + return -1; + } + if (yp->cmd_ok == 0) + { + yp->process = do_yp_add; + yp->next_update += 60; + ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg); + return -1; + } + DEBUG2 ("YP %s at %s succeeded", cmd, server->url); + return 0; +} + + +/* routines for building and issues requests to the YP server */ +static unsigned do_yp_remove (ypdata_t *yp, char *s, unsigned len) +{ + if (yp->sid) + { + int ret = snprintf (s, len, "action=remove&sid=%s", yp->sid); + if (ret >= (signed)len) + return ret+1; + + INFO1 ("clearing up YP entry for %s", yp->mount); + send_to_yp ("remove", yp, s); + yp->remove = 1; + free (yp->sid); + yp->sid = NULL; + yp->process = do_yp_add; + yp_update = 1; + } + return 0; +} + + +static unsigned do_yp_add (ypdata_t *yp, char *s, unsigned len) +{ + int ret = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc=" + "%s&url=%s&listenurl=%s&type=%s&b=%s&%s\r\n", + yp->server_name, yp->server_genre, yp->cluster_password, + yp->server_desc, yp->url, yp->listen_url, + yp->server_type, yp->bitrate, yp->audio_info); + if (ret >= (signed)len) + return ret+1; + if (send_to_yp ("add", yp, s) == 0) + { + yp->process = do_yp_touch; + /* force first touch in 5 secs */ + yp->next_update = time(NULL) + 5; + } + + return 0; +} + + +static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len) +{ + unsigned listeners = 0; + char *val, *artist, *title; + int ret; + + artist = (char *)stats_get_value (yp->mount, "artist"); + title = (char *)stats_get_value (yp->mount, "title"); + if (artist || title) + { + char *song; + char *separator = " - "; + if (artist == NULL) + { + artist = strdup(""); + separator = ""; + } + if (title == NULL) title = strdup(""); + song = malloc (strlen (artist) + strlen (title) + strlen (separator) +1); + if (song) + { + sprintf (song, "%s%s%s", artist, separator, title); + add_yp_info(yp, "yp_currently_playing", song, YP_CURRENT_SONG); + free (song); + } + } + free (artist); + free (title); + + val = (char *)stats_get_value (yp->mount, "listeners"); + if (val) + { + listeners = atoi (val); + free (val); + } + ret = snprintf (s, len, "action=touch&sid=%s&st=%s&listeners=%u\r\n", + yp->sid, yp->current_song, listeners); + + if (ret >= (signed)len) + return ret+1; /* space required for above text and nul*/ + + send_to_yp ("touch", yp, s); + return 0; +} + + + +static void process_ypdata (struct yp_server *server, ypdata_t *yp) +{ + unsigned len = 512; + char *s = NULL, *tmp; + + if (now < yp->next_update) + return; + yp->next_update = now + yp->touch_interval; + + /* loop just in case the memory area isn't big enough */ + while (1) + { + unsigned ret; + if ((tmp = realloc (s, len)) == NULL) + return; + s = tmp; + + ret = yp->process (yp, s, len); + if (ret == 0) + { + free (s); + return; + } + len = ret; + } +} + + +static void yp_process_server (struct yp_server *server) +{ + ypdata_t *yp; + + /* DEBUG1("processing yp server %s", server->url); */ + yp = server->mounts; + while (yp) + { + now = time (NULL); + process_ypdata (server, yp); + yp = yp->next; + } +} + + + +static ypdata_t *create_yp_entry (source_t *source) +{ + ypdata_t *yp; + char *s; + + if (source->running == 0 || source->yp_public == 0) + return NULL; + yp = calloc (1, sizeof (ypdata_t)); + do + { + unsigned len = 512; + int ret; + char *url; + ice_config_t *config; + + if (yp == NULL) + break; + yp->mount = strdup (source->mount); + yp->server_name = strdup (""); + yp->server_desc = strdup (""); + yp->server_genre = strdup (""); + yp->bitrate = strdup (""); + yp->server_desc = strdup (""); + yp->server_type = strdup (""); + yp->cluster_password = strdup (""); + yp->url = strdup (""); + yp->current_song = strdup (""); + yp->audio_info = strdup (""); + yp->process = do_yp_add; + + url = malloc (len); + if (url == NULL) + break; + config = config_get_config(); + ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, source->mount); + if (ret >= (signed)len) + { + s = realloc (url, ++ret); + if (s) url = s; + snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, source->mount); + } + config_release_config(); + yp->listen_url = util_url_escape (url); + free (url); + if (yp->listen_url == NULL) + break; + + /* ice-* is icecast, icy-* is shoutcast */ + add_yp_info (yp, "server_type", source->format->format_description, YP_SERVER_TYPE); + if ((s = httpp_getvar(source->parser, "ice-name"))) { + add_yp_info (yp, "server_name", s, YP_SERVER_NAME); + } + if ((s = httpp_getvar(source->parser, "icy-name"))) { + add_yp_info (yp, "server_name", s, YP_SERVER_NAME); + } + if ((s = httpp_getvar(source->parser, "ice-url"))) { + add_yp_info(yp, "server_url", s, YP_SERVER_URL); + } + if ((s = httpp_getvar(source->parser, "icy-url"))) { + add_yp_info(yp, "server_url", s, YP_SERVER_URL); + } + if ((s = httpp_getvar(source->parser, "ice-genre"))) { + add_yp_info(yp, "genre", s, YP_SERVER_GENRE); + } + if ((s = httpp_getvar(source->parser, "icy-genre"))) { + add_yp_info(yp, "genre", s, YP_SERVER_GENRE); + } + if ((s = httpp_getvar(source->parser, "ice-bitrate"))) { + add_yp_info(yp, "bitrate", s, YP_BITRATE); + } + if ((s = httpp_getvar(source->parser, "icy-br"))) { + add_yp_info(yp, "bitrate", s, YP_BITRATE); + } + if ((s = httpp_getvar(source->parser, "ice-description"))) { + add_yp_info(yp, "server_description", s, YP_SERVER_DESC); + } + s = util_dict_urlencode (source->audio_info, '&'); + if (s) + add_yp_info (yp, "audio_info", s, YP_AUDIO_INFO); + free(s); + return yp; + } while (0); + + yp_destroy_ypdata (yp); return NULL; } -int yp_remove(source_t *source) + +/* Check for changes in the YP servers configured */ +static void check_servers () { - char *url = NULL; - int url_size = 0; - int ret = 0; - int curl_con = 0; - int i = 0; + struct yp_server *server = active_yps, **server_p = &active_yps; - time_t current_time = 0; - - current_time = time(NULL); - - for (i=0; inum_yp_directories; i++) { - source->ypdata[i]->yp_last_touch = current_time; - if (source->ypdata[i]->sid == 0) { - return 0; - } - else { - if (strlen(source->ypdata[i]->sid) == 0) { - return 0; - } - } - if (source->ypdata) { - url_size = strlen("action=remove&sid=") + 1; - url_size += strlen(source->ypdata[i]->sid); - url_size += 1024; - url = malloc(url_size); - sprintf(url, "action=remove&sid=%s", - source->ypdata[i]->sid); - curl_con = curl_get_connection(); - if (curl_con < 0) { - ERROR0("Unable to get auth connection"); - } - else { - /* specify URL to get */ - ret = yp_submit_url(curl_con, source->ypdata[i]->yp_url, - url, "yp_remove", yp_url_timeout[i]); - } - if (url) { - free(url); - } - curl_release_connection(curl_con); - } - } - return 1; -} -int yp_touch() -{ - char *url = NULL; - int url_size = 0; - int ret = 0; - int curl_con = 0; - int i = 0; - int regen_sid = 0; - time_t current_time = 0; - avl_node *node; - source_t *source; - char current_song[256]; - char tyme[128]; - char *s; - - while (global.running == ICE_RUNNING) { - avl_tree_rlock(global.source_tree); - node = avl_get_first(global.source_tree); - while (node) { - source = (source_t *)node->key; - if (source->running == 0) + while (server) + { + if (server->remove) { + struct yp_server *to_go = server; + DEBUG1 ("YP server \"%s\"removed", server->url); + *server_p = server->next; + server = server->next; + destroy_yp_server (to_go); + continue; + } + server_p = &server->next; + server = server->next; + } + /* add new server entries */ + while (pending_yps) + { + avl_node *node; + + server = pending_yps; + pending_yps = server->next; + + DEBUG1("Add pending yps %s", server->url); + server->next = active_yps; + active_yps = server; + + /* new YP server configured, need to populate with existing sources */ + avl_tree_rlock (global.source_tree); + node = avl_get_first (global.source_tree); + while (node) + { + ypdata_t *yp; + + source_t *source = node->key; + if ((yp = create_yp_entry (source)) != NULL) + { + DEBUG1 ("Adding existing mount %s", source->mount); + yp->server = server; + yp->touch_interval = server->touch_interval; + yp->next = server->mounts; + server->mounts = yp; + } node = avl_get_next (node); + } + avl_tree_unlock (global.source_tree); + } +} + + +static void add_pending_yp (struct yp_server *server) +{ + ypdata_t *current, *yp; + unsigned count = 0; + + if (server->pending_mounts == NULL) + return; + current = server->mounts; + server->mounts = server->pending_mounts; + server->pending_mounts = NULL; + yp = server->mounts; + while (1) + { + count++; + if (yp->next == NULL) + break; + yp = yp->next; + } + yp->next = current; + DEBUG2 ("%u YP entries added to %s", count, server->url); +} + + +static void delete_marked_yp (struct yp_server *server) +{ + ypdata_t *yp = server->mounts, **prev = &server->mounts; + + while (yp) + { + if (yp->remove) + { + ypdata_t *to_go = yp; + DEBUG2 ("removed %s from YP server %s", yp->mount, server->url); + *prev = yp->next; + yp = yp->next; + yp_destroy_ypdata (to_go); continue; } - current_time = time(NULL); - if (!source->yp_public) { - node = avl_get_next(node); - continue; - } - for (i=0; inum_yp_directories; i++) { - if (current_time > (source->ypdata[i]->yp_last_touch + - source->ypdata[i]->yp_touch_interval)) { - current_song[0] = 0; - regen_sid = 0; - if ((s = (char *)stats_get_value(source->mount, "artist"))) { - strncat(current_song, s, - sizeof(current_song) - 1); - if (strlen(current_song) + 4 < - sizeof(current_song)) - { - strncat(current_song, " - ", 3); - } - if (s) { - free(s); - } - } - if ((s = (char *)stats_get_value(source->mount, "title"))) { - if (strlen(current_song) + strlen(s) - < sizeof(current_song) -1) - { - strncat(current_song, - s, - sizeof(current_song) - 1 - - strlen(current_song)); - } - if (s) { - free(s); - } - } - add_yp_info(source, "current_song", current_song, - YP_CURRENT_SONG); - - source->ypdata[i]->yp_last_touch = current_time; - if (source->ypdata[i]->sid == 0) { - regen_sid = 1; - } - else { - if (strlen(source->ypdata[i]->sid) == 0) { - regen_sid = 1; - } - } - if (regen_sid) { - yp_add(source, i); - } - if (source->ypdata[i]->sid != 0) { - if (strlen(source->ypdata[i]->sid) != 0) { - if (source->ypdata) { - struct tm tm; - url_size = - strlen("action=touch&sid=&st=&listeners=") + 1; - if (source->ypdata[i]->current_song) { - url_size += - strlen(source->ypdata[i]->current_song); - } - else { - source->ypdata[i]->current_song = - (char *)malloc(1); - source->ypdata[i]->current_song[0] = 0; - } - if (source->ypdata[i]->sid) { - url_size += strlen(source->ypdata[i]->sid); - } - else { - source->ypdata[i]->sid = (char *)malloc(1); - source->ypdata[i]->sid[0] = 0; - } - url_size += 1024; - url = malloc(url_size); - sprintf(url, - "action=touch&sid=%s&st=%s&listeners=%ld", - source->ypdata[i]->sid, - source->ypdata[i]->current_song, - source->listeners); - - curl_con = curl_get_connection(); - if (curl_con < 0) { - ERROR0("Unable to get auth connection"); - } - else { - /* specify URL to get */ - ret = yp_submit_url(curl_con, - source->ypdata[i]->yp_url, - url, "yp_touch", yp_url_timeout[i]); - if (!ret) { - source->ypdata[i]->sid[0] = 0; - } - } - if (url) { - free(url); - } - curl_release_connection(curl_con); - memset(tyme, '\000', sizeof(tyme)); - localtime_r (¤t_time, &tm); - strftime(tyme, 128, "%Y-%m-%d %H:%M:%S", &tm); - stats_event(source->mount, "yp_last_touch", tyme); - source->ypdata[i]->yp_last_touch = current_time; - } - } - } - } - } - node = avl_get_next(node); + prev = &yp->next; + yp = yp->next; } - avl_tree_unlock(global.source_tree); - thread_sleep(200000); - } - - - return 1; } -int yp_add(source_t *source, int which) + + +static void *yp_update_thread(void *arg) { - char *url = NULL; - int url_size = 0; - int ret = 0; - int curl_con = 0; - int i = 0; - int ok = 0; + INFO0("YP update thread started"); - for (i=0; inum_yp_directories; i++) { - if (which != -1) { - if (i == which) { - ok = 1; - } - else { - ok = 0; - } - } - else { - ok = 1; + yp_running = 1; + while (yp_running) + { + struct yp_server *server; + + thread_sleep (200000); + + /* do the YP communication */ + thread_rwlock_rlock (&yp_lock); + server = active_yps; + while (server) + { + /* DEBUG1 ("trying %s", server->url); */ + yp_process_server (server); + server = server->next; } + thread_rwlock_unlock (&yp_lock); - if (ok) { - if (source->ypdata[i]) { - url_size = strlen("action=add&sn=&genre=&cpswd=" - "&desc=&url=&listenurl=&type=&b=&") - + 1; - if (source->ypdata[i]->server_name) { - url_size += strlen(source->ypdata[i]->server_name); - } - else { - source->ypdata[i]->server_name = (char *)malloc(1); - source->ypdata[i]->server_name[0] = 0; - } - if (source->ypdata[i]->server_desc) { - url_size += strlen(source->ypdata[i]->server_desc); - } - else { - source->ypdata[i]->server_desc = (char *)malloc(1); - source->ypdata[i]->server_desc[0] = 0; - } - if (source->ypdata[i]->server_genre) { - url_size += strlen(source->ypdata[i]->server_genre); - } - else { - source->ypdata[i]->server_genre = (char *)malloc(1); - source->ypdata[i]->server_genre[0] = 0; - } - if (source->ypdata[i]->cluster_password) { - url_size += strlen(source->ypdata[i]->cluster_password); - } - else { - source->ypdata[i]->cluster_password = (char *)malloc(1); - source->ypdata[i]->cluster_password[0] = 0; - } - if (source->ypdata[i]->server_url) { - url_size += strlen(source->ypdata[i]->server_url); - } - else { - source->ypdata[i]->server_url = (char *)malloc(1); - source->ypdata[i]->server_url[0] = 0; - } - if (source->ypdata[i]->listen_url) { - url_size += strlen(source->ypdata[i]->listen_url); - } - else { - source->ypdata[i]->listen_url = (char *)malloc(1); - source->ypdata[i]->listen_url[0] = 0; - } - if (source->ypdata[i]->server_type) { - url_size += strlen(source->ypdata[i]->server_type); - } - else { - source->ypdata[i]->server_type = (char *)malloc(1); - source->ypdata[i]->server_type[0] = 0; - } - if (source->ypdata[i]->bitrate) { - url_size += strlen(source->ypdata[i]->bitrate); - } - else { - source->ypdata[i]->bitrate = (char *)malloc(1); - source->ypdata[i]->bitrate[0] = 0; - } - if (source->ypdata[i]->current_song) { - url_size += strlen(source->ypdata[i]->current_song); - } - else { - source->ypdata[i]->current_song = (char *)malloc(1); - source->ypdata[i]->current_song[0] = 0; - } - if (source->ypdata[i]->audio_info) { - url_size += strlen(source->ypdata[i]->audio_info); - } - else { - source->ypdata[i]->audio_info = (char *)malloc(1); - source->ypdata[i]->audio_info[0] = 0; - } - - url_size += 1024; - url = malloc(url_size); - sprintf(url, "action=add&sn=%s&genre=%s&cpswd=%s&desc=" - "%s&url=%s&listenurl=%s&type=%s&b=%s&%s", - source->ypdata[i]->server_name, - source->ypdata[i]->server_genre, - source->ypdata[i]->cluster_password, - source->ypdata[i]->server_desc, - source->ypdata[i]->server_url, - source->ypdata[i]->listen_url, - source->ypdata[i]->server_type, - source->ypdata[i]->bitrate, - source->ypdata[i]->audio_info); - - curl_con = curl_get_connection(); - if (curl_con < 0) { - ERROR0("Unable to get auth connection"); - } - else { - /* specify URL to get */ - ret = yp_submit_url(curl_con, source->ypdata[i]->yp_url, - url, "yp_add", yp_url_timeout[i]); - - if (ret) { - if (strlen(curl_get_header_result(curl_con)->sid) > 0) { - if (source->ypdata) { - if (source->ypdata[i]->sid) { - free(source->ypdata[i]->sid); - source->ypdata[i]->sid = NULL; - } - source->ypdata[i]->sid = (char *)malloc( - strlen(curl_get_header_result(curl_con)-> - sid) +1); - strcpy(source->ypdata[i]->sid, - curl_get_header_result(curl_con)->sid); - source->ypdata[i]->yp_touch_interval = - curl_get_header_result( - curl_con)->touch_interval; - } - } - } - } - if (url) { - free(url); - } - curl_release_connection(curl_con); + /* update the local YP structure */ + if (yp_update) + { + thread_rwlock_wlock (&yp_lock); + check_servers (); + server = active_yps; + while (server) + { + /* DEBUG1 ("Checking yps %s", server->url); */ + add_pending_yp (server); + delete_marked_yp (server); + server = server->next; } + yp_update = 0; + thread_rwlock_unlock (&yp_lock); } } - return 1; + thread_rwlock_destroy (&yp_lock); + thread_mutex_destroy (&yp_pending_lock); + /* free server and ypdata left */ + while (active_yps) + { + struct yp_server *server = active_yps; + active_yps = server->next; + destroy_yp_server (server); + } + + return NULL; } -ypdata_t *yp_create_ypdata() -{ - return calloc(1, sizeof(ypdata_t)); -} -void yp_destroy_ypdata(ypdata_t *ypdata) + +static void yp_destroy_ypdata(ypdata_t *ypdata) { if (ypdata) { - if (ypdata->yp_url) { - free(ypdata->yp_url); + if (ypdata->mount) { + free (ypdata->mount); + } + if (ypdata->url) { + free (ypdata->url); } if (ypdata->sid) { free(ypdata->sid); @@ -457,9 +695,6 @@ void yp_destroy_ypdata(ypdata_t *ypdata) if (ypdata->cluster_password) { free(ypdata->cluster_password); } - if (ypdata->server_url) { - free(ypdata->server_url); - } if (ypdata->listen_url) { free(ypdata->listen_url); } @@ -475,119 +710,181 @@ void yp_destroy_ypdata(ypdata_t *ypdata) if (ypdata->audio_info) { free(ypdata->audio_info); } - free(ypdata); + free (ypdata->error_msg); + free (ypdata); } } -void add_yp_info(source_t *source, char *stat_name, - void *info, int type) +static void add_yp_info (ypdata_t *yp, char *stat_name, void *info, int type) { char *escaped; - int i; - if (!info) { + + if (!info) return; - } - for (i=0;inum_yp_directories;i++) { - switch (type) { + + switch (type) + { case YP_SERVER_NAME: - escaped = util_url_escape(info); - if (escaped) { - if (source->ypdata[i]->server_name) { - free(source->ypdata[i]->server_name); - } - source->ypdata[i]->server_name = - malloc(strlen((char *)escaped) +1); - strcpy(source->ypdata[i]->server_name, (char *)escaped); - stats_event(source->mount, stat_name, (char *)info); - free(escaped); - } - break; + escaped = util_url_escape(info); + if (escaped) + { + if (yp->server_name) + free (yp->server_name); + yp->server_name = escaped; + stats_event (yp->mount, stat_name, (char *)info); + } + break; case YP_SERVER_DESC: - escaped = util_url_escape(info); - if (escaped) { - if (source->ypdata[i]->server_desc) { - free(source->ypdata[i]->server_desc); - } - source->ypdata[i]->server_desc = - malloc(strlen((char *)escaped) +1); - strcpy(source->ypdata[i]->server_desc, (char *)escaped); - stats_event(source->mount, stat_name, (char *)info); - free(escaped); - } - break; + escaped = util_url_escape(info); + if (escaped) + { + if (yp->server_desc) + free (yp->server_desc); + yp->server_desc = escaped; + stats_event(yp->mount, stat_name, (char *)info); + } + break; case YP_SERVER_GENRE: - escaped = util_url_escape(info); - if (escaped) { - if (source->ypdata[i]->server_genre) { - free(source->ypdata[i]->server_genre); - } - source->ypdata[i]->server_genre = - malloc(strlen((char *)escaped) +1); - strcpy(source->ypdata[i]->server_genre, (char *)escaped); - stats_event(source->mount, stat_name, (char *)info); - free(escaped); - } - break; + escaped = util_url_escape(info); + if (escaped) + { + if (yp->server_genre) + free (yp->server_genre); + yp->server_genre = escaped; + stats_event (yp->mount, stat_name, (char *)info); + } + break; case YP_SERVER_URL: - escaped = util_url_escape(info); - if (escaped) { - if (source->ypdata[i]->server_url) { - free(source->ypdata[i]->server_url); - } - source->ypdata[i]->server_url = - malloc(strlen((char *)escaped) +1); - strcpy(source->ypdata[i]->server_url, (char *)escaped); - stats_event(source->mount, stat_name, (char *)info); - free(escaped); - } - break; + escaped = util_url_escape(info); + if (escaped) + { + if (yp->url) + free (yp->url); + yp->url = escaped; + stats_event (yp->mount, stat_name, (char *)info); + } + break; case YP_BITRATE: - escaped = util_url_escape(info); - if (escaped) { - if (source->ypdata[i]->bitrate) { - free(source->ypdata[i]->bitrate); - } - source->ypdata[i]->bitrate = - malloc(strlen((char *)escaped) +1); - strcpy(source->ypdata[i]->bitrate, (char *)escaped); - stats_event(source->mount, stat_name, (char *)info); - free(escaped); - } - break; + escaped = util_url_escape(info); + if (escaped) + { + if (yp->bitrate) + free (yp->bitrate); + yp->bitrate = escaped; + stats_event (yp->mount, stat_name, (char *)info); + } + break; case YP_AUDIO_INFO: - if (source->ypdata[i]->audio_info) { - free(source->ypdata[i]->audio_info); - } - source->ypdata[i]->audio_info = - malloc(strlen((char *)info) +1); - strcpy(source->ypdata[i]->audio_info, (char *)info); - break; + if (yp->audio_info) + free (yp->audio_info); + yp->audio_info = strdup (info); + break; case YP_SERVER_TYPE: - escaped = util_url_escape(info); - if (escaped) { - if (source->ypdata[i]->server_type) { - free(source->ypdata[i]->server_type); - } - source->ypdata[i]->server_type = - malloc(strlen((char *)escaped) +1); - strcpy(source->ypdata[i]->server_type, (char *)escaped); - free(escaped); - } - break; + escaped = util_url_escape(info); + if (escaped) + { + if (yp->server_type) + free (yp->server_type); + yp->server_type = escaped; + } + break; case YP_CURRENT_SONG: - escaped = util_url_escape(info); - if (escaped) { - if (source->ypdata[i]->current_song) { - free(source->ypdata[i]->current_song); - } - source->ypdata[i]->current_song = - malloc(strlen((char *)escaped) +1); - strcpy(source->ypdata[i]->current_song, (char *)escaped); - stats_event(source->mount, "yp_currently_playing", - (char *)info); - free(escaped); - } - break; - } + escaped = util_url_escape(info); + if (escaped) + { + if (yp->current_song) + free (yp->current_song); + yp->current_song = escaped; + stats_event (yp->mount, "yp_currently_playing", (char *)info); + } + break; } } + + +/* Add YP entries to active servers */ +void yp_add (source_t *source) +{ + struct yp_server *server; + + /* make sure YP thread is not modifying the lists */ + thread_rwlock_rlock (&yp_lock); + + /* make sure we don't race against another yp_add */ + thread_mutex_lock (&yp_pending_lock); + server = active_yps; + while (server) + { + ypdata_t *yp; + /* add new ypdata to each servers pending yp */ + if ((yp = create_yp_entry (source)) != NULL) + { + DEBUG2 ("Adding %s to %s", source->mount, server->url); + yp->server = server; + yp->touch_interval = server->touch_interval; + yp->next = server->pending_mounts; + server->pending_mounts = yp; + yp_update = 1; + } + server = server->next; + } + thread_mutex_unlock (&yp_pending_lock); + thread_rwlock_unlock (&yp_lock); + /* DEBUG1 ("Added %s to YP ", source->mount); */ +} + + + +/* Mark an existing entry in the YP list as to be marked for deletion */ +void yp_remove (const char *mount) +{ + struct yp_server *server = active_yps; + + thread_rwlock_rlock (&yp_lock); + while (server) + { + ypdata_t *yp = find_yp_mount (server, mount); + if (yp) + { + yp->process = do_yp_remove; + yp->next_update = 0; + } + server = server->next; + } + thread_rwlock_unlock (&yp_lock); +} + + +/* This is similar to yp_remove, but we force a touch + * attempt */ +void yp_touch (const char *mount) +{ + struct yp_server *server = active_yps; + time_t trigger; + + thread_rwlock_rlock (&yp_lock); + /* do update in 3 secs, give stats chance to update */ + trigger = time(NULL) + 3; + while (server) + { + ypdata_t *yp = find_yp_mount (server, mount); + if (yp) + { + /* only force if touch */ + if (yp->process == do_yp_touch) + yp->next_update = trigger; + } + server = server->next; + } + thread_rwlock_unlock (&yp_lock); +} + + +void yp_shutdown () +{ + yp_running = 0; + yp_update = 1; + thread_join (yp_thread); + curl_global_cleanup(); +} + diff --git a/src/yp.h b/src/yp.h index bd7f835f..87d590d9 100644 --- a/src/yp.h +++ b/src/yp.h @@ -28,39 +28,25 @@ struct source_tag; #define YP_ADD_ALL -1 -typedef struct ypdata_tag -{ - char *sid; - char *server_name; - char *server_desc; - char *server_genre; - char *cluster_password; - char *server_url; - char *listen_url; - char *bitrate; - char *audio_info; - char *server_type; - char *current_song; - char *yp_url; - int yp_url_timeout; - long yp_last_touch; - int yp_touch_interval; -} ypdata_t; -void *yp_touch_thread(void *arg); -int yp_add(struct source_tag *source, int which); -int yp_touch(); -int yp_remove(struct source_tag *psource); -ypdata_t *yp_create_ypdata(); -void yp_destroy_ypdata(ypdata_t *ypdata); -void add_yp_info(struct source_tag *source, char *stat_name, void *info, - int type); #ifdef USE_YP +void yp_add (struct source_tag *source); +void yp_remove (const char *mount); +void yp_touch (const char *mount); void yp_recheck_config (ice_config_t *config); -#else -#define yp_recheck_config(x) do{}while(0) -#endif void yp_initialize(); +void yp_shutdown(); + +#else + +#define yp_add(x) do{}while(0) +#define yp_remove(x) do{}while(0) +#define yp_touch(x) do{}while(0) +#define yp_recheck_config(x) do{}while(0) +#define yp_initialize() do{}while(0) +#define yp_shutdown() do{}while(0) + +#endif /* USE_YP */ #endif