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
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); */
112
    if (strncasecmp (ptr, "YPResponse: 1", 13) == 0)
113 114
        yp->cmd_ok = 1;

115
    if (strncasecmp (ptr, "YPMessage: ", 11) == 0)
116 117 118 119 120
    {
        unsigned len = bytes - 11;
        free (yp->error_msg);
        yp->error_msg = calloc (1, len);
        if (yp->error_msg)
121
            sscanf (ptr + 11, "%[^\r\n]", yp->error_msg);
122 123 124 125
    }

    if (yp->process == do_yp_add)
    {
126
        if (strncasecmp (ptr, "SID: ", 5) == 0)
127 128 129 130 131
        {
            unsigned len = bytes - 5;
            free (yp->sid);
            yp->sid = calloc (1, len);
            if (yp->sid)
132
                sscanf (ptr + 5, "%[^\r\n]", yp->sid);
133
        }
134
    }
135
    if (strncasecmp (ptr, "TouchFreq: ", 11) == 0)
136 137
    {
        unsigned secs;
138 139
        if ( sscanf (ptr + 11, "%u", &secs) != 1 )
            secs = 0;
140 141 142 143
        if (secs < 30)
            secs = 30;
        DEBUG1 ("server touch interval is %u", secs);
        yp->touch_interval = secs;
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    }
    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;

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



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

208 209 210

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

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

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

278

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



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


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

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

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

366
    return ret;
367 368
}

369

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

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

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

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


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

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

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

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

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

492 493


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

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

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

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

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


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

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



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

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

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

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

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

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

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

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

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


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

691

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

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


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

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

762
    return NULL;
763
}
764

765 766 767


static void yp_destroy_ypdata(ypdata_t *ypdata)
768
{
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
769
    if (ypdata) {
770 771 772 773 774
        if (ypdata->mount) {
            free (ypdata->mount);
        }
        if (ypdata->url) {
            free (ypdata->url);
775
        }
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
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 802
        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);
        }
803 804 805
        if (ypdata->audio_info) {
            free(ypdata->audio_info);
        }
806
        free (ypdata->subtype);
807 808
        free (ypdata->error_msg);
        free (ypdata);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
809
    }
810
}
811

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

    if (!info)
817
        return;
818

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

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


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

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

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

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


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

    thread_rwlock_rlock (&yp_lock);
951 952 953
    if (server)
        search_list = server->mounts;

954 955
    while (server)
    {
956
        ypdata_t *yp = find_yp_mount (search_list, mount);
957 958
        if (yp)
        {
959
            /* we may of found old entries not purged yet, so skip them */
960
            if (yp->release != 0 || yp->remove != 0)
961 962 963 964
            {
                search_list = yp->next;
                continue;
            }
965 966 967
            /* don't update the directory if there is a touch scheduled soon */
            if (yp->process == do_yp_touch && now + yp->touch_interval - yp->next_update > 60)
                yp->next_update = now + 3;
968 969
        }
        server = server->next;
970 971
        if (server)
            search_list = server->mounts;
972 973 974 975 976
    }
    thread_rwlock_unlock (&yp_lock);
}


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