1 /* Icecast
2 *
3 * This program is distributed under the GNU General Public License, version 2.
4 * A copy of this license is included with this source.
5 *
6 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 * Michael Smith <msmith@xiph.org>,
8 * oddsock <oddsock@xiph.org>,
9 * Karl Heyes <karl@xiph.org>
10 * and others (see AUTHORS for details).
11 */
12
13 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <curl/curl.h>
22
23 #include "thread/thread.h"
24
25 #include "connection.h"
26 #include "refbuf.h"
27 #include "client.h"
28 #include "logging.h"
29 #include "format.h"
30 #include "source.h"
31 #include "cfgfile.h"
32 #include "stats.h"
33
34 #ifdef WIN32
35 #define snprintf _snprintf
36 #endif
37
38 #define CATMODULE "yp"
39
40 struct yp_server
41 {
42 char *url;
43 char *server_id;
44 unsigned url_timeout;
45 unsigned touch_interval;
46 int remove;
47
48 CURL *curl;
49 struct ypdata_tag *mounts, *pending_mounts;
50 struct yp_server *next;
51 char curl_error[CURL_ERROR_SIZE];
52 };
53
54
55
56 typedef struct ypdata_tag
57 {
58 int remove;
59 int release;
60 int cmd_ok;
61
62 char *sid;
63 char *mount;
64 char *url;
65 char *listen_url;
66 char *server_name;
67 char *server_desc;
68 char *server_genre;
69 char *cluster_password;
70 char *bitrate;
71 char *audio_info;
72 char *server_type;
73 char *current_song;
74 char *subtype;
75
76 struct yp_server *server;
77 time_t next_update;
78 unsigned touch_interval;
79 char *error_msg;
80 int (*process)(struct ypdata_tag *yp, char *s, unsigned len);
81
82 struct ypdata_tag *next;
83 } ypdata_t;
84
85
86 static rwlock_t yp_lock;
87 static mutex_t yp_pending_lock;
88
89 static volatile struct yp_server *active_yps = NULL, *pending_yps = NULL;
90 static volatile int yp_update = 0;
91 static int yp_running;
92 static time_t now;
93 static thread_type *yp_thread;
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 static int do_yp_remove (ypdata_t *yp, char *s, unsigned len);
100 static int do_yp_add (ypdata_t *yp, char *s, unsigned len);
101 static int do_yp_touch (ypdata_t *yp, char *s, unsigned len);
102 static void yp_destroy_ypdata(ypdata_t *ypdata);
103
104
105 /* curl callback used to parse headers coming back from the YP server */
handle_returned_header(void * ptr,size_t size,size_t nmemb,void * stream)106 static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
107 {
108 ypdata_t *yp = stream;
109 unsigned bytes = size * nmemb;
110
111 /* ICECAST_LOG_DEBUG("header from YP is \"%.*s\"", bytes, ptr); */
112 if (strncasecmp (ptr, "YPResponse: 1", 13) == 0)
113 yp->cmd_ok = 1;
114
115 if (strncasecmp (ptr, "YPMessage: ", 11) == 0)
116 {
117 unsigned len = bytes - 11;
118 free (yp->error_msg);
119 yp->error_msg = calloc (1, len);
120 if (yp->error_msg)
121 sscanf (ptr + 11, "%[^\r\n]", yp->error_msg);
122 }
123
124 if (yp->process == do_yp_add)
125 {
126 if (strncasecmp (ptr, "SID: ", 5) == 0)
127 {
128 unsigned len = bytes - 5;
129 free (yp->sid);
130 yp->sid = calloc (1, len);
131 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 if ( sscanf (ptr + 11, "%u", &secs) != 1 )
139 secs = 0;
140 if (secs < 30)
141 secs = 30;
142 ICECAST_LOG_DEBUG("server touch interval is %u", secs);
143 yp->touch_interval = secs;
144 }
145 return (int)bytes;
146 }
147
148
149 /* capture returned data, but don't do anything with it, shouldn't be any */
handle_returned_data(void * ptr,size_t size,size_t nmemb,void * stream)150 static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
151 {
152 return (int)(size*nmemb);
153 }
154
155
156 /* search the active and pending YP server lists */
find_yp_server(const char * url)157 static struct yp_server *find_yp_server (const char *url)
158 {
159 struct yp_server *server;
160
161 server = (struct yp_server *)active_yps;
162 while (server)
163 {
164 if (strcmp (server->url, url) == 0)
165 return server;
166 server = server->next;
167 }
168 server = (struct yp_server *)pending_yps;
169 while (server)
170 {
171 if (strcmp (server->url, url) == 0)
172 break;
173 server = server->next;
174 }
175 return server;
176 }
177
178
destroy_yp_server(struct yp_server * server)179 static void destroy_yp_server (struct yp_server *server)
180 {
181 if (server == NULL)
182 return;
183 ICECAST_LOG_DEBUG("Removing YP server entry for %s", server->url);
184 if (server->curl)
185 curl_easy_cleanup (server->curl);
186 if (server->mounts) ICECAST_LOG_WARN("active ypdata not freed up");
187 if (server->pending_mounts) ICECAST_LOG_WARN("pending ypdata not freed up");
188 free (server->url);
189 free (server->server_id);
190 free (server);
191 }
192
193
194
195 /* search for a ypdata entry corresponding to a specific mountpoint */
find_yp_mount(ypdata_t * mounts,const char * mount)196 static ypdata_t *find_yp_mount (ypdata_t *mounts, const char *mount)
197 {
198 ypdata_t *yp = mounts;
199 while (yp)
200 {
201 if (strcmp (yp->mount, mount) == 0)
202 break;
203 yp = yp->next;
204 }
205 return yp;
206 }
207
208
yp_recheck_config(ice_config_t * config)209 void yp_recheck_config (ice_config_t *config)
210 {
211 int i;
212 struct yp_server *server;
213
214 ICECAST_LOG_DEBUG("Updating YP configuration");
215 thread_rwlock_rlock (&yp_lock);
216
217 server = (struct yp_server *)active_yps;
218 while (server)
219 {
220 server->remove = 1;
221 server = server->next;
222 }
223 client_limit = config->client_limit;
224 free ((char*)server_version);
225 server_version = strdup (config->server_id);
226 /* for each yp url in config, check to see if one exists
227 if not, then add it. */
228 for (i=0 ; i < config->num_yp_directories; i++)
229 {
230 server = find_yp_server (config->yp_url[i]);
231 if (server == NULL)
232 {
233 server = calloc (1, sizeof (struct yp_server));
234
235 if (server == NULL)
236 {
237 destroy_yp_server (server);
238 break;
239 }
240 server->server_id = strdup ((char *)server_version);
241 server->url = strdup (config->yp_url[i]);
242 server->url_timeout = config->yp_url_timeout[i];
243 server->touch_interval = config->yp_touch_interval[i];
244 server->curl = curl_easy_init();
245 if (server->curl == NULL)
246 {
247 destroy_yp_server (server);
248 break;
249 }
250 if (server->url_timeout > 10 || server->url_timeout < 1)
251 server->url_timeout = 6;
252 if (server->touch_interval < 30)
253 server->touch_interval = 30;
254 curl_easy_setopt (server->curl, CURLOPT_USERAGENT, server->server_id);
255 curl_easy_setopt (server->curl, CURLOPT_URL, server->url);
256 curl_easy_setopt (server->curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
257 curl_easy_setopt (server->curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
258 curl_easy_setopt (server->curl, CURLOPT_WRITEDATA, server->curl);
259 curl_easy_setopt (server->curl, CURLOPT_TIMEOUT, server->url_timeout);
260 curl_easy_setopt (server->curl, CURLOPT_NOSIGNAL, 1L);
261 curl_easy_setopt (server->curl, CURLOPT_FOLLOWLOCATION, 1L);
262 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 server->url, server->url_timeout, server->touch_interval);
268 }
269 else
270 {
271 server->remove = 0;
272 }
273 }
274 thread_rwlock_unlock (&yp_lock);
275 yp_update = 1;
276 }
277
278
yp_initialize(void)279 void yp_initialize(void)
280 {
281 ice_config_t *config = config_get_config();
282 thread_rwlock_create (&yp_lock);
283 thread_mutex_create (&yp_pending_lock);
284 yp_recheck_config (config);
285 config_release_config ();
286 yp_thread = thread_create("YP Touch Thread", yp_update_thread,
287 (void *)NULL, THREAD_ATTACHED);
288 }
289
290
291
292 /* handler for curl, checks if successful handling occurred
293 * return 0 for ok, -1 for this entry failed, -2 for server fail.
294 * On failure case, update and process are modified
295 */
send_to_yp(const char * cmd,ypdata_t * yp,char * post)296 static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
297 {
298 int curlcode;
299 struct yp_server *server = yp->server;
300
301 /* ICECAST_LOG_DEBUG("send YP (%s):%s", cmd, post); */
302 yp->cmd_ok = 0;
303 curl_easy_setopt (server->curl, CURLOPT_POSTFIELDS, post);
304 curl_easy_setopt (server->curl, CURLOPT_WRITEHEADER, yp);
305 curlcode = curl_easy_perform (server->curl);
306 if (curlcode)
307 {
308 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 if (yp->cmd_ok == 0)
314 {
315 if (yp->error_msg == NULL)
316 yp->error_msg = strdup ("no response from server");
317 if (yp->process == do_yp_add)
318 {
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 /* At this point the touch request failed, either because they rejected our session
325 * or the server isn't accessible. This means we have to wait before doing another
326 * add request. We have a minimum delay but we could allow the directory server to
327 * give us a wait time using the TouchFreq header. This time could be given in such
328 * cases as a firewall block or incorrect listenurl.
329 */
330 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;
340 }
341 ICECAST_LOG_DEBUG("YP %s at %s succeeded", cmd, server->url);
342 return 0;
343 }
344
345
346 /* routines for building and issues requests to the YP server */
do_yp_remove(ypdata_t * yp,char * s,unsigned len)347 static int do_yp_remove (ypdata_t *yp, char *s, unsigned len)
348 {
349 int ret = 0;
350
351 if (yp->sid)
352 {
353 ret = snprintf (s, len, "action=remove&sid=%s", yp->sid);
354 if (ret >= (signed)len)
355 return ret+1;
356
357 ICECAST_LOG_INFO("clearing up YP entry for %s", yp->mount);
358 ret = send_to_yp ("remove", yp, s);
359 free (yp->sid);
360 yp->sid = NULL;
361 }
362 yp->remove = 1;
363 yp->process = do_yp_add;
364 yp_update = 1;
365
366 return ret;
367 }
368
369
do_yp_add(ypdata_t * yp,char * s,unsigned len)370 static int do_yp_add (ypdata_t *yp, char *s, unsigned len)
371 {
372 int ret;
373 char *value;
374
375 value = stats_get_value (yp->mount, "server_type");
376 add_yp_info (yp, value, YP_SERVER_TYPE);
377 free (value);
378
379 value = stats_get_value (yp->mount, "server_name");
380 add_yp_info (yp, value, YP_SERVER_NAME);
381 free (value);
382
383 value = stats_get_value (yp->mount, "server_url");
384 add_yp_info (yp, value, YP_SERVER_URL);
385 free (value);
386
387 value = stats_get_value (yp->mount, "genre");
388 add_yp_info (yp, value, YP_SERVER_GENRE);
389 free (value);
390
391 value = stats_get_value (yp->mount, "bitrate");
392 if (value == NULL)
393 value = stats_get_value (yp->mount, "ice-bitrate");
394 add_yp_info (yp, value, YP_BITRATE);
395 free (value);
396
397 value = stats_get_value (yp->mount, "server_description");
398 add_yp_info (yp, value, YP_SERVER_DESC);
399 free (value);
400
401 value = stats_get_value (yp->mount, "subtype");
402 add_yp_info (yp, value, YP_SUBTYPE);
403 free (value);
404
405 value = stats_get_value (yp->mount, "audio_info");
406 add_yp_info (yp, value, YP_AUDIO_INFO);
407 free (value);
408
409 ret = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc="
410 "%s&url=%s&listenurl=%s&type=%s&stype=%s&b=%s&%s\r\n",
411 yp->server_name, yp->server_genre, yp->cluster_password,
412 yp->server_desc, yp->url, yp->listen_url,
413 yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
414 if (ret >= (signed)len)
415 return ret+1;
416 ret = send_to_yp ("add", yp, s);
417 if (ret == 0)
418 {
419 yp->process = do_yp_touch;
420 /* force first touch in 5 secs */
421 yp->next_update = time(NULL) + 5;
422 }
423 return ret;
424 }
425
426
do_yp_touch(ypdata_t * yp,char * s,unsigned len)427 static int do_yp_touch (ypdata_t *yp, char *s, unsigned len)
428 {
429 unsigned listeners = 0, max_listeners = 1;
430 char *val, *artist, *title;
431 int ret;
432
433 artist = (char *)stats_get_value (yp->mount, "artist");
434 title = (char *)stats_get_value (yp->mount, "title");
435 if (artist || title)
436 {
437 char *song;
438 char *separator = " - ";
439 if (artist == NULL)
440 {
441 artist = strdup("");
442 separator = "";
443 }
444 if (title == NULL) title = strdup("");
445 song = malloc (strlen (artist) + strlen (title) + strlen (separator) +1);
446 if (song)
447 {
448 sprintf (song, "%s%s%s", artist, separator, title);
449 add_yp_info(yp, song, YP_CURRENT_SONG);
450 stats_event (yp->mount, "yp_currently_playing", song);
451 free (song);
452 }
453 }
454 free (artist);
455 free (title);
456
457 val = (char *)stats_get_value (yp->mount, "listeners");
458 if (val)
459 {
460 listeners = atoi (val);
461 free (val);
462 }
463 val = stats_get_value (yp->mount, "max_listeners");
464 if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
465 max_listeners = client_limit;
466 else
467 max_listeners = atoi (val);
468 free (val);
469
470 val = stats_get_value (yp->mount, "subtype");
471 if (val)
472 {
473 add_yp_info (yp, val, YP_SUBTYPE);
474 free (val);
475 }
476
477 ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
478 "&listeners=%u&max_listeners=%u&stype=%s\r\n",
479 yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
480
481 if (ret >= (signed)len)
482 return ret+1; /* space required for above text and nul*/
483
484 if (send_to_yp ("touch", yp, s) == 0)
485 {
486 yp->next_update = now + yp->touch_interval;
487 return 0;
488 }
489 return -1;
490 }
491
492
493
process_ypdata(struct yp_server * server,ypdata_t * yp)494 static int process_ypdata (struct yp_server *server, ypdata_t *yp)
495 {
496 unsigned len = 1024;
497 char *s = NULL, *tmp;
498
499 if (now < yp->next_update)
500 return 0;
501
502 /* loop just in case the memory area isn't big enough */
503 while (1)
504 {
505 int ret;
506 if ((tmp = realloc (s, len)) == NULL)
507 return 0;
508 s = tmp;
509
510 if (yp->release)
511 {
512 yp->process = do_yp_remove;
513 yp->next_update = 0;
514 }
515
516 ret = yp->process (yp, s, len);
517 if (ret <= 0)
518 {
519 free (s);
520 return ret;
521 }
522 len = ret;
523 }
524 return 0;
525 }
526
527
yp_process_server(struct yp_server * server)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 yp = server->mounts;
535 while (yp)
536 {
537 now = time (NULL);
538 /* if one of the streams shows that the server cannot be contacted then mark the
539 * other entries for an update later. Assume YP server is dead and skip it for now
540 */
541 if (state == -2)
542 {
543 ICECAST_LOG_DEBUG("skipping %s on %s", yp->mount, server->url);
544 yp->process = do_yp_add;
545 yp->next_update += 900;
546 }
547 else
548 state = process_ypdata (server, yp);
549 yp = yp->next;
550 }
551 }
552
553
554
create_yp_entry(const char * mount)555 static ypdata_t *create_yp_entry (const char *mount)
556 {
557 ypdata_t *yp;
558 char *s;
559
560 yp = calloc (1, sizeof (ypdata_t));
561 do
562 {
563 unsigned len = 512;
564 int ret;
565 char *url;
566 mount_proxy *mountproxy = NULL;
567 ice_config_t *config;
568
569 if (yp == NULL)
570 break;
571 yp->mount = strdup (mount);
572 yp->server_name = strdup ("");
573 yp->server_desc = strdup ("");
574 yp->server_genre = strdup ("");
575 yp->bitrate = strdup ("");
576 yp->server_type = strdup ("");
577 yp->cluster_password = strdup ("");
578 yp->url = strdup ("");
579 yp->current_song = strdup ("");
580 yp->audio_info = strdup ("");
581 yp->subtype = strdup ("");
582 yp->process = do_yp_add;
583
584 url = malloc (len);
585 if (url == NULL)
586 break;
587 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 s = realloc (url, ++ret);
592 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 yp->listen_url = util_url_escape (url);
602 free (url);
603 if (yp->listen_url == NULL)
604 break;
605
606 return yp;
607 } while (0);
608
609 yp_destroy_ypdata (yp);
610 return NULL;
611 }
612
613
614 /* Check for changes in the YP servers configured */
check_servers(void)615 static void check_servers (void)
616 {
617 struct yp_server *server = (struct yp_server *)active_yps,
618 **server_p = (struct yp_server **)&active_yps;
619
620 while (server)
621 {
622 if (server->remove)
623 {
624 struct yp_server *to_go = server;
625 ICECAST_LOG_DEBUG("YP server \"%s\"removed", server->url);
626 *server_p = server->next;
627 server = server->next;
628 destroy_yp_server (to_go);
629 continue;
630 }
631 server_p = &server->next;
632 server = server->next;
633 }
634 /* add new server entries */
635 while (pending_yps)
636 {
637 avl_node *node;
638
639 server = (struct yp_server *)pending_yps;
640 pending_yps = server->next;
641
642 ICECAST_LOG_DEBUG("Add pending yps %s", server->url);
643 server->next = (struct yp_server *)active_yps;
644 active_yps = server;
645
646 /* new YP server configured, need to populate with existing sources */
647 avl_tree_rlock (global.source_tree);
648 node = avl_get_first (global.source_tree);
649 while (node)
650 {
651 ypdata_t *yp;
652
653 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 yp->server = server;
658 yp->touch_interval = server->touch_interval;
659 yp->next = server->mounts;
660 server->mounts = yp;
661 }
662 node = avl_get_next (node);
663 }
664 avl_tree_unlock (global.source_tree);
665 }
666 }
667
668
add_pending_yp(struct yp_server * server)669 static void add_pending_yp (struct yp_server *server)
670 {
671 ypdata_t *current, *yp;
672 unsigned count = 0;
673
674 if (server->pending_mounts == NULL)
675 return;
676 current = server->mounts;
677 server->mounts = server->pending_mounts;
678 server->pending_mounts = NULL;
679 yp = server->mounts;
680 while (1)
681 {
682 count++;
683 if (yp->next == NULL)
684 break;
685 yp = yp->next;
686 }
687 yp->next = current;
688 ICECAST_LOG_DEBUG("%u YP entries added to %s", count, server->url);
689 }
690
691
delete_marked_yp(struct yp_server * server)692 static void delete_marked_yp (struct yp_server *server)
693 {
694 ypdata_t *yp = server->mounts, **prev = &server->mounts;
695
696 while (yp)
697 {
698 if (yp->remove)
699 {
700 ypdata_t *to_go = yp;
701 ICECAST_LOG_DEBUG("removed %s from YP server %s", yp->mount, server->url);
702 *prev = yp->next;
703 yp = yp->next;
704 yp_destroy_ypdata (to_go);
705 continue;
706 }
707 prev = &yp->next;
708 yp = yp->next;
709 }
710 }
711
712
yp_update_thread(void * arg)713 static void *yp_update_thread(void *arg)
714 {
715 ICECAST_LOG_INFO("YP update thread started");
716
717 yp_running = 1;
718 while (yp_running)
719 {
720 struct yp_server *server;
721
722 thread_sleep (200000);
723
724 /* do the YP communication */
725 thread_rwlock_rlock (&yp_lock);
726 server = (struct yp_server *)active_yps;
727 while (server)
728 {
729 /* ICECAST_LOG_DEBUG("trying %s", server->url); */
730 yp_process_server (server);
731 server = server->next;
732 }
733 thread_rwlock_unlock (&yp_lock);
734
735 /* update the local YP structure */
736 if (yp_update)
737 {
738 thread_rwlock_wlock (&yp_lock);
739 check_servers ();
740 server = (struct yp_server *)active_yps;
741 while (server)
742 {
743 /* ICECAST_LOG_DEBUG("Checking yps %s", server->url); */
744 add_pending_yp (server);
745 delete_marked_yp (server);
746 server = server->next;
747 }
748 yp_update = 0;
749 thread_rwlock_unlock (&yp_lock);
750 }
751 }
752 thread_rwlock_destroy (&yp_lock);
753 thread_mutex_destroy (&yp_pending_lock);
754 /* free server and ypdata left */
755 while (active_yps)
756 {
757 struct yp_server *server = (struct yp_server *)active_yps;
758 active_yps = server->next;
759 destroy_yp_server (server);
760 }
761
762 return NULL;
763 }
764
765
766
yp_destroy_ypdata(ypdata_t * ypdata)767 static void yp_destroy_ypdata(ypdata_t *ypdata)
768 {
769 if (ypdata) {
770 if (ypdata->mount) {
771 free (ypdata->mount);
772 }
773 if (ypdata->url) {
774 free (ypdata->url);
775 }
776 if (ypdata->sid) {
777 free(ypdata->sid);
778 }
779 if (ypdata->server_name) {
780 free(ypdata->server_name);
781 }
782 if (ypdata->server_desc) {
783 free(ypdata->server_desc);
784 }
785 if (ypdata->server_genre) {
786 free(ypdata->server_genre);
787 }
788 if (ypdata->cluster_password) {
789 free(ypdata->cluster_password);
790 }
791 if (ypdata->listen_url) {
792 free(ypdata->listen_url);
793 }
794 if (ypdata->current_song) {
795 free(ypdata->current_song);
796 }
797 if (ypdata->bitrate) {
798 free(ypdata->bitrate);
799 }
800 if (ypdata->server_type) {
801 free(ypdata->server_type);
802 }
803 if (ypdata->audio_info) {
804 free(ypdata->audio_info);
805 }
806 free (ypdata->subtype);
807 free (ypdata->error_msg);
808 free (ypdata);
809 }
810 }
811
add_yp_info(ypdata_t * yp,void * info,int type)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 escaped = util_url_escape(info);
820 if (escaped == NULL)
821 return;
822
823 switch (type)
824 {
825 case YP_SERVER_NAME:
826 free (yp->server_name);
827 yp->server_name = escaped;
828 break;
829 case YP_SERVER_DESC:
830 free (yp->server_desc);
831 yp->server_desc = escaped;
832 break;
833 case YP_SERVER_GENRE:
834 free (yp->server_genre);
835 yp->server_genre = escaped;
836 break;
837 case YP_SERVER_URL:
838 free (yp->url);
839 yp->url = escaped;
840 break;
841 case YP_BITRATE:
842 free (yp->bitrate);
843 yp->bitrate = escaped;
844 break;
845 case YP_AUDIO_INFO:
846 free (yp->audio_info);
847 yp->audio_info = escaped;
848 break;
849 case YP_SERVER_TYPE:
850 free (yp->server_type);
851 yp->server_type = escaped;
852 break;
853 case YP_CURRENT_SONG:
854 free (yp->current_song);
855 yp->current_song = escaped;
856 break;
857 case YP_CLUSTER_PASSWORD:
858 free (yp->cluster_password);
859 yp->cluster_password = escaped;
860 break;
861 case YP_SUBTYPE:
862 free (yp->subtype);
863 yp->subtype = escaped;
864 break;
865 default:
866 free (escaped);
867 }
868 }
869
870
871 /* Add YP entries to active servers */
yp_add(const char * mount)872 void yp_add (const char *mount)
873 {
874 struct yp_server *server;
875
876 /* make sure YP thread is not modifying the lists */
877 thread_rwlock_rlock (&yp_lock);
878
879 /* make sure we don't race against another yp_add */
880 thread_mutex_lock (&yp_pending_lock);
881 server = (struct yp_server *)active_yps;
882 while (server)
883 {
884 ypdata_t *yp;
885
886 /* on-demand relays may already have a YP entry */
887 yp = find_yp_mount (server->mounts, mount);
888 if (yp == NULL)
889 {
890 /* add new ypdata to each servers pending yp */
891 yp = create_yp_entry (mount);
892 if (yp)
893 {
894 ICECAST_LOG_DEBUG("Adding %s to %s", mount, server->url);
895 yp->server = server;
896 yp->touch_interval = server->touch_interval;
897 yp->next = server->pending_mounts;
898 yp->next_update = time(NULL) + 60;
899 server->pending_mounts = yp;
900 yp_update = 1;
901 }
902 }
903 else
904 ICECAST_LOG_DEBUG("YP entry %s already exists", mount);
905 server = server->next;
906 }
907 thread_mutex_unlock (&yp_pending_lock);
908 thread_rwlock_unlock (&yp_lock);
909 }
910
911
912
913 /* Mark an existing entry in the YP list as to be marked for deletion */
yp_remove(const char * mount)914 void yp_remove (const char *mount)
915 {
916 struct yp_server *server = (struct yp_server *)active_yps;
917
918 thread_rwlock_rlock (&yp_lock);
919 while (server)
920 {
921 ypdata_t *list = server->mounts;
922
923 while (1)
924 {
925 ypdata_t *yp = find_yp_mount (list, mount);
926 if (yp == NULL)
927 break;
928 if (yp->release || yp->remove)
929 {
930 list = yp->next;
931 continue; /* search again these are old entries */
932 }
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 /* This is similar to yp_remove, but we force a touch
944 * attempt */
yp_touch(const char * mount)945 void yp_touch (const char *mount)
946 {
947 struct yp_server *server = (struct yp_server *)active_yps;
948 ypdata_t *search_list = NULL;
949
950 thread_rwlock_rlock (&yp_lock);
951 if (server)
952 search_list = server->mounts;
953
954 while (server)
955 {
956 ypdata_t *yp = find_yp_mount (search_list, mount);
957 if (yp)
958 {
959 /* we may of found old entries not purged yet, so skip them */
960 if (yp->release != 0 || yp->remove != 0)
961 {
962 search_list = yp->next;
963 continue;
964 }
965 /* don't update the directory if there is a touch scheduled soon */
966 if (yp->process == do_yp_touch && now + yp->touch_interval - yp->next_update > 60)
967 yp->next_update = now + 3;
968 }
969 server = server->next;
970 if (server)
971 search_list = server->mounts;
972 }
973 thread_rwlock_unlock (&yp_lock);
974 }
975
976
yp_shutdown(void)977 void yp_shutdown (void)
978 {
979 yp_running = 0;
980 yp_update = 1;
981 if (yp_thread)
982 thread_join (yp_thread);
983 curl_global_cleanup();
984 free ((char*)server_version);
985 server_version = NULL;
986 ICECAST_LOG_INFO("YP thread down");
987 }
988
989