yp.c 26.8 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

Marvin Scholz's avatar
Marvin Scholz committed
23
#include "common/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

    struct yp_server *server;
77
78
79
80
    time_t next_update;
    unsigned touch_interval;
    char *error_msg;
    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
99
100
101
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);
102
103
104
105
static void yp_destroy_ypdata(ypdata_t *ypdata);


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

111
    /* ICECAST_LOG_DEBUG("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
    {
Philipp Schafft's avatar
Philipp Schafft committed
117
        size_t len = bytes - 11;
118
119
120
        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
        {
Philipp Schafft's avatar
Philipp Schafft committed
128
            size_t len = bytes - 5;
129
130
131
            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
        if (secs < 30)
            secs = 30;
142
        ICECAST_LOG_DEBUG("server touch interval is %u", secs);
143
        yp->touch_interval = secs;
144
    }
Philipp Schafft's avatar
Philipp Schafft committed
145
    return (size_t)bytes;
146
147
148
149
}


/* capture returned data, but don't do anything with it, shouldn't be any */
Philipp Schafft's avatar
Philipp Schafft committed
150
static size_t handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
151
{
Philipp Schafft's avatar
Philipp Schafft committed
152
    return (size_t)(size*nmemb);
153
154
155
156
157
158
159
160
}


/* 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
    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;
183
    ICECAST_LOG_DEBUG("Removing YP server entry for %s", server->url);
184
185
    if (server->curl)
        curl_easy_cleanup (server->curl);
186
187
    if (server->mounts) ICECAST_LOG_WARN("active ypdata not freed up");
    if (server->pending_mounts) ICECAST_LOG_WARN("pending ypdata not freed up");
188
    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
    int i;
    struct yp_server *server;

214
    ICECAST_LOG_DEBUG("Updating YP configuration");
215
216
    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
            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
275
                    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
    int curlcode;
    struct yp_server *server = yp->server;

301
    /* ICECAST_LOG_DEBUG("send YP (%s):%s", cmd, post); */
302
303
304
305
306
307
308
    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
        ICECAST_LOG_ERROR("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
        if (yp->process == do_yp_add)
        {
319
            ICECAST_LOG_ERROR("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
            ICECAST_LOG_INFO("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
335
        }
336
        yp->process = do_yp_add;
337
        free(yp->sid);
338
        yp->sid = NULL;
339
        return -1;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
340
    }
341
    ICECAST_LOG_DEBUG("YP %s at %s succeeded", cmd, server->url);
342
343
344
345
346
    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
        if (ret >= (signed)len)
            return ret+1;

357
        ICECAST_LOG_INFO("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
    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
438
         const char *separator = " - ";
439
440
441
442
443
444
445
446
447
448
         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
    /* ICECAST_LOG_DEBUG("processing yp server %s", server->url); */
534
535
536
537
    yp = server->mounts;
    while (yp)
    {
        now = time (NULL);
538
539
540
541
542
        /* 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)
        {
543
            ICECAST_LOG_DEBUG("skiping %s on %s", yp->mount, server->url);
544
545
546
547
548
            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, MOUNT_TYPE_NORMAL);
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

    while (server)
    {
        if (server->remove)
        {
            struct yp_server *to_go = server;
625
            ICECAST_LOG_DEBUG("YP server \"%s\"removed", server->url);
626
627
            *server_p = server->next;
            server = server->next;
628
            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
        pending_yps = server->next;

642
        ICECAST_LOG_DEBUG("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
                ICECAST_LOG_DEBUG("Adding existing mount %s", source->mount);
657
658
659
660
                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
    yp->next = current;
688
    ICECAST_LOG_DEBUG("%u YP entries added to %s", count, server->url);
689
690
}

691

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

696
697
698
699
700
    while (yp)
    {
        if (yp->remove)
        {
            ypdata_t *to_go = yp;
701
            ICECAST_LOG_DEBUG("removed %s from YP server %s", yp->mount, server->url);
702
703
            *prev = yp->next;
            yp = yp->next;
704
            yp_destroy_ypdata(to_go);
705
706
707
708
709
            continue;
        }
        prev = &yp->next;
        yp = yp->next;
    }
710
}
711
712
713


static void *yp_update_thread(void *arg)
714
{
715
    ICECAST_LOG_INFO("YP update thread started");
716
717
718
719
720
721
722
723
724
725

    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
        while (server)
        {
729
            /* ICECAST_LOG_DEBUG("trying %s", server->url); */
730
731
            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
            while (server)
            {
743
                /* ICECAST_LOG_DEBUG("Checking yps %s", server->url); */
744
745
746
                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
        if (ypdata->mount) {
771
            free(ypdata->mount);
772
773
        }
        if (ypdata->url) {
774
            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
807
808
        free(ypdata->subtype);
        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
            /* add new ypdata to each servers pending yp */
            yp = create_yp_entry (mount);
            if (yp)
            {
894
                ICECAST_LOG_DEBUG("Adding %s to %s", mount, server->url);
895
896
897
                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
        else
904
            ICECAST_LOG_DEBUG("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
            ICECAST_LOG_DEBUG("release %s on YP %s", mount, server->url);
934
            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
    ICECAST_LOG_INFO("YP thread down");
987
988
}