yp.c 24.4 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
80
81
82
83
84
85
86
87
88

    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
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
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 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);
        }
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



/* handler for curl, checks if successful handling occurred */
static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
289
{
290
291
292
293
294
295
296
297
298
299
300
    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;
301
        yp->next_update += 300;
302
303
        ERROR2 ("connection to %s failed with \"%s\"", server->url, server->curl_error);
        return -1;
304
    }
305
306
    if (yp->cmd_ok == 0)
    {
307
308
        if (yp->error_msg == NULL)
            yp->error_msg = strdup ("no response from server");
309
        yp->process = do_yp_add;
310
        yp->next_update += 300;
311
312
        ERROR3 ("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
        return -1;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
313
    }
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
    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
332
    }
333
334
335
    yp_update = 1;
    yp->remove = 1;
    yp->process = do_yp_add;
336

337
    return 0;
338
339
}

340
341

static unsigned do_yp_add (ypdata_t *yp, char *s, unsigned len)
342
{
343
344
345
    int ret;
    char *value;

346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    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);

370
    value = stats_get_value (yp->mount, "subtype");
371
372
373
374
375
376
    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);
377
378
379

    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",
380
381
                    yp->server_name, yp->server_genre, yp->cluster_password,
                    yp->server_desc, yp->url, yp->listen_url,
382
                    yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
    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
398
    unsigned listeners = 0, max_listeners = 1;
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
    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);
418
419
             add_yp_info(yp, song, YP_CURRENT_SONG);
             stats_event (yp->mount, "yp_currently_playing", song);
420
421
422
423
424
425
426
427
428
429
430
431
             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
432
    val = stats_get_value (yp->mount, "max_listeners");
433
    if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
Karl Heyes's avatar
Karl Heyes committed
434
435
436
        max_listeners = client_limit;
    else
        max_listeners = atoi (val);
437
    free (val);
Karl Heyes's avatar
Karl Heyes committed
438

439
440
441
    val = stats_get_value (yp->mount, "subtype");
    if (val)
    {
442
        add_yp_info (yp, val, YP_SUBTYPE);
443
444
        free (val);
    }
445
446

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

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

    send_to_yp ("touch", yp, s);
    return 0;
455
}
456

457
458
459


static void process_ypdata (struct yp_server *server, ypdata_t *yp)
460
{
461
462
    unsigned len = 512;
    char *s = NULL, *tmp;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
463

464
465
466
    if (now < yp->next_update)
        return;
    yp->next_update = now + yp->touch_interval;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
467

468
469
470
471
472
473
474
    /* 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
475

476
477
478
479
480
481
        if (yp->release)
        {
            yp->process = do_yp_remove;
            yp->next_update = 0;
        }

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


static void yp_process_server (struct yp_server *server)
494
{
495
496
497
498
499
500
501
502
503
504
505
506
507
508
    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;
    }
}



509
static ypdata_t *create_yp_entry (const char *mount)
510
511
{
    ypdata_t *yp;
512
    char *s;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
513

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

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

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

550
        mountproxy = config_find_mount (config, mount);
551
        if (mountproxy && mountproxy->cluster_password)
552
            add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
553
        config_release_config();
554

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

    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);
583
            continue;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
584
        }
585
586
587
588
589
590
591
592
        server_p = &server->next;
        server = server->next;
    }
    /* add new server entries */
    while (pending_yps)
    {
        avl_node *node;

593
        server = (struct yp_server *)pending_yps;
594
595
596
        pending_yps = server->next;

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


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
640
    }
641
642
643
644
    yp->next = current;
    DEBUG2 ("%u YP entries added to %s", count, server->url);
}

645

646
647
648
static void delete_marked_yp (struct yp_server *server)
{
    ypdata_t *yp = server->mounts, **prev = &server->mounts;
649

650
651
652
653
654
655
656
657
658
659
660
661
662
663
    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;
    }
664
}
665
666
667


static void *yp_update_thread(void *arg)
668
{
669
670
671
672
673
674
675
676
677
678
679
    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);
680
        server = (struct yp_server *)active_yps;
681
682
683
684
685
        while (server)
        {
            /* DEBUG1 ("trying %s", server->url); */
            yp_process_server (server);
            server = server->next;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
686
        }
687
688
689
690
691
692
693
        thread_rwlock_unlock (&yp_lock);

        /* update the local YP structure */
        if (yp_update)
        {
            thread_rwlock_wlock (&yp_lock);
            check_servers ();
694
            server = (struct yp_server *)active_yps;
695
696
697
698
699
700
            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
701
            }
702
703
            yp_update = 0;
            thread_rwlock_unlock (&yp_lock);
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
704
705
        }
    }
706
707
708
709
710
    thread_rwlock_destroy (&yp_lock);
    thread_mutex_destroy (&yp_pending_lock);
    /* free server and ypdata left */
    while (active_yps)
    {
711
        struct yp_server *server = (struct yp_server *)active_yps;
712
713
714
        active_yps = server->next;
        destroy_yp_server (server);
    }
715

716
    return NULL;
717
}
718

719
720
721


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

766
static void add_yp_info (ypdata_t *yp, void *info, int type)
767
768
{
    char *escaped;
769
770

    if (!info)
771
        return;
772

773
774
775
776
    escaped = util_url_escape(info);
    if (escaped == NULL)
        return;

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


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

        /* on-demand relays may already have a YP entry */
        yp = find_yp_mount (server->mounts, mount);
        if (yp == NULL)
843
        {
844
845
846
847
848
849
850
851
852
853
854
855
            /* 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;
            }
856
        }
857
858
        else
            DEBUG1 ("YP entry %s already exists", mount);
859
860
861
862
863
864
865
866
867
868
869
        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)
{
870
    struct yp_server *server = (struct yp_server *)active_yps;
871
872
873
874

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


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

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

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


925
void yp_shutdown (void)
926
927
928
{
    yp_running = 0;
    yp_update = 1;
929
930
    if (yp_thread)
        thread_join (yp_thread);
931
    curl_global_cleanup();
932
933
    free ((char*)server_version);
    server_version = NULL;
934
    INFO0 ("YP thread down");
935
936
}