yp.c 26.7 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
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
11
 * Copyright 2013-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12 13
 */

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

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

23
#include "common/thread/thread.h"
24

25
#include "yp.h"
26
#include "global.h"
27
#include "curl.h"
28 29
#include "logging.h"
#include "source.h"
30
#include "cfgfile.h"
31
#include "stats.h"
32 33 34 35

#ifdef WIN32
#define snprintf _snprintf
#endif
36

37
#define CATMODULE "yp"
38

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

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

    struct ypdata_tag *next;
} ypdata_t;


static rwlock_t yp_lock;
static mutex_t yp_pending_lock;

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

static void *yp_update_thread(void *arg);
97 98 99 100
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);
101 102
static void add_pending_yp (struct yp_server *server);
static void delete_marked_yp(struct yp_server *server);
103 104 105 106
static void yp_destroy_ypdata(ypdata_t *ypdata);


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

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

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

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


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

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


static void destroy_yp_server (struct yp_server *server)
{
175 176
    ypdata_t *yp;

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

    /* 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);

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



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

217 218 219

void yp_recheck_config (ice_config_t *config)
{
220
    size_t i;
221 222
    struct yp_server *server;

223
    ICECAST_LOG_DEBUG("Updating YP configuration");
224 225
    thread_rwlock_rlock (&yp_lock);

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

    yp_running = 1;
730 731 732
    running = 1;

    while (running) {
733 734 735 736 737 738
        struct yp_server *server;

        thread_sleep (200000);

        /* do the YP communication */
        thread_rwlock_rlock (&yp_lock);
739
        server = (struct yp_server *)active_yps;
740 741
        while (server)
        {
742
            /* ICECAST_LOG_DEBUG("trying %s", server->url); */
743 744
            yp_process_server (server);
            server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
745
        }
746 747 748
        /* update the local YP structure */
        if (yp_update)
        {
749
            thread_rwlock_unlock(&yp_lock);
750 751
            thread_rwlock_wlock (&yp_lock);
            check_servers ();
752
            server = (struct yp_server *)active_yps;
753 754
            while (server)
            {
755
                /* ICECAST_LOG_DEBUG("Checking yps %s", server->url); */
756 757 758
                add_pending_yp (server);
                delete_marked_yp (server);
                server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
759
            }
760
            yp_update = 0;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
761
        }
762 763
        running = yp_running;
        thread_rwlock_unlock(&yp_lock);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
764
    }
765 766 767 768 769
    thread_rwlock_destroy (&yp_lock);
    thread_mutex_destroy (&yp_pending_lock);
    /* free server and ypdata left */
    while (active_yps)
    {
770
        struct yp_server *server = (struct yp_server *)active_yps;
771 772 773
        active_yps = server->next;
        destroy_yp_server (server);
    }
774

775
    return NULL;
776
}
777

778 779 780


static void yp_destroy_ypdata(ypdata_t *ypdata)
781
{
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
782
    if (ypdata) {
783
        if (ypdata->mount) {
784
            free(ypdata->mount);
785 786
        }
        if (ypdata->url) {
787
            free(ypdata->url);
788
        }
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
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 814 815
        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);
        }
816 817 818
        if (ypdata->audio_info) {
            free(ypdata->audio_info);
        }
819 820 821
        free(ypdata->subtype);
        free(ypdata->error_msg);
        free(ypdata);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
822
    }
823
}
824

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

    if (!info)
830
        return;
831

832 833 834 835
    escaped = util_url_escape(info);
    if (escaped == NULL)
        return;

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


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

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

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

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


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

    thread_rwlock_rlock (&yp_lock);
964 965 966
    if (server)
        search_list = server->mounts;

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


990
void yp_shutdown (void)
991
{
992
    thread_rwlock_wlock(&yp_lock);
993 994
    yp_running = 0;
    yp_update = 1;
995 996
    thread_rwlock_unlock(&yp_lock);

997 998
    if (yp_thread)
        thread_join (yp_thread);
999 1000
    free ((char*)server_version);
    server_version = NULL;
1001
    ICECAST_LOG_INFO("YP thread down");
1002 1003
}