yp.c 26.5 KB
Newer Older
1 2 3 4 5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 8 9 10 11 12
 *                      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

Marvin Scholz's avatar
Marvin Scholz committed
22
#include "common/thread/thread.h"
23

24
#include "global.h"
25
#include "curl.h"
26 27 28 29 30 31
#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h"
#include "format.h"
#include "source.h"
32
#include "cfgfile.h"
33
#include "stats.h"
34 35 36 37

#ifdef WIN32
#define snprintf _snprintf
#endif
38

39
#define CATMODULE "yp"
40

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

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

    struct ypdata_tag *next;
} ypdata_t;


static rwlock_t yp_lock;
static mutex_t yp_pending_lock;

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

static void *yp_update_thread(void *arg);
99 100 101 102
static void add_yp_info(ypdata_t *yp, void *info, int type);
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);
103 104
static void add_pending_yp (struct yp_server *server);
static void delete_marked_yp(struct yp_server *server);
105 106 107 108
static void yp_destroy_ypdata(ypdata_t *ypdata);


/* curl callback used to parse headers coming back from the YP server */
109
static size_t handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
110 111
{
    ypdata_t *yp = stream;
112
    size_t bytes = size * nmemb;
113

114
    /* ICECAST_LOG_DEBUG("header from YP is \"%.*s\"", bytes, ptr); */
115
    if (strncasecmp (ptr, "YPResponse: 1", 13) == 0)
116 117
        yp->cmd_ok = 1;

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

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


/* search the active and pending YP server lists */
static struct yp_server *find_yp_server (const char *url)
{
    struct yp_server *server;

157
    server = (struct yp_server *)active_yps;
158 159 160 161 162 163
    while (server)
    {
        if (strcmp (server->url, url) == 0)
            return server;
        server = server->next;
    }
164
    server = (struct yp_server *)pending_yps;
165 166 167 168 169 170 171 172 173 174 175 176
    while (server)
    {
        if (strcmp (server->url, url) == 0)
            break;
        server = server->next;
    }
    return server;
}


static void destroy_yp_server (struct yp_server *server)
{
177 178
    ypdata_t *yp;

179 180
    if (server == NULL)
        return;
181
    ICECAST_LOG_DEBUG("Removing YP server entry for %s", server->url);
182 183 184 185 186 187 188 189 190 191 192 193 194 195

    /* delete yps:
     * first move all pendings into main queue.
     * then mark all main queue entries for deleting.
     * then remove all marked entries.
     */
    add_pending_yp(server);
    yp = server->mounts;
    while (yp) {
        yp->remove = 1;
        yp = yp->next;
    }
    delete_marked_yp(server);

196
    icecast_curl_free(server->curl);
197 198
    if (server->mounts) ICECAST_LOG_WARN("active ypdata not freed");
    if (server->pending_mounts) ICECAST_LOG_WARN("pending ypdata not freed");
199
    free (server->url);
200
    free (server->server_id);
201 202 203 204 205 206
    free (server);
}



/* search for a ypdata entry corresponding to a specific mountpoint */
207
static ypdata_t *find_yp_mount (ypdata_t *mounts, const char *mount)
208
{
209
    ypdata_t *yp = mounts;
210 211 212 213 214 215 216 217 218
    while (yp)
    {
        if (strcmp (yp->mount, mount) == 0)
            break;
        yp = yp->next;
    }
    return yp;
}

219 220 221

void yp_recheck_config (ice_config_t *config)
{
222 223 224
    int i;
    struct yp_server *server;

225
    ICECAST_LOG_DEBUG("Updating YP configuration");
226 227
    thread_rwlock_rlock (&yp_lock);

228
    server = (struct yp_server *)active_yps;
229 230 231 232 233
    while (server)
    {
        server->remove = 1;
        server = server->next;
    }
Karl Heyes's avatar
Karl Heyes committed
234
    client_limit = config->client_limit;
235 236
    free ((char*)server_version);
    server_version = strdup (config->server_id);
237
    /* for each yp url in config, check to see if one exists
238 239 240 241 242 243 244 245 246 247 248 249 250
       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;
            }
251
            server->server_id = strdup ((char *)server_version);
252 253
            server->url = strdup (config->yp_url[i]);
            server->url_timeout = config->yp_url_timeout[i];
254
            server->touch_interval = config->yp_touch_interval[i];
255
            server->curl = icecast_curl_new(server->url, &(server->curl_error[0]));
256 257 258 259 260
            if (server->curl == NULL)
            {
                destroy_yp_server (server);
                break;
            }
261 262
            if (server->url_timeout > 10 || server->url_timeout < 1)
                server->url_timeout = 6;
263 264 265
            if (server->touch_interval < 30)
                server->touch_interval = 30;
            curl_easy_setopt (server->curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
266
            server->next = (struct yp_server *)pending_yps;
267
            pending_yps = server;
268
            ICECAST_LOG_INFO("Adding new YP server \"%s\" (timeout %ds, default interval %ds)",
269 270 271 272 273 274 275 276 277
                    server->url, server->url_timeout, server->touch_interval);
        }
        else
        {
            server->remove = 0;
        }
    }
    thread_rwlock_unlock (&yp_lock);
    yp_update = 1;
278 279
}

280

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



294
/* handler for curl, checks if successful handling occurred
295 296
 * return 0 for ok, -1 for this entry failed, -2 for server fail.
 * On failure case, update and process are modified
297
 */
298
static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
299
{
300 301 302
    int curlcode;
    struct yp_server *server = yp->server;

303
    /* ICECAST_LOG_DEBUG("send YP (%s):%s", cmd, post); */
304 305 306 307 308 309 310
    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;
311
        yp->next_update = now + 1200;
312
        ICECAST_LOG_ERROR("connection to %s failed with \"%s\"", server->url, server->curl_error);
313
        return -2;
314
    }
315 316
    if (yp->cmd_ok == 0)
    {
317 318
        if (yp->error_msg == NULL)
            yp->error_msg = strdup ("no response from server");
319 320
        if (yp->process == do_yp_add)
        {
321
            ICECAST_LOG_ERROR("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
322
            yp->next_update = now + 7200;
323
        }
324
        if (yp->process == do_yp_touch)
325
        {
326 327 328 329 330 331 332
            /* 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)
333
                yp->next_update = now + 1200;
334
            else
335
                yp->next_update = now + yp->touch_interval;
336
            ICECAST_LOG_INFO("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
337
        }
338
        yp->process = do_yp_add;
339
        free(yp->sid);
340
        yp->sid = NULL;
341
        return -1;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
342
    }
343
    ICECAST_LOG_DEBUG("YP %s at %s succeeded", cmd, server->url);
344 345 346 347 348
    return 0;
}


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

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

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

368
    return ret;
369 370
}

371

372
static int do_yp_add (ypdata_t *yp, char *s, unsigned len)
373
{
374 375
    int ret;
    char *value;
376 377 378 379 380 381
    ice_config_t *config;
    char *admin;

    config = config_get_config();
    admin = util_url_escape(config->admin);
    config_release_config();
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
    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");
400 401
    if (value == NULL)
        value = stats_get_value (yp->mount, "ice-bitrate");
402 403 404 405 406 407 408
    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);

409
    value = stats_get_value (yp->mount, "subtype");
410 411 412 413 414 415
    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);
416

417
    ret = snprintf (s, len, "action=add&admin=%s&sn=%s&genre=%s&cpswd=%s&desc="
418
                    "%s&url=%s&listenurl=%s&type=%s&stype=%s&b=%s&%s\r\n",
419
                    admin,
420 421
                    yp->server_name, yp->server_genre, yp->cluster_password,
                    yp->server_desc, yp->url, yp->listen_url,
422
                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
423 424
    free(admin);

425 426
    if (ret >= (signed)len)
        return ret+1;
427 428
    ret = send_to_yp ("add", yp, s);
    if (ret == 0)
429 430 431 432 433
    {
        yp->process = do_yp_touch;
        /* force first touch in 5 secs */
        yp->next_update = time(NULL) + 5;
    }
434
    return ret;
435 436 437
}


438
static int do_yp_touch (ypdata_t *yp, char *s, unsigned len)
439
{
Karl Heyes's avatar
Karl Heyes committed
440
    unsigned listeners = 0, max_listeners = 1;
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;
Philipp Schafft's avatar
Philipp Schafft committed
449
         const char *separator = " - ";
450 451 452 453 454 455 456 457 458 459
         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);
460 461
             add_yp_info(yp, song, YP_CURRENT_SONG);
             stats_event (yp->mount, "yp_currently_playing", song);
462 463 464 465 466 467 468 469 470 471 472 473
             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
474
    val = stats_get_value (yp->mount, "max_listeners");
475
    if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
Karl Heyes's avatar
Karl Heyes committed
476 477 478
        max_listeners = client_limit;
    else
        max_listeners = atoi (val);
479
    free (val);
Karl Heyes's avatar
Karl Heyes committed
480

481 482 483
    val = stats_get_value (yp->mount, "subtype");
    if (val)
    {
484
        add_yp_info (yp, val, YP_SUBTYPE);
485 486
        free (val);
    }
487 488

    ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
Karl Heyes's avatar
Karl Heyes committed
489
            "&listeners=%u&max_listeners=%u&stype=%s\r\n",
490
            yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
491 492 493 494

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

495 496 497 498 499 500
    if (send_to_yp ("touch", yp, s) == 0)
    {
        yp->next_update = now + yp->touch_interval;
        return 0;
    }
    return -1;
501
}
502

503 504


505
static int process_ypdata(struct yp_server *server, ypdata_t *yp)
506
{
507
    unsigned len = 1024;
508
    char *s = NULL, *tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
509

510
    if (now < yp->next_update)
511
        return 0;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
512

513 514 515
    /* loop just in case the memory area isn't big enough */
    while (1)
    {
516
        int ret;
517
        if ((tmp = realloc(s, len)) == NULL)
518
            return 0;
519
        s = tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
520

521 522 523 524 525 526
        if (yp->release)
        {
            yp->process = do_yp_remove;
            yp->next_update = 0;
        }

527
        ret = yp->process (yp, s, len);
528
        if (ret <= 0)
529 530
        {
           free (s);
531
           return ret;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
532
        }
533
        len = ret;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
534
    }
535
    return 0;
536
}
537 538 539


static void yp_process_server (struct yp_server *server)
540
{
541
    ypdata_t *yp;
542
    int state = 0;
543

544
    /* ICECAST_LOG_DEBUG("processing yp server %s", server->url); */
545 546 547 548
    yp = server->mounts;
    while (yp)
    {
        now = time (NULL);
549 550 551 552 553
        /* 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)
        {
554
            ICECAST_LOG_DEBUG("skipping %s on %s", yp->mount, server->url);
555 556 557 558 559
            yp->process = do_yp_add;
            yp->next_update += 900;
        }
        else
            state = process_ypdata (server, yp);
560 561 562 563 564 565
        yp = yp->next;
    }
}



566
static ypdata_t *create_yp_entry (const char *mount)
567 568
{
    ypdata_t *yp;
569
    char *s;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
570

571 572 573 574 575 576
    yp = calloc (1, sizeof (ypdata_t));
    do
    {
        unsigned len = 512;
        int ret;
        char *url;
577
        mount_proxy *mountproxy = NULL;
578 579 580 581
        ice_config_t *config;

        if (yp == NULL)
            break;
582
        yp->mount = strdup (mount);
583 584 585 586 587 588 589 590 591
        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 ("");
592
        yp->subtype = strdup ("");
593 594 595 596 597 598
        yp->process = do_yp_add;

        url = malloc (len);
        if (url == NULL)
            break;
        config = config_get_config();
599
        ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, mount);
600
        if (ret >= (signed)len)
601
        {
602 603
            s = realloc (url, ++ret);
            if (s) url = s;
604
            snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, mount);
605
        }
606

607
        mountproxy = config_find_mount (config, mount, MOUNT_TYPE_NORMAL);
608
        if (mountproxy && mountproxy->cluster_password)
609
            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
610
        config_release_config();
611

612 613 614 615 616 617 618 619 620 621 622 623 624 625
        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 */
626
static void check_servers (void)
627
{
628 629
    struct yp_server *server = (struct yp_server *)active_yps,
                     **server_p = (struct yp_server **)&active_yps;
630 631 632 633 634 635

    while (server)
    {
        if (server->remove)
        {
            struct yp_server *to_go = server;
636
            ICECAST_LOG_DEBUG("YP server \"%s\"removed", server->url);
637 638
            *server_p = server->next;
            server = server->next;
639
            destroy_yp_server(to_go);
640
            continue;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
641
        }
642 643 644 645 646 647 648 649
        server_p = &server->next;
        server = server->next;
    }
    /* add new server entries */
    while (pending_yps)
    {
        avl_node *node;

650
        server = (struct yp_server *)pending_yps;
651 652
        pending_yps = server->next;

653
        ICECAST_LOG_DEBUG("Add pending yps %s", server->url);
654
        server->next = (struct yp_server *)active_yps;
655 656 657 658 659 660 661 662 663 664
        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;
665
            if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL)
666
            {
667
                ICECAST_LOG_DEBUG("Adding existing mount %s", source->mount);
668 669 670 671
                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
672
            }
673
            node = avl_get_next (node);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
674
        }
675
        avl_tree_unlock (global.source_tree);
676
    }
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
}


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
697
    }
698
    yp->next = current;
699
    ICECAST_LOG_DEBUG("%u YP entries added to %s", count, server->url);
700 701
}

702

703
static void delete_marked_yp(struct yp_server *server)
704 705
{
    ypdata_t *yp = server->mounts, **prev = &server->mounts;
706

707 708 709 710 711
    while (yp)
    {
        if (yp->remove)
        {
            ypdata_t *to_go = yp;
712
            ICECAST_LOG_DEBUG("removed %s from YP server %s", yp->mount, server->url);
713 714
            *prev = yp->next;
            yp = yp->next;
715
            yp_destroy_ypdata(to_go);
716 717 718 719 720
            continue;
        }
        prev = &yp->next;
        yp = yp->next;
    }
721
}
722 723 724


static void *yp_update_thread(void *arg)
725
{
726
    ICECAST_LOG_INFO("YP update thread started");
727 728 729 730 731 732 733 734 735 736

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

        thread_sleep (200000);

        /* do the YP communication */
        thread_rwlock_rlock (&yp_lock);
737
        server = (struct yp_server *)active_yps;
738 739
        while (server)
        {
740
            /* ICECAST_LOG_DEBUG("trying %s", server->url); */
741 742
            yp_process_server (server);
            server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
743
        }
744 745 746 747 748 749 750
        thread_rwlock_unlock (&yp_lock);

        /* update the local YP structure */
        if (yp_update)
        {
            thread_rwlock_wlock (&yp_lock);
            check_servers ();
751
            server = (struct yp_server *)active_yps;
752 753
            while (server)
            {
754
                /* ICECAST_LOG_DEBUG("Checking yps %s", server->url); */
755 756 757
                add_pending_yp (server);
                delete_marked_yp (server);
                server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
758
            }
759 760
            yp_update = 0;
            thread_rwlock_unlock (&yp_lock);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
761 762
        }
    }
763 764 765 766 767
    thread_rwlock_destroy (&yp_lock);
    thread_mutex_destroy (&yp_pending_lock);
    /* free server and ypdata left */
    while (active_yps)
    {
768
        struct yp_server *server = (struct yp_server *)active_yps;
769 770 771
        active_yps = server->next;
        destroy_yp_server (server);
    }
772

773
    return NULL;
774
}
775

776 777 778


static void yp_destroy_ypdata(ypdata_t *ypdata)
779
{
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
780
    if (ypdata) {
781
        if (ypdata->mount) {
782
            free(ypdata->mount);
783 784
        }
        if (ypdata->url) {
785
            free(ypdata->url);
786
        }
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
        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);
        }
814 815 816
        if (ypdata->audio_info) {
            free(ypdata->audio_info);
        }
817 818 819
        free(ypdata->subtype);
        free(ypdata->error_msg);
        free(ypdata);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
820
    }
821
}
822

823
static void add_yp_info(ypdata_t *yp, void *info, int type)
824 825
{
    char *escaped;
826 827

    if (!info)
828
        return;
829

830 831 832 833
    escaped = util_url_escape(info);
    if (escaped == NULL)
        return;

834 835
    switch (type)
    {
836
        case YP_SERVER_NAME:
837 838
            free (yp->server_name);
            yp->server_name = escaped;
839
            break;
840
        case YP_SERVER_DESC:
841 842
            free (yp->server_desc);
            yp->server_desc = escaped;
843
            break;
844
        case YP_SERVER_GENRE:
845 846
            free (yp->server_genre);
            yp->server_genre = escaped;
847
            break;
848
        case YP_SERVER_URL:
849 850
            free (yp->url);
            yp->url = escaped;
851
            break;
852
        case YP_BITRATE:
853 854
            free (yp->bitrate);
            yp->bitrate = escaped;
855
            break;
856
        case YP_AUDIO_INFO:
857 858
            free (yp->audio_info);
            yp->audio_info = escaped;
859
            break;
860
        case YP_SERVER_TYPE:
861 862
            free (yp->server_type);
            yp->server_type = escaped;
863
            break;
864
        case YP_CURRENT_SONG:
865 866
            free (yp->current_song);
            yp->current_song = escaped;
867
            break;
868
        case YP_CLUSTER_PASSWORD:
869 870
            free (yp->cluster_password);
            yp->cluster_password = escaped;
871
            break;
872
        case YP_SUBTYPE:
873 874
            free (yp->subtype);
            yp->subtype = escaped;
875
            break;
876 877
        default:
            free (escaped);
878 879 880 881 882
    }
}


/* Add YP entries to active servers */
883
void yp_add (const char *mount)
884 885 886 887 888 889 890 891
{
    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);
892
    server = (struct yp_server *)active_yps;
893 894 895
    while (server)
    {
        ypdata_t *yp;
896 897 898 899

        /* on-demand relays may already have a YP entry */
        yp = find_yp_mount (server->mounts, mount);
        if (yp == NULL)
900
        {
901 902 903 904
            /* add new ypdata to each servers pending yp */
            yp = create_yp_entry (mount);
            if (yp)
            {
905
                ICECAST_LOG_DEBUG("Adding %s to %s", mount, server->url);
906 907 908
                yp->server = server;
                yp->touch_interval = server->touch_interval;
                yp->next = server->pending_mounts;
909
                yp->next_update = time(NULL) + 60;
910 911 912
                server->pending_mounts = yp;
                yp_update = 1;
            }
913
        }
914
        else
915
            ICECAST_LOG_DEBUG("YP entry %s already exists", mount);
916 917 918 919 920 921 922 923 924 925 926
        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)
{
927
    struct yp_server *server = (struct yp_server *)active_yps;
928 929 930 931

    thread_rwlock_rlock (&yp_lock);
    while (server)
    {
932 933 934
        ypdata_t *list = server->mounts;

        while (1)
935
        {
936 937 938 939 940 941 942 943
            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 */
            }
944
            ICECAST_LOG_DEBUG("release %s on YP %s", mount, server->url);
945
            yp->release = 1;
946
            yp->next_update = 0;
947
        }
948
        server = server->next;
949
    }
950
    thread_rwlock_unlock (&yp_lock);
951
}
952 953 954 955 956 957


/* This is similar to yp_remove, but we force a touch
 * attempt */
void yp_touch (const char *mount)
{
958
    struct yp_server *server = (struct yp_server *)active_yps;
959
    ypdata_t *search_list = NULL;
960 961

    thread_rwlock_rlock (&yp_lock);
962 963 964
    if (server)
        search_list = server->mounts;

965 966
    while (server)
    {
967
        ypdata_t *yp = find_yp_mount (search_list, mount);
968 969
        if (yp)
        {
970
            /* we may of found old entries not purged yet, so skip them */
971
            if (yp->release != 0 || yp->remove != 0)
972 973 974 975
            {
                search_list = yp->next;
                continue;
            }
976 977 978
            /* 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;
979 980
        }
        server = server->next;
981 982
        if (server)
            search_list = server->mounts;
983 984 985 986 987
    }
    thread_rwlock_unlock (&yp_lock);
}


988
void yp_shutdown (void)
989 990 991
{
    yp_running = 0;
    yp_update = 1;
992 993
    if (yp_thread)
        thread_join (yp_thread);
994 995
    free ((char*)server_version);
    server_version = NULL;
996
    ICECAST_LOG_INFO("YP thread down");
997 998
}