yp.c 26.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/* 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 <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
 */

13
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
14 15 16 17
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

18 19
#include <stdio.h>
#include <string.h>
20
#include <stdlib.h>
21
#include <curl/curl.h>
22

23
#include "thread/thread.h"
24 25 26 27 28 29 30

#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h"
#include "format.h"
#include "source.h"
31
#include "cfgfile.h"
32
#include "stats.h"
33 34 35 36

#ifdef WIN32
#define snprintf _snprintf
#endif
37

Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
38
#define CATMODULE "yp" 
39

40 41 42
struct yp_server
{
    char        *url;
43
    char        *server_id;
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    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;
59
    int release;
60 61 62 63 64 65 66 67 68 69 70 71 72 73
    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;
74
    char *subtype;
75 76 77 78 79

    struct yp_server *server;
    time_t      next_update;
    unsigned    touch_interval;
    char        *error_msg;
80
    int    (*process)(struct ypdata_tag *yp, char *s, unsigned len);
81 82 83 84 85 86 87 88

    struct ypdata_tag *next;
} ypdata_t;


static rwlock_t yp_lock;
static mutex_t yp_pending_lock;

Karl Heyes's avatar
Karl Heyes committed
89
static volatile struct yp_server *active_yps = NULL, *pending_yps = NULL;
90
static volatile int yp_update = 0;
91 92 93
static int yp_running;
static time_t now;
static thread_type *yp_thread;
Karl Heyes's avatar
Karl Heyes committed
94
static volatile unsigned client_limit = 0;
95
static volatile char *server_version = NULL;
96 97

static void *yp_update_thread(void *arg);
98
static void add_yp_info (ypdata_t *yp, void *info, int type);
99 100 101
static int do_yp_remove (ypdata_t *yp, char *s, unsigned len);
static int do_yp_add (ypdata_t *yp, char *s, unsigned len);
static int do_yp_touch (ypdata_t *yp, char *s, unsigned len);
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
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);
        }
134 135 136 137 138 139 140 141 142
    }
    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;
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    }
    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;

160
    server = (struct yp_server *)active_yps;
161 162 163 164 165 166
    while (server)
    {
        if (strcmp (server->url, url) == 0)
            return server;
        server = server->next;
    }
167
    server = (struct yp_server *)pending_yps;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    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);
188
    free (server->server_id);
189 190 191 192 193 194
    free (server);
}



/* search for a ypdata entry corresponding to a specific mountpoint */
195
static ypdata_t *find_yp_mount (ypdata_t *mounts, const char *mount)
196
{
197
    ypdata_t *yp = mounts;
198 199 200 201 202 203 204 205 206
    while (yp)
    {
        if (strcmp (yp->mount, mount) == 0)
            break;
        yp = yp->next;
    }
    return yp;
}

207 208 209

void yp_recheck_config (ice_config_t *config)
{
210 211 212 213 214 215
    int i;
    struct yp_server *server;

    DEBUG0("Updating YP configuration");
    thread_rwlock_rlock (&yp_lock);

216
    server = (struct yp_server *)active_yps;
217 218 219 220 221
    while (server)
    {
        server->remove = 1;
        server = server->next;
    }
Karl Heyes's avatar
Karl Heyes committed
222
    client_limit = config->client_limit;
223 224
    free ((char*)server_version);
    server_version = strdup (config->server_id);
225 226 227 228 229 230 231 232 233 234 235 236 237 238
    /* 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;
            }
239
            server->server_id = strdup ((char *)server_version);
240 241
            server->url = strdup (config->yp_url[i]);
            server->url_timeout = config->yp_url_timeout[i];
242
            server->touch_interval = config->yp_touch_interval[i];
243 244 245 246 247 248 249 250
            server->curl = curl_easy_init();
            if (server->curl == NULL)
            {
                destroy_yp_server (server);
                break;
            }
            if (server->touch_interval < 30)
                server->touch_interval = 30;
251
            curl_easy_setopt (server->curl, CURLOPT_USERAGENT, server->server_id);
252 253 254 255 256 257
            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);
258 259
            curl_easy_setopt (server->curl, CURLOPT_FOLLOWLOCATION, 1L);
            curl_easy_setopt (server->curl, CURLOPT_MAXREDIRS, 3L);
260
            curl_easy_setopt (server->curl, CURLOPT_ERRORBUFFER, &(server->curl_error[0]));
261
            server->next = (struct yp_server *)pending_yps;
262 263 264 265 266 267 268 269 270 271 272
            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;
273 274
}

275

276
void yp_initialize(void)
277
{
278
    ice_config_t *config = config_get_config();
279 280
    thread_rwlock_create (&yp_lock);
    thread_mutex_create (&yp_pending_lock);
281 282
    yp_recheck_config (config);
    config_release_config ();
283 284
    yp_thread = thread_create("YP Touch Thread", yp_update_thread,
                            (void *)NULL, THREAD_ATTACHED);
285
}
286 287 288



289
/* handler for curl, checks if successful handling occurred
290 291
 * return 0 for ok, -1 for this entry failed, -2 for server fail.
 * On failure case, update and process are modified
292
 */
293
static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
294
{
295 296 297 298 299 300 301 302 303 304 305
    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;
306
        yp->next_update = now + 1200;
307
        ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
308
        return -2;
309
    }
310 311
    if (yp->cmd_ok == 0)
    {
312 313
        if (yp->error_msg == NULL)
            yp->error_msg = strdup ("no response from server");
314 315 316
        if (yp->process == do_yp_add)
        {
            ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
317
            yp->next_update = now + 7200;
318
        }
319
        if (yp->process == do_yp_touch)
320
        {
321 322 323 324 325 326 327
            /* At this point the touch request failed, either because they rejected our session
             * or the server isn't accessible. This means we have to wait before doing another
             * add request. We have a minimum delay but we could allow the directory server to
             * give us a wait time using the TouchFreq header. This time could be given in such
             * cases as a firewall block or incorrect listenurl.
             */
            if (yp->touch_interval < 1200)
328
                yp->next_update = now + 1200;
329
            else
330
                yp->next_update = now + yp->touch_interval;
331 332
            INFO3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
        }
333
        yp->process = do_yp_add;
334 335
        free (yp->sid);
        yp->sid = NULL;
336
        return -1;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
337
    }
338 339 340 341 342 343
    DEBUG2 ("YP %s at %s succeeded", cmd, server->url);
    return 0;
}


/* routines for building and issues requests to the YP server */
344
static int do_yp_remove (ypdata_t *yp, char *s, unsigned len)
345
{
346 347
    int ret = 0;

348 349
    if (yp->sid)
    {
350
        ret = snprintf (s, len, "action=remove&sid=%s", yp->sid);
351 352 353 354
        if (ret >= (signed)len)
            return ret+1;

        INFO1 ("clearing up YP entry for %s", yp->mount);
355
        ret = send_to_yp ("remove", yp, s);
356 357
        free (yp->sid);
        yp->sid = NULL;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
358
    }
359 360
    yp->remove = 1;
    yp->process = do_yp_add;
361
    yp_update = 1;
362

363
    return ret;
364 365
}

366

367
static int do_yp_add (ypdata_t *yp, char *s, unsigned len)
368
{
369 370 371
    int ret;
    char *value;

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
    value = stats_get_value (yp->mount, "server_type");
    add_yp_info (yp, value, YP_SERVER_TYPE);
    free (value);

    value = stats_get_value (yp->mount, "server_name");
    add_yp_info (yp, value, YP_SERVER_NAME);
    free (value);

    value = stats_get_value (yp->mount, "server_url");
    add_yp_info (yp, value, YP_SERVER_URL);
    free (value);

    value = stats_get_value (yp->mount, "genre");
    add_yp_info (yp, value, YP_SERVER_GENRE);
    free (value);

    value = stats_get_value (yp->mount, "bitrate");
    add_yp_info (yp, value, YP_BITRATE);
    free (value);

    value = stats_get_value (yp->mount, "server_description");
    add_yp_info (yp, value, YP_SERVER_DESC);
    free (value);

396
    value = stats_get_value (yp->mount, "subtype");
397 398 399 400 401 402
    add_yp_info (yp, value, YP_SUBTYPE);
    free (value);

    value = stats_get_value (yp->mount, "audio_info");
    add_yp_info (yp, value, YP_AUDIO_INFO);
    free (value);
403 404 405

    ret = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc="
                    "%s&url=%s&listenurl=%s&type=%s&stype=%s&b=%s&%s\r\n",
406 407
                    yp->server_name, yp->server_genre, yp->cluster_password,
                    yp->server_desc, yp->url, yp->listen_url,
408
                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
409 410
    if (ret >= (signed)len)
        return ret+1;
411 412
    ret = send_to_yp ("add", yp, s);
    if (ret == 0)
413 414 415 416 417
    {
        yp->process = do_yp_touch;
        /* force first touch in 5 secs */
        yp->next_update = time(NULL) + 5;
    }
418
    return ret;
419 420 421
}


422
static int do_yp_touch (ypdata_t *yp, char *s, unsigned len)
423
{
Karl Heyes's avatar
Karl Heyes committed
424
    unsigned listeners = 0, max_listeners = 1;
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
    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);
444 445
             add_yp_info(yp, song, YP_CURRENT_SONG);
             stats_event (yp->mount, "yp_currently_playing", song);
446 447 448 449 450 451 452 453 454 455 456 457
             free (song);
         }
    }
    free (artist);
    free (title);

    val = (char *)stats_get_value (yp->mount, "listeners");
    if (val)
    {
        listeners = atoi (val);
        free (val);
    }
Karl Heyes's avatar
Karl Heyes committed
458
    val = stats_get_value (yp->mount, "max_listeners");
459
    if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
Karl Heyes's avatar
Karl Heyes committed
460 461 462
        max_listeners = client_limit;
    else
        max_listeners = atoi (val);
463
    free (val);
Karl Heyes's avatar
Karl Heyes committed
464

465 466 467
    val = stats_get_value (yp->mount, "subtype");
    if (val)
    {
468
        add_yp_info (yp, val, YP_SUBTYPE);
469 470
        free (val);
    }
471 472

    ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
Karl Heyes's avatar
Karl Heyes committed
473
            "&listeners=%u&max_listeners=%u&stype=%s\r\n",
474
            yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
475 476 477 478

    if (ret >= (signed)len)
        return ret+1; /* space required for above text and nul*/

479 480 481 482 483 484
    if (send_to_yp ("touch", yp, s) == 0)
    {
        yp->next_update = now + yp->touch_interval;
        return 0;
    }
    return -1;
485
}
486

487 488


489
static int process_ypdata (struct yp_server *server, ypdata_t *yp)
490
{
491
    unsigned len = 1024;
492
    char *s = NULL, *tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
493

494
    if (now < yp->next_update)
495
        return 0;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
496

497 498 499
    /* loop just in case the memory area isn't big enough */
    while (1)
    {
500
        int ret;
501
        if ((tmp = realloc (s, len)) == NULL)
502
            return 0;
503
        s = tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
504

505 506 507 508 509 510
        if (yp->release)
        {
            yp->process = do_yp_remove;
            yp->next_update = 0;
        }

511
        ret = yp->process (yp, s, len);
512
        if (ret <= 0)
513 514
        {
           free (s);
515
           return ret;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
516
        }
517
        len = ret;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
518
    }
519
    return 0;
520
}
521 522 523


static void yp_process_server (struct yp_server *server)
524
{
525
    ypdata_t *yp;
526
    int state = 0;
527 528 529 530 531 532

    /* DEBUG1("processing yp server %s", server->url); */
    yp = server->mounts;
    while (yp)
    {
        now = time (NULL);
533 534 535 536 537 538 539 540 541 542 543
        /* if one of the streams shows that the server cannot be contacted then mark the
         * other entries for an update later. Assume YP server is dead and skip it for now
         */
        if (state == -2)
        {
            DEBUG2 ("skiping %s on %s", yp->mount, server->url);
            yp->process = do_yp_add;
            yp->next_update += 900;
        }
        else
            state = process_ypdata (server, yp);
544 545 546 547 548 549
        yp = yp->next;
    }
}



550
static ypdata_t *create_yp_entry (const char *mount)
551 552
{
    ypdata_t *yp;
553
    char *s;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
554

555 556 557 558 559 560
    yp = calloc (1, sizeof (ypdata_t));
    do
    {
        unsigned len = 512;
        int ret;
        char *url;
561
        mount_proxy *mountproxy = NULL;
562 563 564 565
        ice_config_t *config;

        if (yp == NULL)
            break;
566
        yp->mount = strdup (mount);
567 568 569 570 571 572 573 574 575
        yp->server_name = strdup ("");
        yp->server_desc = strdup ("");
        yp->server_genre = strdup ("");
        yp->bitrate = strdup ("");
        yp->server_type = strdup ("");
        yp->cluster_password = strdup ("");
        yp->url = strdup ("");
        yp->current_song = strdup ("");
        yp->audio_info = strdup ("");
576
        yp->subtype = strdup ("");
577 578 579 580 581 582
        yp->process = do_yp_add;

        url = malloc (len);
        if (url == NULL)
            break;
        config = config_get_config();
583
        ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, mount);
584
        if (ret >= (signed)len)
585
        {
586 587
            s = realloc (url, ++ret);
            if (s) url = s;
588
            snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, mount);
589
        }
590

591
        mountproxy = config_find_mount (config, mount);
592
        if (mountproxy && mountproxy->cluster_password)
593
            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
594
        config_release_config();
595

596 597 598 599 600 601 602 603 604 605 606 607 608 609
        yp->listen_url = util_url_escape (url);
        free (url);
        if (yp->listen_url == NULL)
            break;

        return yp;
    } while (0);

    yp_destroy_ypdata (yp);
    return NULL;
}


/* Check for changes in the YP servers configured */
610
static void check_servers (void)
611
{
612 613
    struct yp_server *server = (struct yp_server *)active_yps,
                     **server_p = (struct yp_server **)&active_yps;
614 615 616 617 618 619 620 621 622 623

    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);
624
            continue;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
625
        }
626 627 628 629 630 631 632 633
        server_p = &server->next;
        server = server->next;
    }
    /* add new server entries */
    while (pending_yps)
    {
        avl_node *node;

634
        server = (struct yp_server *)pending_yps;
635 636 637
        pending_yps = server->next;

        DEBUG1("Add pending yps %s", server->url);
638
        server->next = (struct yp_server *)active_yps;
639 640 641 642 643 644 645 646 647 648
        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;
649
            if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL)
650 651 652 653 654 655
            {
                DEBUG1 ("Adding existing mount %s", source->mount);
                yp->server = server;
                yp->touch_interval = server->touch_interval;
                yp->next = server->mounts;
                server->mounts = yp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
656
            }
657
            node = avl_get_next (node);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
658
        }
659
        avl_tree_unlock (global.source_tree);
660
    }
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
}


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;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
681
    }
682 683 684 685
    yp->next = current;
    DEBUG2 ("%u YP entries added to %s", count, server->url);
}

686

687 688 689
static void delete_marked_yp (struct yp_server *server)
{
    ypdata_t *yp = server->mounts, **prev = &server->mounts;
690

691 692 693 694 695 696 697 698 699 700 701 702 703 704
    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;
        }
        prev = &yp->next;
        yp = yp->next;
    }
705
}
706 707 708


static void *yp_update_thread(void *arg)
709
{
710 711 712 713 714 715 716 717 718 719 720
    INFO0("YP update thread started");

    yp_running = 1;
    while (yp_running)
    {
        struct yp_server *server;

        thread_sleep (200000);

        /* do the YP communication */
        thread_rwlock_rlock (&yp_lock);
721
        server = (struct yp_server *)active_yps;
722 723 724 725 726
        while (server)
        {
            /* DEBUG1 ("trying %s", server->url); */
            yp_process_server (server);
            server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
727
        }
728 729 730 731 732 733 734
        thread_rwlock_unlock (&yp_lock);

        /* update the local YP structure */
        if (yp_update)
        {
            thread_rwlock_wlock (&yp_lock);
            check_servers ();
735
            server = (struct yp_server *)active_yps;
736 737 738 739 740 741
            while (server)
            {
                /* DEBUG1 ("Checking yps %s", server->url); */
                add_pending_yp (server);
                delete_marked_yp (server);
                server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
742
            }
743 744
            yp_update = 0;
            thread_rwlock_unlock (&yp_lock);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
745 746
        }
    }
747 748 749 750 751
    thread_rwlock_destroy (&yp_lock);
    thread_mutex_destroy (&yp_pending_lock);
    /* free server and ypdata left */
    while (active_yps)
    {
752
        struct yp_server *server = (struct yp_server *)active_yps;
753 754 755
        active_yps = server->next;
        destroy_yp_server (server);
    }
756

757
    return NULL;
758
}
759

760 761 762


static void yp_destroy_ypdata(ypdata_t *ypdata)
763
{
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
764
    if (ypdata) {
765 766 767 768 769
        if (ypdata->mount) {
            free (ypdata->mount);
        }
        if (ypdata->url) {
            free (ypdata->url);
770
        }
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
        if (ypdata->sid) {
            free(ypdata->sid);
        }
        if (ypdata->server_name) {
            free(ypdata->server_name);
        }
        if (ypdata->server_desc) {
            free(ypdata->server_desc);
        }
        if (ypdata->server_genre) {
            free(ypdata->server_genre);
        }
        if (ypdata->cluster_password) {
            free(ypdata->cluster_password);
        }
        if (ypdata->listen_url) {
            free(ypdata->listen_url);
        }
        if (ypdata->current_song) {
            free(ypdata->current_song);
        }
        if (ypdata->bitrate) {
            free(ypdata->bitrate);
        }
        if (ypdata->server_type) {
            free(ypdata->server_type);
        }
798 799 800
        if (ypdata->audio_info) {
            free(ypdata->audio_info);
        }
801
        free (ypdata->subtype);
802 803
        free (ypdata->error_msg);
        free (ypdata);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
804
    }
805
}
806

807
static void add_yp_info (ypdata_t *yp, void *info, int type)
808 809
{
    char *escaped;
810 811

    if (!info)
812
        return;
813

814 815 816 817
    escaped = util_url_escape(info);
    if (escaped == NULL)
        return;

818 819
    switch (type)
    {
820
        case YP_SERVER_NAME:
821 822
            free (yp->server_name);
            yp->server_name = escaped;
823
            break;
824
        case YP_SERVER_DESC:
825 826
            free (yp->server_desc);
            yp->server_desc = escaped;
827
            break;
828
        case YP_SERVER_GENRE:
829 830
            free (yp->server_genre);
            yp->server_genre = escaped;
831
            break;
832
        case YP_SERVER_URL:
833 834
            free (yp->url);
            yp->url = escaped;
835
            break;
836
        case YP_BITRATE:
837 838
            free (yp->bitrate);
            yp->bitrate = escaped;
839
            break;
840
        case YP_AUDIO_INFO:
841 842
            free (yp->audio_info);
            yp->audio_info = escaped;
843
            break;
844
        case YP_SERVER_TYPE:
845 846
            free (yp->server_type);
            yp->server_type = escaped;
847
            break;
848
        case YP_CURRENT_SONG:
849 850
            free (yp->current_song);
            yp->current_song = escaped;
851
            break;
852
        case YP_CLUSTER_PASSWORD:
853 854
            free (yp->cluster_password);
            yp->cluster_password = escaped;
855
            break;
856
        case YP_SUBTYPE:
857 858
            free (yp->subtype);
            yp->subtype = escaped;
859
            break;
860 861
        default:
            free (escaped);
862 863 864 865 866
    }
}


/* Add YP entries to active servers */
867
void yp_add (const char *mount)
868 869 870 871 872 873 874 875
{
    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);
876
    server = (struct yp_server *)active_yps;
877 878 879
    while (server)
    {
        ypdata_t *yp;
880 881 882 883

        /* on-demand relays may already have a YP entry */
        yp = find_yp_mount (server->mounts, mount);
        if (yp == NULL)
884
        {
885 886 887 888 889 890 891 892
            /* add new ypdata to each servers pending yp */
            yp = create_yp_entry (mount);
            if (yp)
            {
                DEBUG2 ("Adding %s to %s", mount, server->url);
                yp->server = server;
                yp->touch_interval = server->touch_interval;
                yp->next = server->pending_mounts;
893
                yp->next_update = time(NULL) + 60;
894 895 896
                server->pending_mounts = yp;
                yp_update = 1;
            }
897
        }
898 899
        else
            DEBUG1 ("YP entry %s already exists", mount);
900 901 902 903 904 905 906 907 908 909 910
        server = server->next;
    }
    thread_mutex_unlock (&yp_pending_lock);
    thread_rwlock_unlock (&yp_lock);
}



/* Mark an existing entry in the YP list as to be marked for deletion */
void yp_remove (const char *mount)
{
911
    struct yp_server *server = (struct yp_server *)active_yps;
912 913 914 915

    thread_rwlock_rlock (&yp_lock);
    while (server)
    {
916 917 918
        ypdata_t *list = server->mounts;

        while (1)
919
        {
920 921 922 923 924 925 926 927
            ypdata_t *yp = find_yp_mount (list, mount);
            if (yp == NULL)
                break;
            if (yp->release || yp->remove)
            {
                list = yp->next;
                continue;   /* search again these are old entries */
            }
928 929
            DEBUG2 ("release %s on YP %s", mount, server->url);
            yp->release = 1;
930
            yp->next_update = 0;
931
        }
932
        server = server->next;
933
    }
934
    thread_rwlock_unlock (&yp_lock);
935
}
936 937 938 939 940 941


/* This is similar to yp_remove, but we force a touch
 * attempt */
void yp_touch (const char *mount)
{
942
    struct yp_server *server = (struct yp_server *)active_yps;
943
    time_t trigger;
944
    ypdata_t *search_list = NULL;
945 946 947 948

    thread_rwlock_rlock (&yp_lock);
    /* do update in 3 secs, give stats chance to update */
    trigger = time(NULL) + 3;
949 950 951
    if (server)
        search_list = server->mounts;

952 953
    while (server)
    {
954
        ypdata_t *yp = find_yp_mount (search_list, mount);
955 956
        if (yp)
        {
957
            /* we may of found old entries not purged yet, so skip them */
958
            if (yp->release != 0 || yp->remove != 0)
959 960 961 962
            {
                search_list = yp->next;
                continue;
            }
963 964 965 966 967
            /* only force if touch */
            if (yp->process == do_yp_touch)
                yp->next_update = trigger;
        }
        server = server->next;
968 969
        if (server)
            search_list = server->mounts;
970 971 972 973 974
    }
    thread_rwlock_unlock (&yp_lock);
}


975
void yp_shutdown (void)
976 977 978
{
    yp_running = 0;
    yp_update = 1;
979 980
    if (yp_thread)
        thread_join (yp_thread);
981
    curl_global_cleanup();
982 983
    free ((char*)server_version);
    server_version = NULL;
984
    INFO0 ("YP thread down");
985 986
}