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 2012-2015, Karl Heyes <karl@kheyes.plus.com>,
7 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org>,
8 * Michael Smith <msmith@xiph.org>,
9 * oddsock <oddsock@xiph.org>,
10 * Karl Heyes <karl@xiph.org>
11 * and others (see AUTHORS for details).
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include "compat.h"
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #ifdef HAVE_SYS_STAT_H
24 #include <sys/stat.h>
25 #endif
26 #include <errno.h>
27 #ifdef HAVE_FCNTL_H
28 #include <fcntl.h>
29 #endif
30
31 #ifdef HAVE_POLL
32 #include <sys/poll.h>
33 #endif
34
35 #ifdef _MSC_VER
36 #include <winsock2.h>
37 #include <windows.h>
38 #else
39 #include <unistd.h>
40 #include <sys/time.h>
41 # ifdef HAVE_SYS_SOCKET_H
42 # include <sys/socket.h>
43 # endif
44 # ifndef SCN_OFF_T
45 # define SCN_OFF_T SCNdMAX
46 # endif
47 # ifndef PRI_OFF_T
48 # define PRI_OFF_T PRIdMAX
49 # endif
50 #endif
51 #ifndef O_CLOEXEC
52 #define O_CLOEXEC 0
53 #endif
54 #ifndef O_BINARY
55 #define O_BINARY 0
56 #endif
57
58 #include "thread/thread.h"
59 #include "avl/avl.h"
60 #include "httpp/httpp.h"
61 #include "net/sock.h"
62
63 #include "fserve.h"
64 #include "connection.h"
65 #include "global.h"
66 #include "refbuf.h"
67 #include "client.h"
68 #include "stats.h"
69 #include "format.h"
70 #include "logging.h"
71 #include "cfgfile.h"
72 #include "util.h"
73 #include "admin.h"
74 #include "slave.h"
75
76 #include "format_mp3.h"
77
78 #undef CATMODULE
79 #define CATMODULE "fserve"
80
81 #define BUFSIZE 4096
82
83 static spin_t pending_lock;
84 static avl_tree *mimetypes = NULL;
85 static avl_tree *fh_cache = NULL;
86 #ifndef HAVE_PREAD
87 static mutex_t seekread_lock;
88 #endif
89
90 typedef struct {
91 char *ext;
92 char *type;
93 } mime_type;
94
95 typedef struct {
96 fbinfo finfo;
97 mutex_t lock;
98 int prev_count;
99 int refcount;
100 int peak;
101 int max;
102 icefile_handle f;
103 time_t stats_update;
104 time_t expire;
105 long frame_start_pos;
106 stats_handle_t stats;
107 format_plugin_t *format;
108 struct rate_calc *out_bitrate;
109 avl_tree *clients;
110 } fh_node;
111
112 int fserve_running;
113
114 static int _delete_mapping(void *mapping);
115 static int prefile_send (client_t *client);
116 static int file_send (client_t *client);
117 static int _compare_fh(void *arg, void *a, void *b);
118 static int _delete_fh (void *mapping);
119 static void remove_fh_from_cache (fh_node *fh);
120
121 static fh_node no_file;
122
123
fserve_initialize(void)124 void fserve_initialize(void)
125 {
126 if (fserve_running) return;
127
128 ice_config_t *config = config_get_config();
129
130 mimetypes = NULL;
131 thread_spin_create (&pending_lock);
132 #ifndef HAVE_PREAD
133 thread_mutex_create (&seekread_lock);
134 #endif
135 fh_cache = avl_tree_new (_compare_fh, NULL);
136
137 fserve_recheck_mime_types (config);
138 config_release_config();
139
140 stats_event_flags (NULL, "file_connections", "0", STATS_COUNTERS);
141 fserve_running = 1;
142 memset (&no_file, 0, sizeof (no_file));
143 thread_mutex_create (&no_file.lock);
144 no_file.clients = avl_tree_new (client_compare, NULL);
145 no_file.refcount = 1;
146 no_file.expire = (time_t)-1;
147 no_file.f = -1;
148 avl_insert (fh_cache, &no_file);
149 INFO0("file serving started");
150 }
151
fserve_shutdown(void)152 void fserve_shutdown(void)
153 {
154 fserve_running = 0;
155 if (mimetypes)
156 avl_tree_free (mimetypes, _delete_mapping);
157 if (fh_cache)
158 {
159 int count = 20;
160 avl_delete (fh_cache, &no_file, NULL);
161 while (fh_cache->length > 1 && count)
162 {
163 fh_node *fh = fh_cache->root->right->key;
164 if (fh && fh->refcount == 0)
165 {
166 remove_fh_from_cache (fh);
167 continue;
168 }
169 DEBUG1 ("waiting for %u entries to clear", fh_cache->length);
170 thread_sleep (100000);
171 count--;
172 }
173 avl_tree_free (fh_cache, _delete_fh);
174 }
175
176 thread_spin_destroy (&pending_lock);
177 #ifndef HAVE_PREAD
178 thread_mutex_destroy (&seekread_lock);
179 #endif
180 INFO0("file serving stopped");
181 }
182
183
184 /* string returned needs to be free'd */
fserve_content_type(const char * path)185 char *fserve_content_type (const char *path)
186 {
187 char *ext = util_get_extension(path);
188 mime_type exttype = { NULL, NULL };
189 void *result;
190 char *type;
191
192 if (ext == NULL)
193 return strdup ("text/html");
194 exttype.ext = strdup (ext);
195
196 thread_spin_lock (&pending_lock);
197 if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result))
198 {
199 mime_type *mime = result;
200 type = strdup (mime->type);
201 }
202 else
203 type = strdup ("application/octet-stream");
204 thread_spin_unlock (&pending_lock);
205 free (exttype.ext);
206 return type;
207 }
208
209
_compare_fh(void * arg,void * a,void * b)210 static int _compare_fh(void *arg, void *a, void *b)
211 {
212 fh_node *x = a, *y = b;
213 int r = 0;
214
215 if (x->finfo.mount == NULL && y->finfo.mount)
216 return -1;
217 if (x->finfo.mount && y->finfo.mount == NULL)
218 return 1;
219 if (x->finfo.mount && y->finfo.mount)
220 {
221 r = strcmp (x->finfo.mount, y->finfo.mount);
222 if (r) return r;
223 }
224 r = (int)x->finfo.flags - y->finfo.flags;
225 return r;
226 }
227
228
_delete_fh(void * mapping)229 static int _delete_fh (void *mapping)
230 {
231 fh_node *fh = mapping;
232 if (fh == &no_file)
233 {
234 ERROR0 ("no file handle free detected");
235 return 0;
236 }
237 if (fh->refcount)
238 WARN2 ("handle for %s has refcount %d", fh->finfo.mount, fh->refcount);
239 else
240 thread_mutex_destroy (&fh->lock);
241
242 file_close (&fh->f);
243 if (fh->format)
244 {
245 free (fh->format->mount);
246 format_plugin_clear (fh->format, NULL);
247 free (fh->format);
248 }
249 if (fh->clients)
250 avl_tree_free (fh->clients, NULL);
251 rate_free (fh->out_bitrate);
252 free (fh->finfo.mount);
253 free (fh->finfo.fallback);
254 free (fh);
255
256 return 1;
257 }
258
259
remove_fh_from_cache(fh_node * fh)260 static void remove_fh_from_cache (fh_node *fh)
261 {
262 if (fh->refcount)
263 WARN2 ("removing %s with %d still on", fh->finfo.mount, fh->refcount);
264 avl_delete (fh_cache, fh, NULL);
265 }
266
267
remove_from_fh(fh_node * fh,client_t * client)268 static void remove_from_fh (fh_node *fh, client_t *client)
269 {
270 thread_mutex_lock (&fh->lock);
271 fh->refcount--;
272 if (fh->clients)
273 {
274 avl_delete (fh->clients, client, NULL);
275 if ((fh->refcount != fh->clients->length && fh->finfo.mount) || ((fh->refcount != fh->clients->length+1) && fh->finfo.mount == NULL))
276 ERROR3 (" on %s, with ref %d, len %d", fh->finfo.mount, fh->refcount, fh->clients->length);
277 }
278 if (fh->refcount == 0 && fh->finfo.mount)
279 {
280 rate_free (fh->out_bitrate);
281 if ((fh->finfo.flags & FS_FALLBACK) == 0)
282 {
283 fh->out_bitrate = NULL;
284 if (fh->finfo.flags & FS_DELETE)
285 {
286 thread_mutex_unlock (&fh->lock);
287 _delete_fh (fh);
288 return;
289 }
290 DEBUG1 ("setting timeout as no clients on %s", fh->finfo.mount);
291 fh->expire = time(NULL) + 10;
292 }
293 fh->out_bitrate = rate_setup (10000, 1000);
294 }
295 thread_mutex_unlock (&fh->lock);
296 }
297
298
find_fh(fbinfo * finfo)299 static fh_node *find_fh (fbinfo *finfo)
300 {
301 char *s = finfo->mount;
302 fh_node fh, *result = NULL;
303 if (finfo->mount == NULL)
304 {
305 ERROR0 ("missing name");
306 return NULL;
307 }
308 memcpy (&fh.finfo, finfo, sizeof (fbinfo));
309 if (strncmp (s, "fallback-", 9) == 0)
310 {
311 fh.finfo.flags |= FS_FALLBACK;
312 fh.finfo.mount = s+9;
313 }
314 else if (strncmp (s, "file-", 5) == 0)
315 fh.finfo.mount = s+5;
316 if (avl_get_by_key (fh_cache, &fh, (void**)&result) == 0)
317 {
318 DEBUG2 ("mount %s (%d)", finfo->mount, finfo->flags);
319 return result;
320 }
321 DEBUG2 ("%s (%d) not found in cache", finfo->mount, finfo->flags);
322 return NULL;
323 }
324
325
fh_add_client(fh_node * fh,client_t * client)326 static void fh_add_client (fh_node *fh, client_t *client)
327 {
328 if (fh->clients == NULL)
329 return;
330 avl_insert (fh->clients, client);
331 fh->refcount++;
332 if ((fh->refcount != fh->clients->length && fh->finfo.mount) || ((fh->refcount != fh->clients->length+1) && fh->finfo.mount == NULL))
333 ERROR3 (" on %s, with ref %d, len %d", fh->finfo.mount, fh->refcount, fh->clients->length);
334 if (fh->refcount > fh->peak)
335 fh->peak = fh->refcount;
336 if (fh->finfo.mount)
337 DEBUG2 ("refcount now %d for %s", fh->refcount, fh->finfo.mount);
338 }
339
340
341 /* find/create handle and return it with the structure in a locked state */
open_fh(fbinfo * finfo)342 static fh_node *open_fh (fbinfo *finfo)
343 {
344 fh_node *fh, *result;
345
346 if (finfo->mount == NULL)
347 finfo->mount = "";
348 fh = calloc (1, sizeof (fh_node));
349 memcpy (&fh->finfo, finfo, sizeof (fbinfo));
350 if (avl_get_by_key (fh_cache, fh, (void**)&result) == 0)
351 {
352 free (fh);
353 thread_mutex_lock (&result->lock);
354 avl_tree_unlock (fh_cache);
355 if (finfo->flags & FS_FALLBACK)
356 {
357 if (result->finfo.type != finfo->type && finfo->type != FORMAT_TYPE_UNDEFINED)
358 {
359 WARN1 ("format mismatched for %s", finfo->mount);
360 thread_mutex_unlock (&result->lock);
361 return NULL;
362 }
363 result->expire = (time_t)-1;
364 }
365 return result;
366 }
367
368 // insert new one
369 if (fh->finfo.mount[0])
370 {
371 char *fullpath= util_get_path_from_normalised_uri (fh->finfo.mount, fh->finfo.flags&FS_USE_ADMIN);
372 char *contenttype = fserve_content_type (fullpath);
373 format_type_t type = format_get_type (contenttype);
374
375 if (fh->finfo.type == FORMAT_TYPE_UNDEFINED)
376 fh->finfo.type = type;
377 if (finfo->flags & FS_FALLBACK)
378 {
379 if (fh->finfo.type != type && type != FORMAT_TYPE_UNDEFINED && fh->finfo.type != FORMAT_TYPE_UNDEFINED)
380 {
381 avl_tree_unlock (fh_cache);
382 free (contenttype);
383 free (fullpath);
384 free (fh);
385 WARN1 ("format mismatched for %s", finfo->mount);
386 return NULL;
387 }
388 fh->expire = (time_t)-1;
389 INFO2 ("lookup of fallback file \"%s\" (%d)", finfo->mount, finfo->limit);
390 }
391 else
392 INFO1 ("lookup of \"%s\"", finfo->mount);
393 if (file_open (&fh->f, fullpath) < 0)
394 {
395 INFO1 ("Failed to open \"%s\"", fullpath);
396 avl_tree_unlock (fh_cache);
397 free (contenttype);
398 free (fullpath);
399 free (fh);
400 return NULL;
401 }
402 free (fullpath);
403 fh->format = calloc (1, sizeof (format_plugin_t));
404 fh->format->type = fh->finfo.type;
405 fh->format->contenttype = strdup (contenttype);
406 free (contenttype);
407 if (fh->finfo.type != FORMAT_TYPE_UNDEFINED)
408 {
409 fh->format->mount = strdup (fh->finfo.mount);
410 if (format_get_plugin (fh->format) < 0)
411 {
412 avl_tree_unlock (fh_cache);
413 file_close (&fh->f);
414 free (fh->format);
415 free (fh);
416 return NULL;
417 }
418 format_check_t fcheck;
419 fcheck.fd = fh->f;
420 fcheck.desc = finfo->mount;
421 if (format_check_frames (&fcheck) < 0 || fcheck.type == FORMAT_TYPE_UNDEFINED)
422 WARN1 ("different type detected for %s", finfo->mount);
423 else
424 {
425 if (fh->finfo.limit && fcheck.bitrate > 0)
426 {
427 float ratio = (float)fh->finfo.limit / (fcheck.bitrate/8);
428 if (ratio < 0.9 || ratio > 1.1)
429 WARN3 ("bitrate from %s (%d), was expecting %d", finfo->mount, (fcheck.bitrate/1000), (fh->finfo.limit/1000*8));
430 }
431 }
432 }
433 if (fh->finfo.limit)
434 fh->out_bitrate = rate_setup (10000, 1000);
435 }
436 fh->clients = avl_tree_new (client_compare, NULL);
437 thread_mutex_create (&fh->lock);
438 thread_mutex_lock (&fh->lock);
439 avl_insert (fh_cache, fh);
440 avl_tree_unlock (fh_cache);
441
442 fh->refcount = 0;
443 fh->peak = 0;
444 fh->finfo.mount = strdup (finfo->mount);
445 fh->finfo.fallback = NULL;
446
447 return fh;
448 }
449
450
451 /* client has requested a file, so check for it and send the file. Do not
452 * refer to the client_t afterwards. return 0 for success, -1 on error.
453 */
fserve_client_create(client_t * httpclient,const char * path)454 int fserve_client_create (client_t *httpclient, const char *path)
455 {
456 struct stat file_buf;
457 char *fullpath;
458 int m3u_requested = 0, m3u_file_available = 1;
459 int xspf_requested = 0, xspf_file_available = 1;
460 int ret = -1;
461 ice_config_t *config;
462 fbinfo finfo;
463 char fsize[20];
464
465 fullpath = util_get_path_from_normalised_uri (path, 0);
466 DEBUG2 ("checking for file %s (%s)", path, fullpath);
467
468 if (strcmp (util_get_extension (fullpath), "m3u") == 0)
469 m3u_requested = 1;
470
471 if (strcmp (util_get_extension (fullpath), "xspf") == 0)
472 xspf_requested = 1;
473
474 /* check for the actual file */
475 if (stat (fullpath, &file_buf) != 0)
476 {
477 /* the m3u can be generated, but send an m3u file if available */
478 if (m3u_requested == 0 && xspf_requested == 0)
479 {
480 if (redirect_client (path, httpclient) == 0)
481 {
482 if ((httpclient->flags & CLIENT_SKIP_ACCESSLOG) == 0)
483 WARN2 ("req for file \"%s\" %s", fullpath, strerror (errno));
484 ret = client_send_404 (httpclient, "The file you requested could not be found");
485 }
486 free (fullpath);
487 return ret;
488 }
489 m3u_file_available = 0;
490 xspf_file_available = 0;
491 }
492
493 client_set_queue (httpclient, NULL);
494 httpclient->refbuf = refbuf_new (4096);
495
496 if (m3u_requested && m3u_file_available == 0)
497 {
498 const char *host = httpp_getvar (httpclient->parser, "host"),
499 *args = httpp_getvar (httpclient->parser, HTTPP_VAR_QUERYARGS),
500 *at = "", *user = "", *pass ="";
501 char *sourceuri = strdup (path);
502 char *dot = strrchr (sourceuri, '.');
503 char *protocol = not_ssl_connection (&httpclient->connection) ? "http" : "https";
504 const char *agent = httpp_getvar (httpclient->parser, "user-agent");
505 int x;
506 char scratch[1000];
507
508 if (agent)
509 {
510 if (strstr (agent, "QTS") || strstr (agent, "QuickTime"))
511 protocol = "icy";
512 }
513 /* at least a couple of players (fb2k/winamp) are reported to send a
514 * host header but without the port number. So if we are missing the
515 * port then lets treat it as if no host line was sent */
516 if (host && strchr (host, ':') == NULL)
517 host = NULL;
518
519 *dot = 0;
520 if (httpclient->username && httpclient->password)
521 {
522 at = "@";
523 user = httpclient->username;
524 pass = httpclient->password;
525 }
526 httpclient->respcode = 200;
527 if (host == NULL)
528 {
529 config = config_get_config();
530 x = snprintf (scratch, sizeof scratch,
531 "%s://%s%s%s%s%s:%d%s%s\r\n",
532 protocol,
533 user, at[0]?":":"", pass, at,
534 config->hostname, config->port,
535 sourceuri,
536 args?args:"");
537 config_release_config();
538 }
539 else
540 {
541 x = snprintf (scratch, sizeof scratch,
542 "%s://%s%s%s%s%s%s%s\r\n",
543 protocol,
544 user, at[0]?":":"", pass, at,
545 host,
546 sourceuri,
547 args?args:"");
548 }
549 snprintf (httpclient->refbuf->data, BUFSIZE,
550 "HTTP/1.0 200 OK\r\n"
551 "Content-Length: %d\r\n"
552 "%s\r\n"
553 "Content-Type: audio/x-mpegurl\r\n\r\n%s",
554 x, client_keepalive_header (httpclient), scratch);
555 httpclient->refbuf->len = strlen (httpclient->refbuf->data);
556 free (sourceuri);
557 free (fullpath);
558 return fserve_setup_client_fb (httpclient, NULL);
559 }
560 if (xspf_requested && xspf_file_available == 0)
561 {
562 xmlDocPtr doc;
563 char *reference = strdup (path);
564 char *eol = strrchr (reference, '.');
565 if (eol)
566 *eol = '\0';
567 doc = stats_get_xml (0, reference);
568 free (reference);
569 free (fullpath);
570 return admin_send_response (doc, httpclient, XSLT, "xspf.xsl");
571 }
572
573 /* on demand file serving check */
574 config = config_get_config();
575 if (config->fileserve == 0)
576 {
577 config_release_config();
578 DEBUG1 ("on demand file \"%s\" refused", fullpath);
579 free (fullpath);
580 return client_send_404 (httpclient, "The file you requested could not be found");
581 }
582 config_release_config();
583
584 if (S_ISREG (file_buf.st_mode) == 0)
585 {
586 WARN1 ("found requested file but there is no handler for it: %s", fullpath);
587 free (fullpath);
588 return client_send_404 (httpclient, "The file you requested could not be found");
589 }
590
591 free (fullpath);
592 finfo.flags = 0;
593 finfo.mount = (char *)path;
594 finfo.fallback = NULL;
595 finfo.limit = 0;
596 finfo.type = FORMAT_TYPE_UNDEFINED;
597 snprintf (fsize, 20, "%" PRId64, (int64_t)file_buf.st_size);
598 httpp_setvar (httpclient->parser, "__FILESIZE", fsize);
599 stats_event_inc (NULL, "file_connections");
600
601 return fserve_setup_client_fb (httpclient, &finfo);
602 }
603
604
605
file_release(client_t * client)606 static void file_release (client_t *client)
607 {
608 fh_node *fh = client->shared_data;
609 int ret = -1;
610
611 if ((fh->finfo.flags & FS_FALLBACK) && (client->flags & CLIENT_AUTHENTICATED))
612 {
613 // reduce from global count
614 global_lock();
615 global.listeners--;
616 global_unlock();
617 }
618
619 client_set_queue (client, NULL);
620
621 if (client->flags & CLIENT_AUTHENTICATED && client->parser->req_type == httpp_req_get)
622 {
623 const char *m = NULL;
624
625 if (fh->finfo.flags & FS_FALLBACK)
626 m = httpp_getvar (client->parser, HTTPP_VAR_URI);
627 else if (client->mount)
628 m = client->mount;
629 else
630 m = fh->finfo.mount;
631 if (m)
632 {
633 ice_config_t *config;
634 char *mount = strdup (m);
635 mount_proxy *mountinfo;
636
637 remove_from_fh (fh, client);
638 client->shared_data = NULL;
639 config = config_get_config ();
640 mountinfo = config_find_mount (config, mount);
641 if (mountinfo && mountinfo->access_log.name)
642 logging_access_id (&mountinfo->access_log, client);
643 ret = auth_release_listener (client, mount, mountinfo);
644 config_release_config();
645 free (mount);
646 }
647 else
648 remove_from_fh (fh, client);
649 }
650 else
651 remove_from_fh (fh, client);
652 if (ret < 0)
653 {
654 client->shared_data = NULL;
655 client->flags &= ~CLIENT_AUTHENTICATED;
656 client_destroy (client);
657 }
658 global_reduce_bitrate_sampling (global.out_bitrate);
659 }
660
661
662 struct _client_functions buffer_content_ops =
663 {
664 prefile_send,
665 file_release
666 };
667
668
669 struct _client_functions file_content_ops =
670 {
671 file_send,
672 file_release
673 };
674
675
fserve_move_listener(client_t * client)676 static int fserve_move_listener (client_t *client)
677 {
678 fh_node *fh = client->shared_data;
679 int ret = 0;
680 fbinfo f;
681
682 memset (&f, 0, sizeof (f));
683 if (client->refbuf && client->pos < client->refbuf->len)
684 client->flags |= CLIENT_HAS_INTRO_CONTENT; // treat it as a partial write needing completion
685 else
686 client_set_queue (client, NULL);
687 f.flags = fh->finfo.flags & (~FS_DELETE);
688 f.limit = fh->finfo.limit;
689 f.mount = fh->finfo.fallback;
690 f.type = fh->finfo.type;
691 if (move_listener (client, &f) < 0)
692 {
693 WARN1 ("moved failed, terminating listener on %s", fh->finfo.mount);
694 ret = -1;
695 }
696 else
697 {
698 DEBUG3 ("moved %s from %s (%d)", client->connection.ip, fh->finfo.mount, fh->finfo.flags);
699 ret = 0;
700 remove_from_fh (fh, client);
701 }
702 return ret;
703 }
704
705
fserve_change_worker(client_t * client)706 static int fserve_change_worker (client_t *client)
707 {
708 worker_t *this_worker = client->worker, *worker;
709 int ret = 0;
710
711 if (this_worker->move_allocations == 0)
712 return 0;
713 thread_rwlock_rlock (&workers_lock);
714 worker = worker_selected ();
715 if (worker && worker != client->worker)
716 {
717 long diff = this_worker->move_allocations < 1000000 ? this_worker->count - worker->count : 1000;
718 if (diff > 10)
719 {
720 this_worker->move_allocations--;
721 ret = client_change_worker (client, worker);
722 if (ret)
723 DEBUG2 ("moving listener from %p to %p", this_worker, worker);
724 }
725 }
726 thread_rwlock_unlock (&workers_lock);
727 return ret;
728 }
729
730
731 struct _client_functions throttled_file_content_ops;
732
prefile_send(client_t * client)733 static int prefile_send (client_t *client)
734 {
735 int loop = 8, bytes, written = 0;
736 worker_t *worker = client->worker;
737
738 while (loop)
739 {
740 refbuf_t *refbuf = client->refbuf;
741 fh_node *fh = client->shared_data;
742 loop--;
743 if (fserve_running == 0 || client->connection.error)
744 return -1;
745 if (refbuf == NULL || client->pos == refbuf->len)
746 {
747 if (fh->finfo.fallback && (client->flags & CLIENT_AUTHENTICATED))
748 return fserve_move_listener (client);
749
750 if (refbuf == NULL || refbuf->next == NULL)
751 {
752 if ((client->flags & CLIENT_AUTHENTICATED) == 0)
753 return -1;
754 if (file_in_use (fh->f)) // is there a file to read from
755 {
756 if (fh->format->detach_queue_block)
757 fh->format->detach_queue_block (NULL, client->refbuf);
758 refbuf_release (client->refbuf);
759 client->refbuf = NULL;
760 client->pos = 0;
761 client->intro_offset = fh->frame_start_pos;
762 if (fh->finfo.limit)
763 {
764 client->ops = &throttled_file_content_ops;
765 rate_add (fh->out_bitrate, 0, worker->time_ms);
766 return 0;
767 }
768 client->ops = &file_content_ops;
769 return client->ops->process (client);
770 }
771 if (client->respcode)
772 return -1;
773 return client_send_404 (client, NULL);
774 }
775 else
776 {
777 refbuf_t *to_go = client->refbuf;
778 refbuf = client->refbuf = to_go->next;
779 to_go->next = NULL;
780 if (fh->format && fh->format->detach_queue_block)
781 fh->format->detach_queue_block (NULL, client->refbuf);
782 refbuf_release (to_go);
783 }
784 client->pos = 0;
785 }
786 if (refbuf->flags & WRITE_BLOCK_GENERIC)
787 bytes = format_generic_write_to_client (client);
788 else
789 bytes = client->check_buffer (client);
790 if (bytes < 0)
791 {
792 client->schedule_ms = worker->time_ms + (written ? 150 : 300);
793 return 0;
794 }
795 written += bytes;
796 global_add_bitrates (global.out_bitrate, bytes, worker->time_ms);
797 if (written > 30000)
798 break;
799 }
800 return 0;
801 }
802
803
804 /* fast send routine */
file_send(client_t * client)805 static int file_send (client_t *client)
806 {
807 int loop = 6, bytes, written = 0;
808 fh_node *fh = client->shared_data;
809 worker_t *worker = client->worker;
810 time_t now;
811
812 #if 0
813 if (fserve_change_worker (client)) // allow for balancing
814 return 1;
815 #endif
816 client->schedule_ms = worker->time_ms;
817 now = worker->current_time.tv_sec;
818 /* slowdown if max bandwidth is exceeded, but allow for short-lived connections to avoid
819 * this, eg admin requests */
820 if (throttle_sends > 1 && now - client->connection.con_time > 1)
821 {
822 client->schedule_ms += 300;
823 loop = 1;
824 }
825 while (loop && written < 48000)
826 {
827 loop--;
828 if (fserve_running == 0 || client->connection.error)
829 return -1;
830 if (format_file_read (client, fh->format, fh->f) < 0)
831 return -1;
832 bytes = client->check_buffer (client);
833 if (bytes < 0)
834 {
835 client->schedule_ms += (written ? 80 : 150);
836 return 0;
837 }
838 written += bytes;
839 }
840 client->schedule_ms += 4;
841 return 0;
842 }
843
844
845
846 /* send routine for files sent at a target bitrate, eg fallback files. */
throttled_file_send(client_t * client)847 static int throttled_file_send (client_t *client)
848 {
849 int bytes;
850 fh_node *fh = client->shared_data;
851 time_t now;
852 worker_t *worker = client->worker;
853 unsigned long secs;
854 unsigned int rate = 0;
855 unsigned int limit = fh->finfo.limit;
856
857 if (fserve_running == 0 || client->connection.error)
858 return -1;
859 now = worker->current_time.tv_sec;
860 secs = now - client->timer_start;
861 client->schedule_ms = worker->time_ms;
862 if (fh->finfo.fallback)
863 return fserve_move_listener (client);
864
865 if (fserve_change_worker (client)) // allow for balancing
866 return 1;
867
868 if (client->flags & CLIENT_WANTS_FLV) /* increase limit for flv clients as wrapping takes more space */
869 limit = (unsigned long)(limit * 1.01);
870 rate = secs ? (client->counter+1400)/secs : limit * 2;
871 // DEBUG3 ("counter %lld, duration %ld, limit %u", client->counter, secs, rate);
872 if (rate > limit)
873 {
874 if (limit >= 1400)
875 client->schedule_ms += 1000/(limit/1400);
876 else
877 client->schedule_ms += 50; // should not happen but guard against it
878 rate_add (fh->out_bitrate, 0, worker->time_ms);
879 global_add_bitrates (global.out_bitrate, 0, worker->time_ms);
880 if (client->counter > 8192)
881 return 0; // allow an initial amount without throttling
882 }
883 switch (format_file_read (client, fh->format, fh->f))
884 {
885 case -1: // DEBUG0 ("loop of file triggered");
886 client->intro_offset = 0;
887 client->schedule_ms += client->throttle ? client->throttle : 150;
888 return 0;
889 case -2: // DEBUG0 ("major failure on read, better leave");
890 return -1;
891 default: //DEBUG1 ("reading from offset %ld", client->intro_offset);
892 break;
893 }
894 bytes = client->check_buffer (client);
895 if (bytes < 0)
896 bytes = 0;
897 //DEBUG3 ("bytes %d, counter %ld, %ld", bytes, client->counter, client->worker->time_ms - (client->timer_start*1000));
898 rate_add (fh->out_bitrate, bytes, worker->time_ms);
899 global_add_bitrates (global.out_bitrate, bytes, worker->time_ms);
900 if (limit > 2800)
901 client->schedule_ms += (1000/(limit/1400*2));
902 else
903 client->schedule_ms += 50;
904
905 /* progessive slowdown if max bandwidth is exceeded. */
906 if (throttle_sends > 1)
907 client->schedule_ms += 300;
908 return 0;
909 }
910
911
912 struct _client_functions throttled_file_content_ops =
913 {
914 throttled_file_send,
915 file_release
916 };
917
918
fserve_setup_client_fb(client_t * client,fbinfo * finfo)919 int fserve_setup_client_fb (client_t *client, fbinfo *finfo)
920 {
921 fh_node *fh = &no_file;
922 int ret = 0;
923
924 if (finfo)
925 {
926 mount_proxy *minfo;
927 if (finfo->flags & FS_FALLBACK && finfo->limit == 0)
928 return -1;
929 avl_tree_wlock (fh_cache);
930 fh = find_fh (finfo);
931 minfo = config_find_mount (config_get_config(), finfo->mount);
932 if (fh)
933 {
934 thread_mutex_lock (&fh->lock);
935 avl_tree_unlock (fh_cache);
936 client->shared_data = NULL;
937 if (minfo)
938 {
939 if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners)
940 {
941 thread_mutex_unlock (&fh->lock);
942 config_release_config();
943 return client_send_403redirect (client, finfo->mount, "max listeners reached");
944 }
945 if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0)
946 {
947 thread_mutex_unlock (&fh->lock);
948 config_release_config();
949 return client_send_403 (client, "Account already in use");
950 }
951 }
952 config_release_config();
953 }
954 else
955 {
956 if (minfo && minfo->max_listeners == 0)
957 {
958 avl_tree_unlock (fh_cache);
959 config_release_config();
960 client->shared_data = NULL;
961 return client_send_403redirect (client, finfo->mount, "max listeners reached");
962 }
963 config_release_config();
964 fh = open_fh (finfo);
965 if (fh == NULL)
966 return client_send_404 (client, NULL);
967 if (fh->finfo.limit)
968 DEBUG2 ("request for throttled file %s (bitrate %d)", fh->finfo.mount, fh->finfo.limit*8);
969 }
970 if (fh->finfo.limit)
971 {
972 client->timer_start = client->worker->current_time.tv_sec;
973 if (client->connection.sent_bytes == 0)
974 client->timer_start -= 2;
975 client->counter = 0;
976 global_reduce_bitrate_sampling (global.out_bitrate);
977 }
978 }
979 else
980 {
981 if (client->mount && (client->flags & CLIENT_AUTHENTICATED) && (client->respcode >= 300 || client->respcode < 200))
982 {
983 fh = calloc (1, sizeof (no_file));
984 fh->finfo.mount = strdup (client->mount);
985 fh->finfo.flags |= FS_DELETE;
986 fh->refcount = 1;
987 fh->f = SOCK_ERROR;
988 thread_mutex_create (&fh->lock);
989 }
990 thread_mutex_lock (&fh->lock);
991 }
992 client->mount = fh->finfo.mount;
993 if (fh->finfo.type == FORMAT_TYPE_UNDEFINED)
994 {
995 if (client->respcode == 0)
996 {
997 client->refbuf->len = 0;
998 ret = format_general_headers (fh->format, client);
999 }
1000 }
1001 else
1002 {
1003 if (fh->format->create_client_data && client->format_data == NULL)
1004 ret = fh->format->create_client_data (fh->format, client);
1005 if (fh->format->write_buf_to_client)
1006 client->check_buffer = fh->format->write_buf_to_client;
1007 }
1008 if (ret < 0)
1009 {
1010 thread_mutex_unlock (&fh->lock);
1011 client->mount = NULL;
1012 return client_send_416 (client);
1013 }
1014 fh_add_client (fh, client);
1015 thread_mutex_unlock (&fh->lock);
1016 client->shared_data = fh;
1017
1018 if (client->check_buffer == NULL)
1019 client->check_buffer = format_generic_write_to_client;
1020
1021 client->ops = &buffer_content_ops;
1022 client->flags &= ~CLIENT_HAS_INTRO_CONTENT;
1023 client->flags |= CLIENT_IN_FSERVE;
1024 if (client->flags & CLIENT_ACTIVE)
1025 {
1026 client->schedule_ms = client->worker->time_ms;
1027 if (finfo && finfo->flags & FS_FALLBACK)
1028 return 0; // prevent a recursive loop
1029 return client->ops->process (client);
1030 }
1031 else
1032 {
1033 worker_t *worker = client->worker;
1034 ret = (fh->finfo.limit) ? 0 : -1;
1035 client->flags |= CLIENT_ACTIVE;
1036 worker_wakeup (worker); /* worker may of already processed client but make sure */
1037 }
1038 return ret;
1039 }
1040
1041
fserve_setup_client(client_t * client)1042 int fserve_setup_client (client_t *client)
1043 {
1044 client->check_buffer = format_generic_write_to_client;
1045 return fserve_setup_client_fb (client, NULL);
1046 }
1047
1048
fserve_set_override(const char * mount,const char * dest,format_type_t type)1049 int fserve_set_override (const char *mount, const char *dest, format_type_t type)
1050 {
1051 fh_node fh, *result;
1052
1053 fh.finfo.flags = FS_FALLBACK;
1054 fh.finfo.mount = (char *)mount;
1055 fh.finfo.fallback = NULL;
1056 fh.finfo.type = type;
1057
1058 avl_tree_wlock (fh_cache);
1059 result = find_fh (&fh.finfo);
1060 if (result)
1061 {
1062 thread_mutex_lock (&result->lock);
1063
1064 if (result->refcount > 0)
1065 {
1066 fh_node *copy = calloc (1, sizeof (*copy));
1067 avl_delete (fh_cache, result, NULL);
1068 copy->finfo = result->finfo;
1069 copy->finfo.mount = strdup (copy->finfo.mount);
1070 copy->prev_count = -1; // trigger stats update
1071 copy->expire = (time_t)-1;
1072 copy->stats = result->stats;
1073 copy->format = result->format;
1074 copy->f = result->f;
1075 thread_mutex_create (©->lock);
1076 copy->out_bitrate = rate_setup (10000, 1000);
1077 copy->clients = avl_tree_new (client_compare, NULL);
1078 avl_insert (fh_cache, copy);
1079
1080 result->finfo.flags |= FS_DELETE;
1081 result->finfo.flags &= ~FS_FALLBACK;
1082 result->format = NULL;
1083 result->stats = 0;
1084 result->f = SOCK_ERROR;
1085 result->finfo.fallback = strdup (dest);
1086 result->finfo.type = type;
1087 }
1088 avl_tree_unlock (fh_cache);
1089 thread_mutex_unlock (&result->lock);
1090 INFO2 ("move clients from %s to %s", mount, dest);
1091 return 1;
1092 }
1093 avl_tree_unlock (fh_cache);
1094 return 0;
1095 }
1096
_delete_mapping(void * mapping)1097 static int _delete_mapping(void *mapping) {
1098 mime_type *map = mapping;
1099 free(map->ext);
1100 free(map->type);
1101 free(map);
1102
1103 return 1;
1104 }
1105
_compare_mappings(void * arg,void * a,void * b)1106 static int _compare_mappings(void *arg, void *a, void *b)
1107 {
1108 return strcmp(
1109 ((mime_type *)a)->ext,
1110 ((mime_type *)b)->ext);
1111 }
1112
1113
1114 // write filename extension for matching mime type.
1115 // lookup matching mime type and write extension into buffer space provided
fserve_write_mime_ext(const char * mimetype,char * buf,unsigned int len)1116 void fserve_write_mime_ext (const char *mimetype, char *buf, unsigned int len)
1117 {
1118 avl_node *node;
1119 int semi;
1120
1121 if (mimetype == NULL || buf == NULL || len > 2000) return;
1122 semi = strcspn (mimetype, "; ");
1123 if (semi == 0) return;
1124 if (mimetype [semi])
1125 {
1126 char *mt = alloca (++semi);
1127 snprintf (mt, semi, "%s", mimetype);
1128 mimetype = (const char *)mt;
1129 }
1130 thread_spin_lock (&pending_lock);
1131 node = avl_get_first (mimetypes);
1132 while (node)
1133 {
1134 mime_type *mime = (mime_type *)node->key;
1135 if (mime && strcmp (mime->type, mimetype) == 0)
1136 {
1137 snprintf (buf, len, "%s", mime->ext);
1138 break;
1139 }
1140 node = avl_get_next (node);
1141 }
1142 thread_spin_unlock (&pending_lock);
1143 }
1144
1145
fserve_recheck_mime_types(ice_config_t * config)1146 void fserve_recheck_mime_types (ice_config_t *config)
1147 {
1148 mime_type *mapping;
1149 int i;
1150 avl_tree *old_mimetypes = NULL, *new_mimetypes = avl_tree_new(_compare_mappings, NULL);
1151
1152 mime_type defaults[] = {
1153 { "m3u", "audio/x-mpegurl" },
1154 { "pls", "audio/x-scpls" },
1155 { "xspf", "application/xspf+xml" },
1156 { "ogg", "application/ogg" },
1157 { "xml", "text/xml" },
1158 { "mp3", "audio/mpeg" },
1159 { "aac", "audio/aac" },
1160 { "aacp", "audio/aacp" },
1161 { "css", "text/css" },
1162 { "txt", "text/plain" },
1163 { "html", "text/html" },
1164 { "jpg", "image/jpg" },
1165 { "png", "image/png" },
1166 { "gif", "image/gif" },
1167 { NULL, NULL }
1168 };
1169
1170 for (i=0; defaults[i].ext; i++)
1171 {
1172 mapping = malloc (sizeof(mime_type));
1173 mapping->ext = strdup (defaults [i].ext);
1174 mapping->type = strdup (defaults [i].type);
1175 if (avl_insert (new_mimetypes, mapping) != 0)
1176 _delete_mapping (mapping);
1177 }
1178 do
1179 {
1180 char *type, *ext, *cur;
1181 FILE *mimefile = NULL;
1182 char line[4096];
1183
1184 if (config->mimetypes_fn == NULL)
1185 {
1186 INFO0 ("no mime types file defined, using defaults");
1187 break;
1188 }
1189 mimefile = fopen (config->mimetypes_fn, "r");
1190 if (mimefile == NULL)
1191 {
1192 WARN1 ("Cannot open mime types file %s, using defaults", config->mimetypes_fn);
1193 break;
1194 }
1195 while (fgets(line, sizeof line, mimefile))
1196 {
1197 line[4095] = 0;
1198
1199 if(*line == 0 || *line == '#')
1200 continue;
1201
1202 type = line;
1203 cur = line;
1204
1205 while(*cur != ' ' && *cur != '\t' && *cur)
1206 cur++;
1207 if(*cur == 0)
1208 continue;
1209
1210 *cur++ = 0;
1211
1212 while(1)
1213 {
1214 while(*cur == ' ' || *cur == '\t')
1215 cur++;
1216 if(*cur == 0)
1217 break;
1218
1219 ext = cur;
1220 while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
1221 cur++;
1222 *cur++ = 0;
1223 if(*ext)
1224 {
1225 void *tmp;
1226 /* Add a new extension->type mapping */
1227 mapping = malloc(sizeof(mime_type));
1228 mapping->ext = strdup(ext);
1229 mapping->type = strdup(type);
1230 if (!avl_get_by_key (new_mimetypes, mapping, &tmp))
1231 avl_delete (new_mimetypes, mapping, _delete_mapping);
1232 if (avl_insert (new_mimetypes, mapping) != 0)
1233 _delete_mapping (mapping);
1234 }
1235 }
1236 }
1237 fclose(mimefile);
1238 } while (0);
1239
1240 thread_spin_lock (&pending_lock);
1241 old_mimetypes = mimetypes;
1242 mimetypes = new_mimetypes;
1243 thread_spin_unlock (&pending_lock);
1244 if (old_mimetypes)
1245 avl_tree_free (old_mimetypes, _delete_mapping);
1246 }
1247
1248
fserve_kill_client(client_t * client,const char * mount,int response)1249 int fserve_kill_client (client_t *client, const char *mount, int response)
1250 {
1251 int loop = 2, id;
1252 fbinfo finfo;
1253 xmlDocPtr doc;
1254 xmlNodePtr node;
1255 const char *idtext, *v = "0";
1256 char buf[50];
1257
1258 finfo.flags = 0;
1259 finfo.mount = (char*)mount;
1260 finfo.limit = 0;
1261 finfo.fallback = NULL;
1262
1263 idtext = httpp_get_query_param (client->parser, "id");
1264 if (idtext == NULL)
1265 return client_send_400 (client, "missing parameter id");
1266
1267 id = atoi(idtext);
1268
1269 doc = xmlNewDoc(XMLSTR("1.0"));
1270 node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL);
1271 xmlDocSetRootElement(doc, node);
1272 snprintf (buf, sizeof(buf), "Client %d not found", id);
1273
1274 avl_tree_rlock (fh_cache);
1275 while (1)
1276 {
1277 avl_node *node;
1278 fh_node *fh = find_fh (&finfo);
1279 if (fh)
1280 {
1281 thread_mutex_lock (&fh->lock);
1282 avl_tree_unlock (fh_cache);
1283 node = avl_get_first (fh->clients);
1284 while (node)
1285 {
1286 client_t *listener = (client_t *)node->key;
1287 if (listener->connection.id == id)
1288 {
1289 listener->connection.error = 1;
1290 snprintf (buf, sizeof(buf), "Client %d removed", id);
1291 v = "1";
1292 loop = 0;
1293 break;
1294 }
1295 node = avl_get_next (node);
1296 }
1297 thread_mutex_unlock (&fh->lock);
1298 avl_tree_rlock (fh_cache);
1299 }
1300 if (loop == 0) break;
1301 loop--;
1302 if (loop == 1) finfo.flags = FS_FALLBACK;
1303 }
1304 avl_tree_unlock (fh_cache);
1305 xmlNewChild (node, NULL, XMLSTR("message"), XMLSTR(buf));
1306 xmlNewChild (node, NULL, XMLSTR("return"), XMLSTR(v));
1307 return admin_send_response (doc, client, response, "response.xsl");
1308 }
1309
1310
fserve_list_clients_xml(xmlNodePtr parent,fbinfo * finfo)1311 int fserve_list_clients_xml (xmlNodePtr parent, fbinfo *finfo)
1312 {
1313 int ret = 0;
1314 fh_node *fh;
1315 avl_node *anode;
1316
1317 avl_tree_rlock (fh_cache);
1318 fh = find_fh (finfo);
1319 if (fh == NULL)
1320 {
1321 avl_tree_unlock (fh_cache);
1322 return 0;
1323 }
1324 thread_mutex_lock (&fh->lock);
1325 avl_tree_unlock (fh_cache);
1326
1327 anode = avl_get_first (fh->clients);
1328 while (anode)
1329 {
1330 client_t *listener = (client_t *)anode->key;
1331
1332 stats_listener_to_xml (listener, parent);
1333 ret++;
1334 anode = avl_get_next (anode);
1335 }
1336 thread_mutex_unlock (&fh->lock);
1337 return ret;
1338 }
1339
1340
fserve_list_clients(client_t * client,const char * mount,int response,int show_listeners)1341 int fserve_list_clients (client_t *client, const char *mount, int response, int show_listeners)
1342 {
1343 int ret;
1344 fbinfo finfo;
1345 xmlDocPtr doc;
1346 xmlNodePtr node, srcnode;
1347
1348 finfo.flags = 0;
1349 finfo.mount = (char*)mount;
1350 finfo.limit = 0;
1351 finfo.fallback = NULL;
1352
1353 doc = xmlNewDoc(XMLSTR("1.0"));
1354 node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL);
1355 xmlDocSetRootElement(doc, node);
1356 srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL);
1357 xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(mount));
1358
1359 ret = fserve_list_clients_xml (srcnode, &finfo);
1360 if (ret == 0 && finfo.flags&FS_FALLBACK)
1361 {
1362 finfo.flags = 0; // retry
1363 ret = fserve_list_clients_xml (srcnode, &finfo);
1364 }
1365 if (ret)
1366 {
1367 char buf [20];
1368 snprintf (buf, sizeof(buf), "%d", ret);
1369 xmlNewChild (srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf));
1370 return admin_send_response (doc, client, response, "listclients.xsl");
1371 }
1372 xmlFreeDoc (doc);
1373 return client_send_400 (client, "mount does not exist");
1374 }
1375
1376
fserve_query_count(fbinfo * finfo)1377 int fserve_query_count (fbinfo *finfo)
1378 {
1379 int ret = -1;
1380 fh_node *fh;
1381
1382 if (finfo->flags & FS_FALLBACK && finfo->limit)
1383 {
1384 avl_tree_wlock (fh_cache);
1385 fh = open_fh (finfo);
1386 if (fh)
1387 {
1388 ret = fh->refcount;
1389 thread_mutex_unlock (&fh->lock);
1390 }
1391 }
1392 else
1393 {
1394 avl_tree_rlock (fh_cache);
1395 fh = find_fh (finfo);
1396 if (fh)
1397 {
1398 thread_mutex_lock (&fh->lock);
1399 ret = fh->refcount;
1400 thread_mutex_unlock (&fh->lock);
1401 }
1402 avl_tree_unlock (fh_cache);
1403 }
1404 return ret;
1405 }
1406
1407
file_in_use(icefile_handle f)1408 int file_in_use (icefile_handle f)
1409 {
1410 return f != -1;
1411 }
1412
1413
file_close(icefile_handle * f)1414 void file_close (icefile_handle *f)
1415 {
1416 if (*f != -1)
1417 close (*f);
1418 *f = -1;
1419 }
1420
1421
file_open(icefile_handle * f,const char * fn)1422 int file_open (icefile_handle *f, const char *fn)
1423 {
1424 *f = open (fn, O_RDONLY|O_CLOEXEC|O_BINARY);
1425 return (*f) < 0 ? -1 : 0;
1426 }
1427
1428
1429 #ifndef HAVE_PREAD
pread(icefile_handle f,void * data,size_t count,off_t offset)1430 ssize_t pread (icefile_handle f, void *data, size_t count, off_t offset)
1431 {
1432 ssize_t bytes = -1;
1433
1434 // we do not want another thread to modifiy handle between seek and read
1435 // win32 may be able to use the overlapped io struct in ReadFile
1436 thread_mutex_lock (&seekread_lock);
1437 if (lseek (f, offset, SEEK_SET) != (off_t)-1)
1438 bytes = read (f, data, count);
1439 thread_mutex_unlock (&seekread_lock);
1440 return bytes;
1441 }
1442 #endif
1443
1444
fserve_scan(time_t now)1445 void fserve_scan (time_t now)
1446 {
1447 avl_node *node;
1448
1449 global_lock();
1450 if (global.running != ICE_RUNNING)
1451 now = (time_t)0;
1452 global_unlock();
1453
1454 avl_tree_wlock (fh_cache);
1455 node = avl_get_first (fh_cache);
1456 while (node)
1457 {
1458 fh_node *fh = node->key;
1459 node = avl_get_next (node);
1460
1461 thread_mutex_lock (&fh->lock);
1462
1463 if (now == (time_t)0)
1464 {
1465 fh->expire = 0;
1466 thread_mutex_unlock (&fh->lock);
1467 continue;
1468 }
1469
1470 if (fh->finfo.limit)
1471 {
1472 fbinfo *finfo = &fh->finfo;
1473 if (fh->stats == 0)
1474 {
1475 int len = strlen (finfo->mount) + 10;
1476 char *str = alloca (len);
1477 char buf[30];
1478 snprintf (str, len, "%s-%s", (finfo->flags & FS_FALLBACK) ? "fallback" : "file", finfo->mount);
1479 fh->stats = stats_handle (str);
1480 stats_set_flags (fh->stats, "fallback", "file", STATS_COUNTERS|STATS_HIDDEN);
1481 stats_set_flags (fh->stats, "outgoing_kbitrate", "0", STATS_COUNTERS|STATS_HIDDEN);
1482 snprintf (buf, sizeof (buf), "%d", fh->refcount);
1483 stats_set_flags (fh->stats, "listeners", buf, STATS_GENERAL|STATS_HIDDEN);
1484 snprintf (buf, sizeof (buf), "%d", fh->peak);
1485 stats_set_flags (fh->stats, "listener_peak", buf, STATS_GENERAL|STATS_HIDDEN);
1486 fh->prev_count = fh->refcount;
1487 }
1488 else
1489 {
1490 stats_lock (fh->stats, NULL);
1491 if (fh->prev_count != fh->refcount)
1492 {
1493 fh->prev_count = fh->refcount;
1494 stats_set_args (fh->stats, "listeners", "%ld", fh->refcount);
1495 stats_set_args (fh->stats, "listener_peak", "%ld", fh->peak);
1496 }
1497 }
1498 if (fh->stats_update <= now)
1499 {
1500 fh->stats_update = now + 5;
1501 stats_set_args (fh->stats, "outgoing_kbitrate", "%ld",
1502 (long)((8 * rate_avg (fh->out_bitrate))/1024));
1503 }
1504 stats_release (fh->stats);
1505 }
1506
1507 if (fh->refcount == 0 && fh->expire >= 0 && now >= fh->expire)
1508 {
1509 DEBUG1 ("timeout of %s", fh->finfo.mount);
1510 if (fh->stats)
1511 {
1512 stats_lock (fh->stats, NULL);
1513 stats_set (fh->stats, NULL, NULL);
1514 }
1515 remove_fh_from_cache (fh);
1516 thread_mutex_unlock (&fh->lock);
1517 _delete_fh (fh);
1518 continue;
1519 }
1520 thread_mutex_unlock (&fh->lock);
1521 }
1522 avl_tree_unlock (fh_cache);
1523 }
1524
1525
1526
fserve_contains(const char * name)1527 int fserve_contains (const char *name)
1528 {
1529 int ret = -1;
1530 fbinfo finfo;
1531
1532 memset (&finfo, 0, sizeof (finfo));
1533 if (strncmp (name, "fallback-/", 10) == 0)
1534 {
1535 finfo.mount = (char*)name+9;
1536 finfo.flags = FS_FALLBACK;
1537 }
1538 else if (strncmp (name, "file-/", 6) == 0)
1539 finfo.mount = (char*)name;
1540 DEBUG1 ("looking for %s", name);
1541 avl_tree_rlock (fh_cache);
1542 if (find_fh (&finfo))
1543 ret = 0;
1544 avl_tree_unlock (fh_cache);
1545 return ret;
1546 }
1547
1548