yp.c 26.5 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
            server->curl = curl_easy_init();
            if (server->curl == NULL)
            {
                destroy_yp_server (server);
                break;
            }
249 250
            if (server->url_timeout > 10 || server->url_timeout < 1)
                server->url_timeout = 6;
251 252
            if (server->touch_interval < 30)
                server->touch_interval = 30;
253
            curl_easy_setopt (server->curl, CURLOPT_USERAGENT, server->server_id);
254 255 256 257 258 259
            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);
260 261
            curl_easy_setopt (server->curl, CURLOPT_FOLLOWLOCATION, 1L);
            curl_easy_setopt (server->curl, CURLOPT_MAXREDIRS, 3L);
262
            curl_easy_setopt (server->curl, CURLOPT_ERRORBUFFER, &(server->curl_error[0]));
263
            server->next = (struct yp_server *)pending_yps;
264 265 266 267 268 269 270 271 272 273 274
            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;
275 276
}

277

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



291
/* handler for curl, checks if successful handling occurred
292 293
 * return 0 for ok, -1 for this entry failed, -2 for server fail.
 * On failure case, update and process are modified
294
 */
295
static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
296
{
297 298 299 300 301 302 303 304 305 306 307
    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;
308
        yp->next_update = now + 1200;
309
        ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
310
        return -2;
311
    }
312 313
    if (yp->cmd_ok == 0)
    {
314 315
        if (yp->error_msg == NULL)
            yp->error_msg = strdup ("no response from server");
316 317 318
        if (yp->process == do_yp_add)
        {
            ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
319
            yp->next_update = now + 7200;
320
        }
321
        if (yp->process == do_yp_touch)
322
        {
323 324 325 326 327 328 329
            /* 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)
330
                yp->next_update = now + 1200;
331
            else
332
                yp->next_update = now + yp->touch_interval;
333 334
            INFO3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
        }
335
        yp->process = do_yp_add;
336 337
        free (yp->sid);
        yp->sid = NULL;
338
        return -1;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
339
    }
340 341 342 343 344 345
    DEBUG2 ("YP %s at %s succeeded", cmd, server->url);
    return 0;
}


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

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

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

365
    return ret;
366 367
}

368

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

374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
    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");
391 392
    if (value == NULL)
        value = stats_get_value (yp->mount, "ice-bitrate");
393 394 395 396 397 398 399
    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);

400
    value = stats_get_value (yp->mount, "subtype");
401 402 403 404 405 406
    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);
407 408 409

    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",
410 411
                    yp->server_name, yp->server_genre, yp->cluster_password,
                    yp->server_desc, yp->url, yp->listen_url,
412
                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
413 414
    if (ret >= (signed)len)
        return ret+1;
415 416
    ret = send_to_yp ("add", yp, s);
    if (ret == 0)
417 418 419 420 421
    {
        yp->process = do_yp_touch;
        /* force first touch in 5 secs */
        yp->next_update = time(NULL) + 5;
    }
422
    return ret;
423 424 425
}


426
static int do_yp_touch (ypdata_t *yp, char *s, unsigned len)
427
{
Karl Heyes's avatar
Karl Heyes committed
428
    unsigned listeners = 0, max_listeners = 1;
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    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);
448 449
             add_yp_info(yp, song, YP_CURRENT_SONG);
             stats_event (yp->mount, "yp_currently_playing", song);
450 451 452 453 454 455 456 457 458 459 460 461
             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
462
    val = stats_get_value (yp->mount, "max_listeners");
463
    if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
Karl Heyes's avatar
Karl Heyes committed
464 465 466
        max_listeners = client_limit;
    else
        max_listeners = atoi (val);
467
    free (val);
Karl Heyes's avatar
Karl Heyes committed
468

469 470 471
    val = stats_get_value (yp->mount, "subtype");
    if (val)
    {
472
        add_yp_info (yp, val, YP_SUBTYPE);
473 474
        free (val);
    }
475 476

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

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

483 484 485 486 487 488
    if (send_to_yp ("touch", yp, s) == 0)
    {
        yp->next_update = now + yp->touch_interval;
        return 0;
    }
    return -1;
489
}
490

491 492


493
static int process_ypdata (struct yp_server *server, ypdata_t *yp)
494
{
495
    unsigned len = 1024;
496
    char *s = NULL, *tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
497

498
    if (now < yp->next_update)
499
        return 0;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
500

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

509 510 511 512 513 514
        if (yp->release)
        {
            yp->process = do_yp_remove;
            yp->next_update = 0;
        }

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


static void yp_process_server (struct yp_server *server)
528
{
529
    ypdata_t *yp;
530
    int state = 0;
531 532 533 534 535 536

    /* DEBUG1("processing yp server %s", server->url); */
    yp = server->mounts;
    while (yp)
    {
        now = time (NULL);
537 538 539 540 541 542 543 544 545 546 547
        /* 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);
548 549 550 551 552 553
        yp = yp->next;
    }
}



554
static ypdata_t *create_yp_entry (const char *mount)
555 556
{
    ypdata_t *yp;
557
    char *s;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
558

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

        if (yp == NULL)
            break;
570
        yp->mount = strdup (mount);
571 572 573 574 575 576 577 578 579
        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 ("");
580
        yp->subtype = strdup ("");
581 582 583 584 585 586
        yp->process = do_yp_add;

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

595
        mountproxy = config_find_mount (config, mount);
596
        if (mountproxy && mountproxy->cluster_password)
597
            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
598
        config_release_config();
599

600 601 602 603 604 605 606 607 608 609 610 611 612 613
        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 */
614
static void check_servers (void)
615
{
616 617
    struct yp_server *server = (struct yp_server *)active_yps,
                     **server_p = (struct yp_server **)&active_yps;
618 619 620 621 622 623 624 625 626 627

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

638
        server = (struct yp_server *)pending_yps;
639 640 641
        pending_yps = server->next;

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


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
685
    }
686 687 688 689
    yp->next = current;
    DEBUG2 ("%u YP entries added to %s", count, server->url);
}

690

691 692 693
static void delete_marked_yp (struct yp_server *server)
{
    ypdata_t *yp = server->mounts, **prev = &server->mounts;
694

695 696 697 698 699 700 701 702 703 704 705 706 707 708
    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;
    }
709
}
710 711 712


static void *yp_update_thread(void *arg)
713
{
714 715 716 717 718 719 720 721 722 723 724
    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);
725
        server = (struct yp_server *)active_yps;
726 727 728 729 730
        while (server)
        {
            /* DEBUG1 ("trying %s", server->url); */
            yp_process_server (server);
            server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
731
        }
732 733 734 735 736 737 738
        thread_rwlock_unlock (&yp_lock);

        /* update the local YP structure */
        if (yp_update)
        {
            thread_rwlock_wlock (&yp_lock);
            check_servers ();
739
            server = (struct yp_server *)active_yps;
740 741 742 743 744 745
            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
746
            }
747 748
            yp_update = 0;
            thread_rwlock_unlock (&yp_lock);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
749 750
        }
    }
751 752 753 754 755
    thread_rwlock_destroy (&yp_lock);
    thread_mutex_destroy (&yp_pending_lock);
    /* free server and ypdata left */
    while (active_yps)
    {
756
        struct yp_server *server = (struct yp_server *)active_yps;
757 758 759
        active_yps = server->next;
        destroy_yp_server (server);
    }
760

761
    return NULL;
762
}
763

764 765 766


static void yp_destroy_ypdata(ypdata_t *ypdata)
767
{
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
768
    if (ypdata) {
769 770 771 772 773
        if (ypdata->mount) {
            free (ypdata->mount);
        }
        if (ypdata->url) {
            free (ypdata->url);
774
        }
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
        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);
        }
802 803 804
        if (ypdata->audio_info) {
            free(ypdata->audio_info);
        }
805
        free (ypdata->subtype);
806 807
        free (ypdata->error_msg);
        free (ypdata);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
808
    }
809
}
810

811
static void add_yp_info (ypdata_t *yp, void *info, int type)
812 813
{
    char *escaped;
814 815

    if (!info)
816
        return;
817

818 819 820 821
    escaped = util_url_escape(info);
    if (escaped == NULL)
        return;

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


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

        /* on-demand relays may already have a YP entry */
        yp = find_yp_mount (server->mounts, mount);
        if (yp == NULL)
888
        {
889 890 891 892 893 894 895 896
            /* 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;
897
                yp->next_update = time(NULL) + 60;
898 899 900
                server->pending_mounts = yp;
                yp_update = 1;
            }
901
        }
902 903
        else
            DEBUG1 ("YP entry %s already exists", mount);
904 905 906 907 908 909 910 911 912 913 914
        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)
{
915
    struct yp_server *server = (struct yp_server *)active_yps;
916 917 918 919

    thread_rwlock_rlock (&yp_lock);
    while (server)
    {
920 921 922
        ypdata_t *list = server->mounts;

        while (1)
923
        {
924 925 926 927 928 929 930 931
            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 */
            }
932 933
            DEBUG2 ("release %s on YP %s", mount, server->url);
            yp->release = 1;
934
            yp->next_update = 0;
935
        }
936
        server = server->next;
937
    }
938
    thread_rwlock_unlock (&yp_lock);
939
}
940 941 942 943 944 945


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

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

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


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