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

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

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

23
#include "thread/thread.h"
24
25
26
27
28
29
30

#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h"
#include "format.h"
#include "source.h"
31
#include "cfgfile.h"
32
#include "stats.h"
33
34
35
36

#ifdef WIN32
#define snprintf _snprintf
#endif
37

Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
38
#define CATMODULE "yp" 
39

40
41
42
struct yp_server
{
    char        *url;
43
    char        *server_id;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    unsigned    url_timeout;
    unsigned    touch_interval;
    int         remove;

    CURL *curl;
    struct ypdata_tag *mounts, *pending_mounts;
    struct yp_server *next;
    char curl_error[CURL_ERROR_SIZE];
};



typedef struct ypdata_tag
{
    int remove;
59
    int release;
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    int cmd_ok;

    char *sid;
    char *mount;
    char *url;
    char *listen_url;
    char *server_name;
    char *server_desc;
    char *server_genre;
    char *cluster_password;
    char *bitrate;
    char *audio_info;
    char *server_type;
    char *current_song;
74
    char *subtype;
75
76
77
78
79

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

    struct ypdata_tag *next;
} ypdata_t;


static rwlock_t yp_lock;
static mutex_t yp_pending_lock;

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

static void *yp_update_thread(void *arg);
98
static void add_yp_info (ypdata_t *yp, void *info, int type);
99
100
101
static int do_yp_remove (ypdata_t *yp, char *s, unsigned len);
static int do_yp_add (ypdata_t *yp, char *s, unsigned len);
static int do_yp_touch (ypdata_t *yp, char *s, unsigned len);
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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);
        }
134
135
136
137
138
139
140
141
142
    }
    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;
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    }
    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;

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



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

207
208
209

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

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

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

273

274
void yp_initialize(void)
275
{
276
    ice_config_t *config = config_get_config();
277
278
    thread_rwlock_create (&yp_lock);
    thread_mutex_create (&yp_pending_lock);
279
280
    yp_recheck_config (config);
    config_release_config ();
281
282
    yp_thread = thread_create("YP Touch Thread", yp_update_thread,
                            (void *)NULL, THREAD_ATTACHED);
283
}
284
285
286



287
288
289
/* handler for curl, checks if successful handling occurred
 * return 0 for ok, -1 for this entry failed, -2 for server fail
 */
290
static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
291
{
292
293
294
295
296
297
298
299
300
301
302
    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;
303
        yp->next_update += 900;
304
        ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
305
        return -2;
306
    }
307
308
    if (yp->cmd_ok == 0)
    {
309
310
        if (yp->error_msg == NULL)
            yp->error_msg = strdup ("no response from server");
311
        yp->process = do_yp_add;
312
        yp->next_update += 300;
313
314
        ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
        return -1;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
315
    }
316
317
318
319
320
321
    DEBUG2 ("YP %s at %s succeeded", cmd, server->url);
    return 0;
}


/* routines for building and issues requests to the YP server */
322
static int do_yp_remove (ypdata_t *yp, char *s, unsigned len)
323
{
324
325
    int ret = 0;

326
327
    if (yp->sid)
    {
328
        ret = snprintf (s, len, "action=remove&sid=%s", yp->sid);
329
330
331
332
        if (ret >= (signed)len)
            return ret+1;

        INFO1 ("clearing up YP entry for %s", yp->mount);
333
        ret = send_to_yp ("remove", yp, s);
334
335
        free (yp->sid);
        yp->sid = NULL;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
336
    }
337
338
339
    yp_update = 1;
    yp->remove = 1;
    yp->process = do_yp_add;
340

341
    return ret;
342
343
}

344

345
static int do_yp_add (ypdata_t *yp, char *s, unsigned len)
346
{
347
348
349
    int ret;
    char *value;

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    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);

374
    value = stats_get_value (yp->mount, "subtype");
375
376
377
378
379
380
    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);
381
382
383

    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",
384
385
                    yp->server_name, yp->server_genre, yp->cluster_password,
                    yp->server_desc, yp->url, yp->listen_url,
386
                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
387
388
    if (ret >= (signed)len)
        return ret+1;
389
390
    ret = send_to_yp ("add", yp, s);
    if (ret == 0)
391
392
393
394
395
396
    {
        yp->process = do_yp_touch;
        /* force first touch in 5 secs */
        yp->next_update = time(NULL) + 5;
    }

397
    return ret;
398
399
400
}


401
static int do_yp_touch (ypdata_t *yp, char *s, unsigned len)
402
{
Karl Heyes's avatar
Karl Heyes committed
403
    unsigned listeners = 0, max_listeners = 1;
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
    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);
423
424
             add_yp_info(yp, song, YP_CURRENT_SONG);
             stats_event (yp->mount, "yp_currently_playing", song);
425
426
427
428
429
430
431
432
433
434
435
436
             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
437
    val = stats_get_value (yp->mount, "max_listeners");
438
    if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
Karl Heyes's avatar
Karl Heyes committed
439
440
441
        max_listeners = client_limit;
    else
        max_listeners = atoi (val);
442
    free (val);
Karl Heyes's avatar
Karl Heyes committed
443

444
445
446
    val = stats_get_value (yp->mount, "subtype");
    if (val)
    {
447
        add_yp_info (yp, val, YP_SUBTYPE);
448
449
        free (val);
    }
450
451

    ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
Karl Heyes's avatar
Karl Heyes committed
452
            "&listeners=%u&max_listeners=%u&stype=%s\r\n",
453
            yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
454
455
456
457

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

458
    return send_to_yp ("touch", yp, s);
459
}
460

461
462


463
static int process_ypdata (struct yp_server *server, ypdata_t *yp)
464
{
465
466
    unsigned len = 512;
    char *s = NULL, *tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
467

468
    if (now < yp->next_update)
469
        return 0;
470
    yp->next_update = now + yp->touch_interval;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
471

472
473
474
    /* loop just in case the memory area isn't big enough */
    while (1)
    {
475
        int ret;
476
        if ((tmp = realloc (s, len)) == NULL)
477
            return 0;
478
        s = tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
479

480
481
482
483
484
485
        if (yp->release)
        {
            yp->process = do_yp_remove;
            yp->next_update = 0;
        }

486
487
488
489
        ret = yp->process (yp, s, len);
        if (ret == 0)
        {
           free (s);
490
           return ret;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
491
        }
492
        len = ret;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
493
    }
494
    return 0;
495
}
496
497
498


static void yp_process_server (struct yp_server *server)
499
{
500
    ypdata_t *yp;
501
    int state = 0;
502
503
504
505
506
507

    /* DEBUG1("processing yp server %s", server->url); */
    yp = server->mounts;
    while (yp)
    {
        now = time (NULL);
508
509
510
511
512
513
514
515
516
517
518
        /* if one of the streams shows that the server cannot be contacted then mark the
         * other entries for an update later. Assume YP server is dead and skip it for now
         */
        if (state == -2)
        {
            DEBUG2 ("skiping %s on %s", yp->mount, server->url);
            yp->process = do_yp_add;
            yp->next_update += 900;
        }
        else
            state = process_ypdata (server, yp);
519
520
521
522
523
524
        yp = yp->next;
    }
}



525
static ypdata_t *create_yp_entry (const char *mount)
526
527
{
    ypdata_t *yp;
528
    char *s;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
529

530
531
532
533
534
535
    yp = calloc (1, sizeof (ypdata_t));
    do
    {
        unsigned len = 512;
        int ret;
        char *url;
536
        mount_proxy *mountproxy = NULL;
537
538
539
540
        ice_config_t *config;

        if (yp == NULL)
            break;
541
        yp->mount = strdup (mount);
542
543
544
545
546
547
548
549
550
        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 ("");
551
        yp->subtype = strdup ("");
552
553
554
555
556
557
        yp->process = do_yp_add;

        url = malloc (len);
        if (url == NULL)
            break;
        config = config_get_config();
558
        ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, mount);
559
        if (ret >= (signed)len)
560
        {
561
562
            s = realloc (url, ++ret);
            if (s) url = s;
563
            snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, mount);
564
        }
565

566
        mountproxy = config_find_mount (config, mount);
567
        if (mountproxy && mountproxy->cluster_password)
568
            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
569
        config_release_config();
570

571
572
573
574
575
576
577
578
579
580
581
582
583
584
        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 */
585
static void check_servers (void)
586
{
587
588
    struct yp_server *server = (struct yp_server *)active_yps,
                     **server_p = (struct yp_server **)&active_yps;
589
590
591
592
593
594
595
596
597
598

    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);
599
            continue;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
600
        }
601
602
603
604
605
606
607
608
        server_p = &server->next;
        server = server->next;
    }
    /* add new server entries */
    while (pending_yps)
    {
        avl_node *node;

609
        server = (struct yp_server *)pending_yps;
610
611
612
        pending_yps = server->next;

        DEBUG1("Add pending yps %s", server->url);
613
        server->next = (struct yp_server *)active_yps;
614
615
616
617
618
619
620
621
622
623
        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;
624
            if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL)
625
626
627
628
629
630
            {
                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
631
            }
632
            node = avl_get_next (node);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
633
        }
634
        avl_tree_unlock (global.source_tree);
635
    }
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
}


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
656
    }
657
658
659
660
    yp->next = current;
    DEBUG2 ("%u YP entries added to %s", count, server->url);
}

661

662
663
664
static void delete_marked_yp (struct yp_server *server)
{
    ypdata_t *yp = server->mounts, **prev = &server->mounts;
665

666
667
668
669
670
671
672
673
674
675
676
677
678
679
    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;
    }
680
}
681
682
683


static void *yp_update_thread(void *arg)
684
{
685
686
687
688
689
690
691
692
693
694
695
    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);
696
        server = (struct yp_server *)active_yps;
697
698
699
700
701
        while (server)
        {
            /* DEBUG1 ("trying %s", server->url); */
            yp_process_server (server);
            server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
702
        }
703
704
705
706
707
708
709
        thread_rwlock_unlock (&yp_lock);

        /* update the local YP structure */
        if (yp_update)
        {
            thread_rwlock_wlock (&yp_lock);
            check_servers ();
710
            server = (struct yp_server *)active_yps;
711
712
713
714
715
716
            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
717
            }
718
719
            yp_update = 0;
            thread_rwlock_unlock (&yp_lock);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
720
721
        }
    }
722
723
724
725
726
    thread_rwlock_destroy (&yp_lock);
    thread_mutex_destroy (&yp_pending_lock);
    /* free server and ypdata left */
    while (active_yps)
    {
727
        struct yp_server *server = (struct yp_server *)active_yps;
728
729
730
        active_yps = server->next;
        destroy_yp_server (server);
    }
731

732
    return NULL;
733
}
734

735
736
737


static void yp_destroy_ypdata(ypdata_t *ypdata)
738
{
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
739
    if (ypdata) {
740
741
742
743
744
        if (ypdata->mount) {
            free (ypdata->mount);
        }
        if (ypdata->url) {
            free (ypdata->url);
745
        }
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
        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);
        }
773
774
775
        if (ypdata->audio_info) {
            free(ypdata->audio_info);
        }
776
        free (ypdata->subtype);
777
778
        free (ypdata->error_msg);
        free (ypdata);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
779
    }
780
}
781

782
static void add_yp_info (ypdata_t *yp, void *info, int type)
783
784
{
    char *escaped;
785
786

    if (!info)
787
        return;
788

789
790
791
792
    escaped = util_url_escape(info);
    if (escaped == NULL)
        return;

793
794
    switch (type)
    {
795
        case YP_SERVER_NAME:
796
797
            free (yp->server_name);
            yp->server_name = escaped;
798
            break;
799
        case YP_SERVER_DESC:
800
801
            free (yp->server_desc);
            yp->server_desc = escaped;
802
            break;
803
        case YP_SERVER_GENRE:
804
805
            free (yp->server_genre);
            yp->server_genre = escaped;
806
            break;
807
        case YP_SERVER_URL:
808
809
            free (yp->url);
            yp->url = escaped;
810
            break;
811
        case YP_BITRATE:
812
813
            free (yp->bitrate);
            yp->bitrate = escaped;
814
            break;
815
        case YP_AUDIO_INFO:
816
817
            free (yp->audio_info);
            yp->audio_info = escaped;
818
            break;
819
        case YP_SERVER_TYPE:
820
821
            free (yp->server_type);
            yp->server_type = escaped;
822
            break;
823
        case YP_CURRENT_SONG:
824
825
            free (yp->current_song);
            yp->current_song = escaped;
826
            break;
827
        case YP_CLUSTER_PASSWORD:
828
829
            free (yp->cluster_password);
            yp->cluster_password = escaped;
830
            break;
831
        case YP_SUBTYPE:
832
833
            free (yp->subtype);
            yp->subtype = escaped;
834
            break;
835
836
        default:
            free (escaped);
837
838
839
840
841
    }
}


/* Add YP entries to active servers */
842
void yp_add (const char *mount)
843
844
845
846
847
848
849
850
{
    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);
851
    server = (struct yp_server *)active_yps;
852
853
854
    while (server)
    {
        ypdata_t *yp;
855
856
857
858

        /* on-demand relays may already have a YP entry */
        yp = find_yp_mount (server->mounts, mount);
        if (yp == NULL)
859
        {
860
861
862
863
864
865
866
867
868
869
870
871
            /* 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;
            }
872
        }
873
874
        else
            DEBUG1 ("YP entry %s already exists", mount);
875
876
877
878
879
880
881
882
883
884
885
        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)
{
886
    struct yp_server *server = (struct yp_server *)active_yps;
887
888
889
890

    thread_rwlock_rlock (&yp_lock);
    while (server)
    {
891
        ypdata_t *yp = find_yp_mount (server->mounts, mount);
892
893
        if (yp)
        {
894
895
            DEBUG2 ("release %s on YP %s", mount, server->url);
            yp->release = 1;
896
            yp->next_update = 0;
897
        }
898
        server = server->next;
899
    }
900
    thread_rwlock_unlock (&yp_lock);
901
}
902
903
904
905
906
907


/* This is similar to yp_remove, but we force a touch
 * attempt */
void yp_touch (const char *mount)
{
908
    struct yp_server *server = (struct yp_server *)active_yps;
909
    time_t trigger;
910
    ypdata_t *search_list = NULL;
911
912
913
914

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

918
919
    while (server)
    {
920
        ypdata_t *yp = find_yp_mount (search_list, mount);
921
922
        if (yp)
        {
923
            /* we may of found old entries not purged yet, so skip them */
924
            if (yp->release != 0 || yp->remove != 0)
925
926
927
928
            {
                search_list = yp->next;
                continue;
            }
929
930
931
932
933
            /* only force if touch */
            if (yp->process == do_yp_touch)
                yp->next_update = trigger;
        }
        server = server->next;
934
935
        if (server)
            search_list = server->mounts;
936
937
938
939
940
    }
    thread_rwlock_unlock (&yp_lock);
}


941
void yp_shutdown (void)
942
943
944
{
    yp_running = 0;
    yp_update = 1;
945
946
    if (yp_thread)
        thread_join (yp_thread);
947
    curl_global_cleanup();
948
949
    free ((char*)server_version);
    server_version = NULL;
950
    INFO0 ("YP thread down");
951
952
}