yp.c 24.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
 */

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

18
19
#include <stdio.h>
#include <string.h>
20
#include <stdlib.h>
21
#include <curl/curl.h>
22
23
24
25
26
27
28
29
30

#include <thread/thread.h>

#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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
struct yp_server
{
    char        *url;
    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
76
77
78
79
80
81
82
83
84
85
86
87

    struct yp_server *server;
    time_t      next_update;
    unsigned    touch_interval;
    char        *error_msg;
    unsigned    (*process)(struct ypdata_tag *yp, char *s, unsigned len);

    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
95

static void *yp_update_thread(void *arg);
96
static void add_yp_info (ypdata_t *yp, void *info, int type);
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
static unsigned do_yp_remove (ypdata_t *yp, char *s, unsigned len);
static unsigned do_yp_add (ypdata_t *yp, char *s, unsigned len);
static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len);
static void yp_destroy_ypdata(ypdata_t *ypdata);


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

    /* DEBUG2 ("header from YP is \"%.*s\"", bytes, ptr); */
    if (strncmp (ptr, "YPResponse: 1", 13) == 0)
        yp->cmd_ok = 1;

    if (strncmp (ptr, "YPMessage: ", 11) == 0)
    {
        unsigned len = bytes - 11;
        free (yp->error_msg);
        yp->error_msg = calloc (1, len);
        if (yp->error_msg)
            sscanf (ptr, "YPMessage: %[^\r\n]", yp->error_msg);
    }

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


/* capture returned data, but don't do anything with it, shouldn't be any */
static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
{
    return (int)(size*nmemb);
}


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

158
    server = (struct yp_server *)active_yps;
159
160
161
162
163
164
    while (server)
    {
        if (strcmp (server->url, url) == 0)
            return server;
        server = server->next;
    }
165
    server = (struct yp_server *)pending_yps;
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
    while (server)
    {
        if (strcmp (server->url, url) == 0)
            break;
        server = server->next;
    }
    return server;
}


static void destroy_yp_server (struct yp_server *server)
{
    if (server == NULL)
        return;
    DEBUG1 ("Removing YP server entry for %s", server->url);
    if (server->curl)
        curl_easy_cleanup (server->curl);
    if (server->mounts) WARN0 ("active ypdata not freed up");
    if (server->pending_mounts) WARN0 ("pending ypdata not freed up");
    free (server->url);
    free (server);
}



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

204
205
206

void yp_recheck_config (ice_config_t *config)
{
207
208
209
210
211
212
    int i;
    struct yp_server *server;

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

213
    server = (struct yp_server *)active_yps;
214
215
216
217
218
    while (server)
    {
        server->remove = 1;
        server = server->next;
    }
Karl Heyes's avatar
Karl Heyes committed
219
    client_limit = config->client_limit;
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
    /* 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;
            }
            server->url = strdup (config->yp_url[i]);
            server->url_timeout = config->yp_url_timeout[i];
236
            server->touch_interval = config->yp_touch_interval[i];
237
238
239
240
241
242
243
244
            server->curl = curl_easy_init();
            if (server->curl == NULL)
            {
                destroy_yp_server (server);
                break;
            }
            if (server->touch_interval < 30)
                server->touch_interval = 30;
245
            curl_easy_setopt (server->curl, CURLOPT_USERAGENT, ICECAST_VERSION_STRING);
246
247
248
249
250
251
252
            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);
            curl_easy_setopt (server->curl, CURLOPT_ERRORBUFFER, &(server->curl_error[0]));
253
            server->next = (struct yp_server *)pending_yps;
254
255
256
257
258
259
260
261
262
263
264
            pending_yps = server;
            INFO3 ("Adding new YP server \"%s\" (timeout %ds, default interval %ds)",
                    server->url, server->url_timeout, server->touch_interval);
        }
        else
        {
            server->remove = 0;
        }
    }
    thread_rwlock_unlock (&yp_lock);
    yp_update = 1;
265
266
}

267

268
void yp_initialize(void)
269
{
270
    ice_config_t *config = config_get_config();
271
272
    thread_rwlock_create (&yp_lock);
    thread_mutex_create (&yp_pending_lock);
273
274
    yp_recheck_config (config);
    config_release_config ();
275
276
    yp_thread = thread_create("YP Touch Thread", yp_update_thread,
                            (void *)NULL, THREAD_ATTACHED);
277
}
278
279
280
281
282



/* handler for curl, checks if successful handling occurred */
static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
283
{
284
285
286
287
288
289
290
291
292
293
294
    int curlcode;
    struct yp_server *server = yp->server;

    /* DEBUG2 ("send YP (%s):%s", cmd, post); */
    yp->cmd_ok = 0;
    curl_easy_setopt (server->curl, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (server->curl, CURLOPT_WRITEHEADER, yp);
    curlcode = curl_easy_perform (server->curl);
    if (curlcode)
    {
        yp->process = do_yp_add;
295
        yp->next_update += 300;
296
297
        ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
        return -1;
298
    }
299
300
    if (yp->cmd_ok == 0)
    {
301
302
        if (yp->error_msg == NULL)
            yp->error_msg = strdup ("no response from server");
303
        yp->process = do_yp_add;
304
        yp->next_update += 300;
305
306
        ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
        return -1;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
307
    }
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    DEBUG2 ("YP %s at %s succeeded", cmd, server->url);
    return 0;
}


/* routines for building and issues requests to the YP server */
static unsigned do_yp_remove (ypdata_t *yp, char *s, unsigned len)
{
    if (yp->sid)
    {
        int ret = snprintf (s, len, "action=remove&sid=%s", yp->sid);
        if (ret >= (signed)len)
            return ret+1;

        INFO1 ("clearing up YP entry for %s", yp->mount);
        send_to_yp ("remove", yp, s);
        free (yp->sid);
        yp->sid = NULL;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
326
    }
327
328
329
    yp_update = 1;
    yp->remove = 1;
    yp->process = do_yp_add;
330

331
    return 0;
332
333
}

334
335

static unsigned do_yp_add (ypdata_t *yp, char *s, unsigned len)
336
{
337
338
339
    int ret;
    char *value;

340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    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");
    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);

364
    value = stats_get_value (yp->mount, "subtype");
365
366
367
368
369
370
    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);
371
372
373

    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",
374
375
                    yp->server_name, yp->server_genre, yp->cluster_password,
                    yp->server_desc, yp->url, yp->listen_url,
376
                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
    if (ret >= (signed)len)
        return ret+1;
    if (send_to_yp ("add", yp, s) == 0)
    {
        yp->process = do_yp_touch;
        /* force first touch in 5 secs */
        yp->next_update = time(NULL) + 5;
    }

    return 0;
}


static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len)
{
Karl Heyes's avatar
Karl Heyes committed
392
    unsigned listeners = 0, max_listeners = 1;
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
    char *val, *artist, *title;
    int ret;

    artist = (char *)stats_get_value (yp->mount, "artist");
    title = (char *)stats_get_value (yp->mount, "title");
    if (artist || title)
    {
         char *song;
         char *separator = " - ";
         if (artist == NULL)
         {
             artist = strdup("");
             separator = "";
         }
         if (title == NULL) title = strdup("");
         song = malloc (strlen (artist) + strlen (title) + strlen (separator) +1);
         if (song)
         {
             sprintf (song, "%s%s%s", artist, separator, title);
412
413
             add_yp_info(yp, song, YP_CURRENT_SONG);
             stats_event (yp->mount, "yp_currently_playing", song);
414
415
416
417
418
419
420
421
422
423
424
425
             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
426
427
    val = stats_get_value (yp->mount, "max_listeners");
    if (val == NULL || strcmp (val, "unlimited") == 0)
428
    {
Karl Heyes's avatar
Karl Heyes committed
429
430
        free (val);
        max_listeners = client_limit;
431
    }
Karl Heyes's avatar
Karl Heyes committed
432
433
434
    else
        max_listeners = atoi (val);

435
436
437
    val = stats_get_value (yp->mount, "subtype");
    if (val)
    {
438
        add_yp_info (yp, val, YP_SUBTYPE);
439
440
        free (val);
    }
441
442

    ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
Karl Heyes's avatar
Karl Heyes committed
443
            "&listeners=%u&max_listeners=%u&stype=%s\r\n",
444
            yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
445
446
447
448
449
450

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

    send_to_yp ("touch", yp, s);
    return 0;
451
}
452

453
454
455


static void process_ypdata (struct yp_server *server, ypdata_t *yp)
456
{
457
458
    unsigned len = 512;
    char *s = NULL, *tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
459

460
461
462
    if (now < yp->next_update)
        return;
    yp->next_update = now + yp->touch_interval;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
463

464
465
466
467
468
469
470
    /* loop just in case the memory area isn't big enough */
    while (1)
    {
        unsigned ret;
        if ((tmp = realloc (s, len)) == NULL)
            return;
        s = tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
471

472
473
474
475
476
477
        if (yp->release)
        {
            yp->process = do_yp_remove;
            yp->next_update = 0;
        }

478
479
480
481
482
        ret = yp->process (yp, s, len);
        if (ret == 0)
        {
           free (s);
           return;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
483
        }
484
        len = ret;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
485
    }
486
}
487
488
489


static void yp_process_server (struct yp_server *server)
490
{
491
492
493
494
495
496
497
498
499
500
501
502
503
504
    ypdata_t *yp;

    /* DEBUG1("processing yp server %s", server->url); */
    yp = server->mounts;
    while (yp)
    {
        now = time (NULL);
        process_ypdata (server, yp);
        yp = yp->next;
    }
}



505
static ypdata_t *create_yp_entry (const char *mount)
506
507
{
    ypdata_t *yp;
508
    char *s;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
509

510
511
512
513
514
515
    yp = calloc (1, sizeof (ypdata_t));
    do
    {
        unsigned len = 512;
        int ret;
        char *url;
516
        mount_proxy *mountproxy = NULL;
517
518
519
520
        ice_config_t *config;

        if (yp == NULL)
            break;
521
        yp->mount = strdup (mount);
522
523
524
525
526
527
528
529
530
        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 ("");
531
        yp->subtype = strdup ("");
532
533
534
535
536
537
        yp->process = do_yp_add;

        url = malloc (len);
        if (url == NULL)
            break;
        config = config_get_config();
538
        ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, mount);
539
        if (ret >= (signed)len)
540
        {
541
542
            s = realloc (url, ++ret);
            if (s) url = s;
543
            snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, mount);
544
        }
545

546
        mountproxy = config_find_mount (config, mount);
547
        if (mountproxy && mountproxy->cluster_password)
548
            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
549
        config_release_config();
550

551
552
553
554
555
556
557
558
559
560
561
562
563
564
        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 */
565
static void check_servers (void)
566
{
567
568
    struct yp_server *server = (struct yp_server *)active_yps,
                     **server_p = (struct yp_server **)&active_yps;
569
570
571
572
573
574
575
576
577
578

    while (server)
    {
        if (server->remove)
        {
            struct yp_server *to_go = server;
            DEBUG1 ("YP server \"%s\"removed", server->url);
            *server_p = server->next;
            server = server->next;
            destroy_yp_server (to_go);
579
            continue;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
580
        }
581
582
583
584
585
586
587
588
        server_p = &server->next;
        server = server->next;
    }
    /* add new server entries */
    while (pending_yps)
    {
        avl_node *node;

589
        server = (struct yp_server *)pending_yps;
590
591
592
        pending_yps = server->next;

        DEBUG1("Add pending yps %s", server->url);
593
        server->next = (struct yp_server *)active_yps;
594
595
596
597
598
599
600
601
602
603
        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;
604
            if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL)
605
606
607
608
609
610
            {
                DEBUG1 ("Adding existing mount %s", source->mount);
                yp->server = server;
                yp->touch_interval = server->touch_interval;
                yp->next = server->mounts;
                server->mounts = yp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
611
            }
612
            node = avl_get_next (node);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
613
        }
614
        avl_tree_unlock (global.source_tree);
615
    }
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
}


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
636
    }
637
638
639
640
    yp->next = current;
    DEBUG2 ("%u YP entries added to %s", count, server->url);
}

641

642
643
644
static void delete_marked_yp (struct yp_server *server)
{
    ypdata_t *yp = server->mounts, **prev = &server->mounts;
645

646
647
648
649
650
651
652
653
654
655
656
657
658
659
    while (yp)
    {
        if (yp->remove)
        {
            ypdata_t *to_go = yp;
            DEBUG2 ("removed %s from YP server %s", yp->mount, server->url);
            *prev = yp->next;
            yp = yp->next;
            yp_destroy_ypdata (to_go);
            continue;
        }
        prev = &yp->next;
        yp = yp->next;
    }
660
}
661
662
663


static void *yp_update_thread(void *arg)
664
{
665
666
667
668
669
670
671
672
673
674
675
    INFO0("YP update thread started");

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

        thread_sleep (200000);

        /* do the YP communication */
        thread_rwlock_rlock (&yp_lock);
676
        server = (struct yp_server *)active_yps;
677
678
679
680
681
        while (server)
        {
            /* DEBUG1 ("trying %s", server->url); */
            yp_process_server (server);
            server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
682
        }
683
684
685
686
687
688
689
        thread_rwlock_unlock (&yp_lock);

        /* update the local YP structure */
        if (yp_update)
        {
            thread_rwlock_wlock (&yp_lock);
            check_servers ();
690
            server = (struct yp_server *)active_yps;
691
692
693
694
695
696
            while (server)
            {
                /* DEBUG1 ("Checking yps %s", server->url); */
                add_pending_yp (server);
                delete_marked_yp (server);
                server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
697
            }
698
699
            yp_update = 0;
            thread_rwlock_unlock (&yp_lock);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
700
701
        }
    }
702
703
704
705
706
    thread_rwlock_destroy (&yp_lock);
    thread_mutex_destroy (&yp_pending_lock);
    /* free server and ypdata left */
    while (active_yps)
    {
707
        struct yp_server *server = (struct yp_server *)active_yps;
708
709
710
        active_yps = server->next;
        destroy_yp_server (server);
    }
711

712
    return NULL;
713
}
714

715
716
717


static void yp_destroy_ypdata(ypdata_t *ypdata)
718
{
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
719
    if (ypdata) {
720
721
722
723
724
        if (ypdata->mount) {
            free (ypdata->mount);
        }
        if (ypdata->url) {
            free (ypdata->url);
725
        }
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
        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);
        }
753
754
755
        if (ypdata->audio_info) {
            free(ypdata->audio_info);
        }
756
        free (ypdata->subtype);
757
758
        free (ypdata->error_msg);
        free (ypdata);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
759
    }
760
}
761

762
static void add_yp_info (ypdata_t *yp, void *info, int type)
763
764
{
    char *escaped;
765
766

    if (!info)
767
        return;
768

769
770
771
772
    escaped = util_url_escape(info);
    if (escaped == NULL)
        return;

773
774
    switch (type)
    {
775
        case YP_SERVER_NAME:
776
777
            free (yp->server_name);
            yp->server_name = escaped;
778
            break;
779
        case YP_SERVER_DESC:
780
781
            free (yp->server_desc);
            yp->server_desc = escaped;
782
            break;
783
        case YP_SERVER_GENRE:
784
785
            free (yp->server_genre);
            yp->server_genre = escaped;
786
            break;
787
        case YP_SERVER_URL:
788
789
            free (yp->url);
            yp->url = escaped;
790
            break;
791
        case YP_BITRATE:
792
793
            free (yp->bitrate);
            yp->bitrate = escaped;
794
            break;
795
        case YP_AUDIO_INFO:
796
797
            free (yp->audio_info);
            yp->audio_info = escaped;
798
            break;
799
        case YP_SERVER_TYPE:
800
801
            free (yp->server_type);
            yp->server_type = escaped;
802
            break;
803
        case YP_CURRENT_SONG:
804
805
            free (yp->current_song);
            yp->current_song = escaped;
806
            break;
807
        case YP_CLUSTER_PASSWORD:
808
809
            free (yp->cluster_password);
            yp->cluster_password = escaped;
810
            break;
811
        case YP_SUBTYPE:
812
813
            free (yp->subtype);
            yp->subtype = escaped;
814
            break;
815
816
        default:
            free (escaped);
817
818
819
820
821
    }
}


/* Add YP entries to active servers */
822
void yp_add (const char *mount)
823
824
825
826
827
828
829
830
{
    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);
831
    server = (struct yp_server *)active_yps;
832
833
834
    while (server)
    {
        ypdata_t *yp;
835
836
837
838

        /* on-demand relays may already have a YP entry */
        yp = find_yp_mount (server->mounts, mount);
        if (yp == NULL)
839
        {
840
841
842
843
844
845
846
847
848
849
850
851
            /* add new ypdata to each servers pending yp */
            yp = create_yp_entry (mount);
            if (yp)
            {
                DEBUG2 ("Adding %s to %s", mount, server->url);
                yp->server = server;
                yp->touch_interval = server->touch_interval;
                yp->next = server->pending_mounts;
                yp->next_update = time(NULL) + 5;
                server->pending_mounts = yp;
                yp_update = 1;
            }
852
        }
853
854
        else
            DEBUG1 ("YP entry %s already exists", mount);
855
856
857
858
859
860
861
862
863
864
865
        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)
{
866
    struct yp_server *server = (struct yp_server *)active_yps;
867
868
869
870

    thread_rwlock_rlock (&yp_lock);
    while (server)
    {
871
        ypdata_t *yp = find_yp_mount (server->mounts, mount);
872
873
        if (yp)
        {
874
875
            DEBUG2 ("release %s on YP %s", mount, server->url);
            yp->release = 1;
876
            yp->next_update = 0;
877
        }
878
        server = server->next;
879
    }
880
    thread_rwlock_unlock (&yp_lock);
881
}
882
883
884
885
886
887


/* This is similar to yp_remove, but we force a touch
 * attempt */
void yp_touch (const char *mount)
{
888
    struct yp_server *server = (struct yp_server *)active_yps;
889
    time_t trigger;
890
    ypdata_t *search_list = NULL;
891
892
893
894

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

898
899
    while (server)
    {
900
        ypdata_t *yp = find_yp_mount (search_list, mount);
901
902
        if (yp)
        {
903
            /* we may of found old entries not purged yet, so skip them */
904
            if (yp->release != 0 || yp->remove != 0)
905
906
907
908
            {
                search_list = yp->next;
                continue;
            }
909
910
911
912
913
            /* only force if touch */
            if (yp->process == do_yp_touch)
                yp->next_update = trigger;
        }
        server = server->next;
914
915
        if (server)
            search_list = server->mounts;
916
917
918
919
920
    }
    thread_rwlock_unlock (&yp_lock);
}


921
void yp_shutdown (void)
922
923
924
{
    yp_running = 0;
    yp_update = 1;
925
926
    if (yp_thread)
        thread_join (yp_thread);
927
    curl_global_cleanup();
928
    INFO0 ("YP thread down");
929
930
}