1 /*
2 * TVheadend
3 * Copyright (C) 2007 - 2010 Andreas Öman
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <pthread.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <syslog.h>
29 #include <limits.h>
30 #include <time.h>
31 #include <locale.h>
32
33 #include <pwd.h>
34 #include <grp.h>
35
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38
39 #include "tvheadend.h"
40 #include "api.h"
41 #include "tcp.h"
42 #include "access.h"
43 #include "http.h"
44 #include "upnp.h"
45 #include "webui/webui.h"
46 #include "epggrab.h"
47 #include "spawn.h"
48 #include "subscriptions.h"
49 #include "service_mapper.h"
50 #include "descrambler.h"
51 #include "profile.h"
52 #include "dvr/dvr.h"
53 #include "htsp_server.h"
54 #include "satip/server.h"
55 #include "avahi.h"
56 #include "bonjour.h"
57 #include "input.h"
58 #include "service.h"
59 #include "trap.h"
60 #include "settings.h"
61 #include "config.h"
62 #include "notify.h"
63 #include "idnode.h"
64 #include "imagecache.h"
65 #include "timeshift.h"
66 #include "fsmonitor.h"
67 #include "lang_codes.h"
68 #include "esfilter.h"
69 #include "intlconv.h"
70 #include "dbus.h"
71 #include "libav.h"
72 #include "bouquet.h"
73 #include "tvhtime.h"
74 #include "packet.h"
75 #include "streaming.h"
76 #include "memoryinfo.h"
77
78 #ifdef PLATFORM_LINUX
79 #include <sys/prctl.h>
80 #endif
81 #include <sys/resource.h>
82 #include <openssl/ssl.h>
83 #include <openssl/conf.h>
84 #include <openssl/err.h>
85 #include <openssl/rand.h>
86 #include <openssl/engine.h>
87
88 pthread_t main_tid;
89
90 int64_t __mdispatch_clock;
91 time_t __gdispatch_clock;
92
93 /* Command line option struct */
94 typedef struct {
95 const char sopt;
96 const char *lopt;
97 const char *desc;
98 enum {
99 OPT_STR,
100 OPT_INT,
101 OPT_BOOL,
102 OPT_STR_LIST,
103 } type;
104 void *param;
105 } cmdline_opt_t;
106
cmdline_opt_find(cmdline_opt_t * opts,int num,const char * arg)107 static cmdline_opt_t* cmdline_opt_find
108 ( cmdline_opt_t *opts, int num, const char *arg )
109 {
110 int i;
111 int isshort = 0;
112
113 if (strlen(arg) < 2 || *arg != '-')
114 return NULL;
115 arg++;
116
117 if (strlen(arg) == 1)
118 isshort = 1;
119 else if (*arg == '-')
120 arg++;
121 else
122 return NULL;
123
124 for (i = 0; i < num; i++) {
125 if (!opts[i].lopt) continue;
126 if (isshort && opts[i].sopt == *arg)
127 return &opts[i];
128 if (!isshort && !strcmp(opts[i].lopt, arg))
129 return &opts[i];
130 }
131
132 return NULL;
133 }
134
135 /*
136 * Globals
137 */
138 int tvheadend_running;
139 int tvheadend_webui_port;
140 int tvheadend_webui_debug;
141 int tvheadend_htsp_port;
142 int tvheadend_htsp_port_extra;
143 const char *tvheadend_tsdebug;
144 const char *tvheadend_cwd0;
145 const char *tvheadend_cwd;
146 const char *tvheadend_webroot;
147 const tvh_caps_t tvheadend_capabilities[] = {
148 #if ENABLE_CWC || ENABLE_CAPMT || ENABLE_CONSTCW
149 { "caclient", NULL },
150 #endif
151 #if ENABLE_LINUXDVB || ENABLE_SATIP_CLIENT || ENABLE_HDHOMERUN_CLIENT
152 { "tvadapters", NULL },
153 #endif
154 #if ENABLE_SATIP_CLIENT
155 { "satip_client", NULL },
156 #endif
157 #if ENABLE_SATIP_SERVER
158 { "satip_server", NULL },
159 #endif
160 #if ENABLE_IMAGECACHE
161 { "imagecache", (uint32_t*)&imagecache_conf.enabled },
162 #endif
163 #if ENABLE_TIMESHIFT
164 { "timeshift", (uint32_t *)×hift_conf.enabled },
165 #endif
166 #if ENABLE_TRACE
167 { "trace", NULL },
168 #endif
169 #if ENABLE_LIBAV
170 { "libav", NULL },
171 #endif
172 { NULL, NULL }
173 };
174
175 pthread_mutex_t global_lock;
176 pthread_mutex_t tasklet_lock;
177 pthread_mutex_t fork_lock;
178 pthread_mutex_t atomic_lock;
179
180 /*
181 * Locals
182 */
183 static LIST_HEAD(, mtimer) mtimers;
184 static tvh_cond_t mtimer_cond;
185 static int64_t mtimer_periodic;
186 static pthread_t mtimer_tid;
187 static pthread_t mtimer_tick_tid;
188 static LIST_HEAD(, gtimer) gtimers;
189 static pthread_cond_t gtimer_cond;
190 static TAILQ_HEAD(, tasklet) tasklets;
191 static tvh_cond_t tasklet_cond;
192 static pthread_t tasklet_tid;
193 static memoryinfo_t tasklet_memoryinfo = { .my_name = "Tasklet" };
194
195 static void
handle_sigpipe(int x)196 handle_sigpipe(int x)
197 {
198 return;
199 }
200
201 static void
handle_sigill(int x)202 handle_sigill(int x)
203 {
204 /* Note that on some platforms, the SSL library tries */
205 /* to determine the CPU capabilities with possible */
206 /* unknown instructions */
207 tvhwarn(LS_CPU, "Illegal instruction handler (might be OK)");
208 signal(SIGILL, handle_sigill);
209 }
210
211 void
doexit(int x)212 doexit(int x)
213 {
214 if (pthread_self() != main_tid)
215 pthread_kill(main_tid, SIGTERM);
216 pthread_cond_signal(>imer_cond);
217 tvh_cond_signal(&mtimer_cond, 1);
218 atomic_set(&tvheadend_running, 0);
219 signal(x, doexit);
220 }
221
222 static int
get_user_groups(const struct passwd * pw,gid_t * glist,size_t gmax)223 get_user_groups (const struct passwd *pw, gid_t* glist, size_t gmax)
224 {
225 int num = 0;
226 #if !ENABLE_ANDROID
227 struct group *gr;
228 char **mem;
229 glist[num++] = pw->pw_gid;
230 for ( gr = getgrent(); (gr != NULL) && (num < gmax); gr = getgrent() ) {
231 if (gr->gr_gid == pw->pw_gid) continue;
232 for (mem = gr->gr_mem; *mem; mem++) {
233 if(!strcmp(*mem, pw->pw_name)) glist[num++] = gr->gr_gid;
234 }
235 }
236 #endif
237 return num;
238 }
239
240 /**
241 *
242 */
243
244 #define safecmp(a, b) ((a) > (b) ? 1 : ((a) < (b) ? -1 : 0))
245
246 static int
mtimercmp(mtimer_t * a,mtimer_t * b)247 mtimercmp(mtimer_t *a, mtimer_t *b)
248 {
249 return safecmp(a->mti_expire, b->mti_expire);
250 }
251
252 /**
253 *
254 */
255 void
GTIMER_FCN(mtimer_arm_abs)256 GTIMER_FCN(mtimer_arm_abs)
257 (GTIMER_TRACEID_ mtimer_t *mti, mti_callback_t *callback, void *opaque, int64_t when)
258 {
259 lock_assert(&global_lock);
260
261 if (mti->mti_callback != NULL)
262 LIST_REMOVE(mti, mti_link);
263
264 mti->mti_callback = callback;
265 mti->mti_opaque = opaque;
266 mti->mti_expire = when;
267 #if ENABLE_GTIMER_CHECK
268 mti->mti_id = id;
269 mti->mti_fcn = fcn;
270 #endif
271
272 LIST_INSERT_SORTED(&mtimers, mti, mti_link, mtimercmp);
273
274 if (LIST_FIRST(&mtimers) == mti)
275 tvh_cond_signal(&mtimer_cond, 0); // force timer re-check
276 }
277
278 /**
279 *
280 */
281 void
GTIMER_FCN(mtimer_arm_rel)282 GTIMER_FCN(mtimer_arm_rel)
283 (GTIMER_TRACEID_ mtimer_t *gti, mti_callback_t *callback, void *opaque, int64_t delta)
284 {
285 #if ENABLE_GTIMER_CHECK
286 GTIMER_FCN(mtimer_arm_abs)(id, fcn, gti, callback, opaque, mclk() + delta);
287 #else
288 mtimer_arm_abs(gti, callback, opaque, mclk() + delta);
289 #endif
290 }
291
292 /**
293 *
294 */
295 void
mtimer_disarm(mtimer_t * mti)296 mtimer_disarm(mtimer_t *mti)
297 {
298 if(mti->mti_callback) {
299 LIST_REMOVE(mti, mti_link);
300 mti->mti_callback = NULL;
301 }
302 }
303
304 /**
305 *
306 */
307 static int
gtimercmp(gtimer_t * a,gtimer_t * b)308 gtimercmp(gtimer_t *a, gtimer_t *b)
309 {
310 return safecmp(a->gti_expire, b->gti_expire);
311 }
312
313 /**
314 *
315 */
316 void
GTIMER_FCN(gtimer_arm_absn)317 GTIMER_FCN(gtimer_arm_absn)
318 (GTIMER_TRACEID_ gtimer_t *gti, gti_callback_t *callback, void *opaque, time_t when)
319 {
320 lock_assert(&global_lock);
321
322 if (gti->gti_callback != NULL)
323 LIST_REMOVE(gti, gti_link);
324
325 gti->gti_callback = callback;
326 gti->gti_opaque = opaque;
327 gti->gti_expire = when;
328 #if ENABLE_GTIMER_CHECK
329 gti->gti_id = id;
330 gti->gti_fcn = fcn;
331 #endif
332
333 LIST_INSERT_SORTED(>imers, gti, gti_link, gtimercmp);
334
335 if (LIST_FIRST(>imers) == gti)
336 pthread_cond_signal(>imer_cond); // force timer re-check
337 }
338
339 /**
340 *
341 */
342 void
GTIMER_FCN(gtimer_arm_rel)343 GTIMER_FCN(gtimer_arm_rel)
344 (GTIMER_TRACEID_ gtimer_t *gti, gti_callback_t *callback, void *opaque, time_t delta)
345 {
346 #if ENABLE_GTIMER_CHECK
347 GTIMER_FCN(gtimer_arm_absn)(id, fcn, gti, callback, opaque, gclk() + delta);
348 #else
349 gtimer_arm_absn(gti, callback, opaque, gclk() + delta);
350 #endif
351 }
352
353 /**
354 *
355 */
356 void
gtimer_disarm(gtimer_t * gti)357 gtimer_disarm(gtimer_t *gti)
358 {
359 if(gti->gti_callback) {
360 LIST_REMOVE(gti, gti_link);
361 gti->gti_callback = NULL;
362 }
363 }
364
365 /**
366 *
367 */
368 tasklet_t *
tasklet_arm_alloc(tsk_callback_t * callback,void * opaque)369 tasklet_arm_alloc(tsk_callback_t *callback, void *opaque)
370 {
371 tasklet_t *tsk = calloc(1, sizeof(*tsk));
372 if (tsk) {
373 memoryinfo_alloc(&tasklet_memoryinfo, sizeof(*tsk));
374 tsk->tsk_free = free;
375 tasklet_arm(tsk, callback, opaque);
376 }
377 return tsk;
378 }
379
380 /**
381 *
382 */
383 void
tasklet_arm(tasklet_t * tsk,tsk_callback_t * callback,void * opaque)384 tasklet_arm(tasklet_t *tsk, tsk_callback_t *callback, void *opaque)
385 {
386 pthread_mutex_lock(&tasklet_lock);
387
388 if (tsk->tsk_callback != NULL)
389 TAILQ_REMOVE(&tasklets, tsk, tsk_link);
390
391 tsk->tsk_callback = callback;
392 tsk->tsk_opaque = opaque;
393
394 TAILQ_INSERT_TAIL(&tasklets, tsk, tsk_link);
395
396 if (TAILQ_FIRST(&tasklets) == tsk)
397 tvh_cond_signal(&tasklet_cond, 0);
398
399 pthread_mutex_unlock(&tasklet_lock);
400 }
401
402 /**
403 *
404 */
405 void
tasklet_disarm(tasklet_t * tsk)406 tasklet_disarm(tasklet_t *tsk)
407 {
408 pthread_mutex_lock(&tasklet_lock);
409
410 if(tsk->tsk_callback) {
411 TAILQ_REMOVE(&tasklets, tsk, tsk_link);
412 tsk->tsk_callback(tsk->tsk_opaque, 1);
413 tsk->tsk_callback = NULL;
414 if (tsk->tsk_free) tsk->tsk_free(tsk);
415 }
416
417 pthread_mutex_unlock(&tasklet_lock);
418 }
419
420 static void
tasklet_flush()421 tasklet_flush()
422 {
423 tasklet_t *tsk;
424
425 pthread_mutex_lock(&tasklet_lock);
426
427 while ((tsk = TAILQ_FIRST(&tasklets)) != NULL) {
428 TAILQ_REMOVE(&tasklets, tsk, tsk_link);
429 tsk->tsk_callback(tsk->tsk_opaque, 1);
430 tsk->tsk_callback = NULL;
431 if (tsk->tsk_free) {
432 memoryinfo_free(&tasklet_memoryinfo, sizeof(*tsk));
433 tsk->tsk_free(tsk);
434 }
435 }
436
437 pthread_mutex_unlock(&tasklet_lock);
438 }
439
440 /**
441 *
442 */
443 static void *
tasklet_thread(void * aux)444 tasklet_thread ( void *aux )
445 {
446 tasklet_t *tsk;
447 tsk_callback_t *tsk_cb;
448 void *opaque;
449
450 tvhthread_renice(20);
451
452 pthread_mutex_lock(&tasklet_lock);
453 while (tvheadend_is_running()) {
454 tsk = TAILQ_FIRST(&tasklets);
455 if (tsk == NULL) {
456 tvh_cond_wait(&tasklet_cond, &tasklet_lock);
457 continue;
458 }
459 /* the callback might re-initialize tasklet, save everything */
460 TAILQ_REMOVE(&tasklets, tsk, tsk_link);
461 tsk_cb = tsk->tsk_callback;
462 opaque = tsk->tsk_opaque;
463 tsk->tsk_callback = NULL;
464 if (tsk->tsk_free) {
465 memoryinfo_free(&tasklet_memoryinfo, sizeof(*tsk));
466 tsk->tsk_free(tsk);
467 }
468 /* now, the callback can be safely called */
469 if (tsk_cb) {
470 pthread_mutex_unlock(&tasklet_lock);
471 tsk_cb(opaque, 0);
472 pthread_mutex_lock(&tasklet_lock);
473 }
474 }
475 pthread_mutex_unlock(&tasklet_lock);
476
477 return NULL;
478 }
479
480 /**
481 * Show version info
482 */
483 static void
show_version(const char * argv0)484 show_version(const char *argv0)
485 {
486 printf("%s: version %s\n", argv0, tvheadend_version);
487 exit(0);
488 }
489
490 /**
491 *
492 */
493 static void
show_usage(const char * argv0,cmdline_opt_t * opts,int num,const char * err,...)494 show_usage
495 (const char *argv0, cmdline_opt_t *opts, int num, const char *err, ...)
496 {
497 int i;
498 char buf[256];
499 printf(_("Usage: %s [OPTIONS]\n"), argv0);
500 for (i = 0; i < num; i++) {
501
502 /* Section */
503 if (!opts[i].lopt) {
504 printf("\n%s\n\n", tvh_gettext(opts[i].desc));
505
506 /* Option */
507 } else {
508 char sopt[4];
509 char *desc, *tok;
510 if (opts[i].sopt)
511 snprintf(sopt, sizeof(sopt), "-%c,", opts[i].sopt);
512 else
513 strcpy(sopt, " ");
514 snprintf(buf, sizeof(buf), " %s --%s", sopt, opts[i].lopt);
515 desc = strdup(tvh_gettext(opts[i].desc));
516 tok = strtok(desc, "\n");
517 while (tok) {
518 printf("%-30s%s\n", buf, tok);
519 tok = buf;
520 while (*tok) {
521 *tok = ' ';
522 tok++;
523 }
524 tok = strtok(NULL, "\n");
525 }
526 free(desc);
527 }
528 }
529 printf("%s",
530 _("\n"
531 "For more information please visit the Tvheadend website:\n"
532 "https://tvheadend.org\n"));
533 exit(0);
534 }
535
536 /**
537 * Show subsystems info
538 */
539 static void
show_subsystems(const char * argv0)540 show_subsystems(const char *argv0)
541 {
542 int i;
543 tvhlog_subsys_t *ts = tvhlog_subsystems;
544 printf("Subsystems:\n\n");
545 for (i = 1, ts++; i < LS_LAST; i++, ts++) {
546 printf(" %-15s %s\n", ts->name, _(ts->desc));
547 }
548 exit(0);
549 }
550
551 /**
552 *
553 */
554 static inline time_t
gdispatch_clock_update(void)555 gdispatch_clock_update(void)
556 {
557 time_t now = time(NULL);
558 atomic_set_time_t(&__gdispatch_clock, now);
559 return now;
560 }
561
562 /**
563 *
564 */
565 static int64_t
mdispatch_clock_update(void)566 mdispatch_clock_update(void)
567 {
568 int64_t mono = getmonoclock();
569
570 if (mono > atomic_get_s64(&mtimer_periodic)) {
571 atomic_set_s64(&mtimer_periodic, mono + MONOCLOCK_RESOLUTION);
572 gdispatch_clock_update(); /* gclk() update */
573 comet_flush(); /* Flush idle comet mailboxes */
574 }
575
576 atomic_set_s64(&__mdispatch_clock, mono);
577 return mono;
578 }
579
580 /**
581 *
582 */
583 static void *
mtimer_tick_thread(void * aux)584 mtimer_tick_thread(void *aux)
585 {
586 while (tvheadend_is_running()) {
587 /* update clocks each 10x in one second */
588 atomic_set_s64(&__mdispatch_clock, getmonoclock());
589 tvh_safe_usleep(100000);
590 }
591 return NULL;
592 }
593
594 /**
595 *
596 */
597 static void *
mtimer_thread(void * aux)598 mtimer_thread(void *aux)
599 {
600 mtimer_t *mti;
601 mti_callback_t *cb;
602 int64_t now, next;
603 #if ENABLE_GTIMER_CHECK
604 int64_t mtm;
605 const char *id;
606 const char *fcn;
607 #endif
608
609 while (tvheadend_is_running()) {
610 now = mdispatch_clock_update();
611
612 /* Global monoclock timers */
613 pthread_mutex_lock(&global_lock);
614
615 next = now + sec2mono(3600);
616
617 while((mti = LIST_FIRST(&mtimers)) != NULL) {
618
619 if (mti->mti_expire > now) {
620 next = mti->mti_expire;
621 break;
622 }
623
624 #if ENABLE_GTIMER_CHECK
625 mtm = getmonoclock();
626 id = mti->mti_id;
627 fcn = mti->mti_fcn;
628 #endif
629 cb = mti->mti_callback;
630
631 LIST_REMOVE(mti, mti_link);
632 mti->mti_callback = NULL;
633
634 cb(mti->mti_opaque);
635
636 #if ENABLE_GTIMER_CHECK
637 mtm = getmonoclock() - mtm;
638 if (mtm > 10000)
639 tvhtrace(LS_MTIMER, "%s:%s duration %"PRId64"us", id, fcn, mtm);
640 #endif
641 }
642
643 /* Periodic updates */
644 if (next > mtimer_periodic)
645 next = mtimer_periodic;
646
647 /* Wait */
648 tvh_cond_timedwait(&mtimer_cond, &global_lock, next);
649 pthread_mutex_unlock(&global_lock);
650 }
651
652 return NULL;
653 }
654
655 /**
656 *
657 */
658 static void
mainloop(void)659 mainloop(void)
660 {
661 gtimer_t *gti;
662 gti_callback_t *cb;
663 time_t now;
664 struct timespec ts;
665 #if ENABLE_GTIMER_CHECK
666 int64_t mtm;
667 const char *id;
668 const char *fcn;
669 #endif
670
671 while (tvheadend_is_running()) {
672 now = gdispatch_clock_update();
673 ts.tv_sec = now + 3600;
674 ts.tv_nsec = 0;
675
676 /* Global timers */
677 pthread_mutex_lock(&global_lock);
678
679 // TODO: there is a risk that if timers re-insert themselves to
680 // the top of the list with a 0 offset we could loop indefinitely
681
682 #if 0
683 tvhdebug(LS_GTIMER, "now %"PRItime_t, ts.tv_sec);
684 LIST_FOREACH(gti, >imers, gti_link)
685 tvhdebug(LS_GTIMER, " gti %p expire %"PRItimet, gti, gti->gti_expire.tv_sec);
686 #endif
687
688 while((gti = LIST_FIRST(>imers)) != NULL) {
689
690 if (gti->gti_expire > now) {
691 ts.tv_sec = gti->gti_expire;
692 break;
693 }
694
695 #if ENABLE_GTIMER_CHECK
696 mtm = getmonoclock();
697 id = gti->gti_id;
698 fcn = gti->gti_fcn;
699 #endif
700 cb = gti->gti_callback;
701
702 LIST_REMOVE(gti, gti_link);
703 gti->gti_callback = NULL;
704
705 cb(gti->gti_opaque);
706
707 #if ENABLE_GTIMER_CHECK
708 mtm = getmonoclock() - mtm;
709 if (mtm > 10000)
710 tvhtrace(LS_GTIMER, "%s:%s duration %"PRId64"us", id, fcn, mtm);
711 #endif
712 }
713
714 /* Wait */
715 pthread_cond_timedwait(>imer_cond, &global_lock, &ts);
716 pthread_mutex_unlock(&global_lock);
717 }
718 }
719
720
721 /**
722 *
723 */
724 int
main(int argc,char ** argv)725 main(int argc, char **argv)
726 {
727 int i;
728 sigset_t set;
729 #if ENABLE_MPEGTS
730 uint32_t adapter_mask = 0;
731 #endif
732 int log_level = LOG_INFO;
733 int log_options = TVHLOG_OPT_MILLIS | TVHLOG_OPT_STDERR | TVHLOG_OPT_SYSLOG;
734 const char *log_debug = NULL, *log_trace = NULL;
735 gid_t gid = -1;
736 uid_t uid = -1;
737 char buf[512];
738 FILE *pidfile = NULL;
739 static struct {
740 void *thread_id;
741 struct timeval tv;
742 uint8_t ru[32];
743 } randseed;
744 struct rlimit rl;
745 extern int dvb_bouquets_parse;
746
747 main_tid = pthread_self();
748
749 /* Setup global mutexes */
750 pthread_mutex_init(&fork_lock, NULL);
751 pthread_mutex_init(&global_lock, NULL);
752 pthread_mutex_init(&tasklet_lock, NULL);
753 pthread_mutex_init(&atomic_lock, NULL);
754 tvh_cond_init(&mtimer_cond);
755 pthread_cond_init(>imer_cond, NULL);
756 tvh_cond_init(&tasklet_cond);
757 TAILQ_INIT(&tasklets);
758
759 /* Defaults */
760 tvheadend_webui_port = 9981;
761 tvheadend_webroot = NULL;
762 tvheadend_htsp_port = 9982;
763 tvheadend_htsp_port_extra = 0;
764 __mdispatch_clock = getmonoclock();
765 __gdispatch_clock = time(NULL);
766
767 /* Command line options */
768 int opt_help = 0,
769 opt_version = 0,
770 opt_fork = 0,
771 opt_firstrun = 0,
772 opt_stderr = 0,
773 opt_nostderr = 0,
774 opt_syslog = 0,
775 opt_nosyslog = 0,
776 opt_uidebug = 0,
777 opt_abort = 0,
778 opt_noacl = 0,
779 opt_fileline = 0,
780 opt_threadid = 0,
781 opt_libav = 0,
782 opt_ipv6 = 0,
783 opt_nosatip = 0,
784 opt_satip_rtsp = 0,
785 #if ENABLE_TSFILE
786 opt_tsfile_tuner = 0,
787 #endif
788 opt_dump = 0,
789 opt_xspf = 0,
790 opt_dbus = 0,
791 opt_dbus_session = 0,
792 opt_nobackup = 0,
793 opt_nobat = 0,
794 opt_subsystems = 0;
795 const char *opt_config = NULL,
796 *opt_user = NULL,
797 *opt_group = NULL,
798 *opt_logpath = NULL,
799 *opt_log_debug = NULL,
800 *opt_log_trace = NULL,
801 *opt_pidpath = "/var/run/tvheadend.pid",
802 #if ENABLE_LINUXDVB
803 *opt_dvb_adapters = NULL,
804 #endif
805 *opt_bindaddr = NULL,
806 *opt_subscribe = NULL,
807 *opt_user_agent = NULL,
808 *opt_satip_bindaddr = NULL;
809 static char *__opt_satip_xml[10];
810 str_list_t opt_satip_xml = { .max = 10, .num = 0, .str = __opt_satip_xml };
811 static char *__opt_satip_tsfile[10];
812 str_list_t opt_tsfile = { .max = 10, .num = 0, .str = __opt_satip_tsfile };
813 cmdline_opt_t cmdline_opts[] = {
814 { 0, NULL, N_("Generic options"), OPT_BOOL, NULL },
815 { 'h', "help", N_("Show this page"), OPT_BOOL, &opt_help },
816 { 'v', "version", N_("Show version information"),OPT_BOOL, &opt_version },
817
818 { 0, NULL, N_("Service configuration"), OPT_BOOL, NULL },
819 { 'c', "config", N_("Alternate configuration path"), OPT_STR, &opt_config },
820 { 'B', "nobackup", N_("Don't backup configuration tree at upgrade"), OPT_BOOL, &opt_nobackup },
821 { 'f', "fork", N_("Fork and run as daemon"), OPT_BOOL, &opt_fork },
822 { 'u', "user", N_("Run as user"), OPT_STR, &opt_user },
823 { 'g', "group", N_("Run as group"), OPT_STR, &opt_group },
824 { 'p', "pid", N_("Alternate PID path"), OPT_STR, &opt_pidpath },
825 { 'C', "firstrun", N_("If no user account exists then create one with\n"
826 "no username and no password. Use with care as\n"
827 "it will allow world-wide administrative access\n"
828 "to your Tvheadend installation until you create or edit\n"
829 "the access control from within the Tvheadend web interface."),
830 OPT_BOOL, &opt_firstrun },
831 #if ENABLE_DBUS_1
832 { 'U', "dbus", N_("Enable DBus"),
833 OPT_BOOL, &opt_dbus },
834 { 'e', "dbus_session", N_("DBus - use the session message bus instead of the system one"),
835 OPT_BOOL, &opt_dbus_session },
836 #endif
837 #if ENABLE_LINUXDVB
838 { 'a', "adapters", N_("Only use specified DVB adapters (comma-separated, -1 = none)"),
839 OPT_STR, &opt_dvb_adapters },
840 #endif
841 #if ENABLE_SATIP_SERVER
842 { 0, "satip_bindaddr", N_("Specify bind address for SAT>IP server"),
843 OPT_STR, &opt_satip_bindaddr },
844 { 0, "satip_rtsp", N_("SAT>IP RTSP port number for server\n"
845 "(default: -1 = disable, 0 = webconfig, standard port is 554)"),
846 OPT_INT, &opt_satip_rtsp },
847 #endif
848 #if ENABLE_SATIP_CLIENT
849 { 0, "nosatip", N_("Disable SAT>IP client"),
850 OPT_BOOL, &opt_nosatip },
851 { 0, "satip_xml", N_("URL with the SAT>IP server XML location"),
852 OPT_STR_LIST, &opt_satip_xml },
853 #endif
854 { 0, NULL, N_("Server connectivity"), OPT_BOOL, NULL },
855 { '6', "ipv6", N_("Listen on IPv6"), OPT_BOOL, &opt_ipv6 },
856 { 'b', "bindaddr", N_("Specify bind address"), OPT_STR, &opt_bindaddr},
857 { 0, "http_port", N_("Specify alternative http port"),
858 OPT_INT, &tvheadend_webui_port },
859 { 0, "http_root", N_("Specify alternative http webroot"),
860 OPT_STR, &tvheadend_webroot },
861 { 0, "htsp_port", N_("Specify alternative htsp port"),
862 OPT_INT, &tvheadend_htsp_port },
863 { 0, "htsp_port2", N_("Specify extra htsp port"),
864 OPT_INT, &tvheadend_htsp_port_extra },
865 { 0, "useragent", N_("Specify User-Agent header for the http client"),
866 OPT_STR, &opt_user_agent },
867 { 0, "xspf", N_("Use XSPF playlist instead of M3U"),
868 OPT_BOOL, &opt_xspf },
869
870 { 0, NULL, N_("Debug options"), OPT_BOOL, NULL },
871 { 'd', "stderr", N_("Enable debug on stderr"), OPT_BOOL, &opt_stderr },
872 { 'n', "nostderr", N_("Disable debug on stderr"), OPT_BOOL, &opt_nostderr },
873 { 's', "syslog", N_("Enable debug to syslog"), OPT_BOOL, &opt_syslog },
874 { 'S', "nosyslog", N_("Disable syslog (all messages)"), OPT_BOOL, &opt_nosyslog },
875 { 'l', "logfile", N_("Enable debug to file"), OPT_STR, &opt_logpath },
876 { 0, "debug", N_("Enable debug subsystems"), OPT_STR, &opt_log_debug },
877 #if ENABLE_TRACE
878 { 0, "trace", N_("Enable trace subsystems"), OPT_STR, &opt_log_trace },
879 #endif
880 { 0, "subsystems",N_("List subsystems"), OPT_BOOL, &opt_subsystems },
881 { 0, "fileline", N_("Add file and line numbers to debug"), OPT_BOOL, &opt_fileline },
882 { 0, "threadid", N_("Add the thread ID to debug"), OPT_BOOL, &opt_threadid },
883 #if ENABLE_LIBAV
884 { 0, "libav", N_("More verbose libav log"), OPT_BOOL, &opt_libav },
885 #endif
886 { 0, "uidebug", N_("Enable web UI debug (non-minified JS)"), OPT_BOOL, &opt_uidebug },
887 { 'A', "abort", N_("Immediately abort"), OPT_BOOL, &opt_abort },
888 { 'D', "dump", N_("Enable coredumps for daemon"), OPT_BOOL, &opt_dump },
889 { 0, "noacl", N_("Disable all access control checks"),
890 OPT_BOOL, &opt_noacl },
891 { 0, "nobat", N_("Disable DVB bouquets"),
892 OPT_BOOL, &opt_nobat },
893 { 'j', "join", N_("Subscribe to a service permanently"),
894 OPT_STR, &opt_subscribe },
895
896
897 #if ENABLE_TSFILE || ENABLE_TSDEBUG
898 { 0, NULL, N_("Testing options"), OPT_BOOL, NULL },
899 { 0, "tsfile_tuners", N_("Number of tsfile tuners"), OPT_INT, &opt_tsfile_tuner },
900 { 0, "tsfile", N_("tsfile input (mux file)"), OPT_STR_LIST, &opt_tsfile },
901 #endif
902 #if ENABLE_TSDEBUG
903 { 0, "tsdebug", N_("Output directory for tsdebug"), OPT_STR, &tvheadend_tsdebug },
904 #endif
905
906 };
907
908 /* Get current directory */
909 tvheadend_cwd0 = dirname(tvh_strdupa(argv[0]));
910 tvheadend_cwd = dirname(tvh_strdupa(tvheadend_cwd0));
911
912 /* Set locale */
913 setlocale(LC_ALL, "");
914 setlocale(LC_NUMERIC, "C");
915 tvh_gettext_init();
916
917 /* make sure the timezone is set */
918 tzset();
919
920 /* Process command line */
921 for (i = 1; i < argc; i++) {
922
923 /* Find option */
924 cmdline_opt_t *opt
925 = cmdline_opt_find(cmdline_opts, ARRAY_SIZE(cmdline_opts), argv[i]);
926 if (!opt) {
927 show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts),
928 _("invalid option specified [%s]"), argv[i]);
929 continue;
930 }
931
932 /* Process */
933 if (opt->type == OPT_BOOL)
934 *((int*)opt->param) = 1;
935 else if (++i == argc)
936 show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts),
937 _("option %s requires a value"), opt->lopt);
938 else if (opt->type == OPT_INT)
939 *((int*)opt->param) = atoi(argv[i]);
940 else if (opt->type == OPT_STR_LIST) {
941 str_list_t *strl = opt->param;
942 if (strl->num < strl->max)
943 strl->str[strl->num++] = argv[i];
944 }
945 else
946 *((char**)opt->param) = argv[i];
947
948 /* Stop processing */
949 if (opt_help)
950 show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), NULL);
951 if (opt_version)
952 show_version(argv[0]);
953 if (opt_subsystems)
954 show_subsystems(argv[0]);
955 }
956
957 /* Additional cmdline processing */
958 if (opt_nobat)
959 dvb_bouquets_parse = 0;
960 #if ENABLE_LINUXDVB
961 if (!opt_dvb_adapters) {
962 adapter_mask = ~0;
963 } else {
964 char *p, *e;
965 char *r = NULL;
966 char *dvb_adapters = strdup(opt_dvb_adapters);
967 adapter_mask = 0x0;
968 i = 0;
969 p = strtok_r(dvb_adapters, ",", &r);
970 while (p) {
971 int a = strtol(p, &e, 10);
972 if (*e != 0 || a > 31) {
973 fprintf(stderr, _("Invalid adapter number '%s'\n"), p);
974 free(dvb_adapters);
975 return 1;
976 }
977 i = 1;
978 if (a < 0)
979 adapter_mask = 0;
980 else
981 adapter_mask |= (1 << a);
982 p = strtok_r(NULL, ",", &r);
983 }
984 free(dvb_adapters);
985 if (!i) {
986 fprintf(stderr, "%s", _("No adapters specified!\n"));
987 return 1;
988 }
989 }
990 #endif
991 if (tvheadend_webroot) {
992 char *tmp;
993 if (*tvheadend_webroot == '/')
994 tmp = strdup(tvheadend_webroot);
995 else {
996 tmp = malloc(strlen(tvheadend_webroot)+2);
997 *tmp = '/';
998 strcpy(tmp+1, tvheadend_webroot);
999 }
1000 if (tmp[strlen(tmp)-1] == '/')
1001 tmp[strlen(tmp)-1] = '\0';
1002 tvheadend_webroot = tmp;
1003 }
1004 tvheadend_webui_debug = opt_uidebug;
1005
1006 /* Setup logging */
1007 if (isatty(2))
1008 log_options |= TVHLOG_OPT_DECORATE;
1009 if (opt_stderr || opt_syslog || opt_logpath) {
1010 if (!opt_log_trace && !opt_log_debug)
1011 log_debug = "all";
1012 log_level = LOG_DEBUG;
1013 if (opt_stderr)
1014 log_options |= TVHLOG_OPT_DBG_STDERR;
1015 if (opt_syslog)
1016 log_options |= TVHLOG_OPT_DBG_SYSLOG;
1017 if (opt_logpath)
1018 log_options |= TVHLOG_OPT_DBG_FILE;
1019 }
1020 if (opt_nostderr)
1021 log_options &= ~(TVHLOG_OPT_DECORATE|TVHLOG_OPT_STDERR|TVHLOG_OPT_DBG_STDERR);
1022 if (opt_nosyslog)
1023 log_options &= ~(TVHLOG_OPT_SYSLOG|TVHLOG_OPT_DBG_SYSLOG);
1024 if (opt_fileline)
1025 log_options |= TVHLOG_OPT_FILELINE;
1026 if (opt_threadid)
1027 log_options |= TVHLOG_OPT_THREAD;
1028 if (opt_libav)
1029 log_options |= TVHLOG_OPT_LIBAV;
1030 if (opt_log_trace) {
1031 log_level = LOG_TRACE;
1032 log_trace = opt_log_trace;
1033 }
1034 if (opt_log_debug)
1035 log_debug = opt_log_debug;
1036
1037 tvhlog_init(log_level, log_options, opt_logpath);
1038 tvhlog_set_debug(log_debug);
1039 tvhlog_set_trace(log_trace);
1040 tvhinfo(LS_MAIN, "Log started");
1041
1042 signal(SIGPIPE, handle_sigpipe); // will be redundant later
1043 signal(SIGILL, handle_sigill); // see handler..
1044
1045 /* Set priviledges */
1046 if((opt_fork && getuid() == 0) || opt_group || opt_user) {
1047 const char *homedir;
1048 struct group *grp = getgrnam(opt_group ?: "video");
1049 struct passwd *pw = getpwnam(opt_user ?: "daemon");
1050
1051 if(grp != NULL) {
1052 gid = grp->gr_gid;
1053 } else {
1054 gid = 1;
1055 }
1056
1057 if (pw != NULL) {
1058 if (getuid() != pw->pw_uid) {
1059 gid_t glist[16];
1060 int gnum;
1061 gnum = get_user_groups(pw, glist, ARRAY_SIZE(glist));
1062 if (gnum > 0 && setgroups(gnum, glist)) {
1063 char buf[256] = "";
1064 int i;
1065 for (i = 0; i < gnum; i++)
1066 snprintf(buf + strlen(buf), sizeof(buf) - 1 - strlen(buf),
1067 ",%d", glist[i]);
1068 tvhlog(LOG_ALERT, LS_START,
1069 "setgroups(%s) failed, do you have permission?", buf+1);
1070 return 1;
1071 }
1072 }
1073 uid = pw->pw_uid;
1074 homedir = pw->pw_dir;
1075 setenv("HOME", homedir, 1);
1076 } else {
1077 uid = 1;
1078 }
1079 }
1080
1081 uuid_init();
1082 idnode_boot();
1083 config_boot(opt_config, gid, uid);
1084 tcp_server_preinit(opt_ipv6);
1085 http_server_init(opt_bindaddr); // bind to ports only
1086 htsp_init(opt_bindaddr); // bind to ports only
1087 satip_server_init(opt_satip_bindaddr, opt_satip_rtsp); // bind to ports only
1088
1089 if (opt_fork)
1090 pidfile = tvh_fopen(opt_pidpath, "w+");
1091
1092 if (gid != -1 && (getgid() != gid) && setgid(gid)) {
1093 tvhlog(LOG_ALERT, LS_START,
1094 "setgid(%d) failed, do you have permission?", gid);
1095 return 1;
1096 }
1097 if (uid != -1 && (getuid() != uid) && setuid(uid)) {
1098 tvhlog(LOG_ALERT, LS_START,
1099 "setuid(%d) failed, do you have permission?", uid);
1100 return 1;
1101 }
1102
1103 /* Daemonise */
1104 if(opt_fork) {
1105 if(daemon(0, 0)) {
1106 exit(2);
1107 }
1108 if(pidfile != NULL) {
1109 fprintf(pidfile, "%d\n", getpid());
1110 fclose(pidfile);
1111 }
1112
1113 /* Make dumpable */
1114 if (opt_dump) {
1115 if (chdir("/tmp"))
1116 tvhwarn(LS_START, "failed to change cwd to /tmp");
1117 #ifdef PLATFORM_LINUX
1118 prctl(PR_SET_DUMPABLE, 1);
1119 #else
1120 tvhwarn(LS_START, "Coredumps not implemented on your platform");
1121 #endif
1122 }
1123
1124 umask(0);
1125 }
1126
1127 memset(&rl, 0, sizeof(rl));
1128 if (getrlimit(RLIMIT_STACK, &rl) || rl.rlim_cur < 2*1024*1024) {
1129 rlim_t rl2 = rl.rlim_cur;
1130 rl.rlim_cur = 2*1024*1024;
1131 if (setrlimit(RLIMIT_STACK, &rl)) {
1132 tvhlog(LOG_ALERT, LS_START, "too small stack size - %ld", (long)rl2);
1133 return 1;
1134 }
1135 }
1136
1137 atomic_set(&tvheadend_running, 1);
1138
1139 /* Start log thread (must be done post fork) */
1140 tvhlog_start();
1141
1142 /* Alter logging */
1143 if (opt_fork)
1144 tvhlog_options &= ~TVHLOG_OPT_STDERR;
1145 if (!isatty(2))
1146 tvhlog_options &= ~TVHLOG_OPT_DECORATE;
1147
1148 /* Initialise clock */
1149 pthread_mutex_lock(&global_lock);
1150 __mdispatch_clock = getmonoclock();
1151 __gdispatch_clock = time(NULL);
1152
1153 /* Signal handling */
1154 sigfillset(&set);
1155 sigprocmask(SIG_BLOCK, &set, NULL);
1156 trap_init(argv[0]);
1157
1158 /* SSL library init */
1159 OPENSSL_config(NULL);
1160 SSL_load_error_strings();
1161 SSL_library_init();
1162 /* Rand seed */
1163 randseed.thread_id = (void *)main_tid;
1164 gettimeofday(&randseed.tv, NULL);
1165 uuid_random(randseed.ru, sizeof(randseed.ru));
1166 RAND_seed(&randseed, sizeof(randseed));
1167
1168 /* Initialise configuration */
1169 tvhftrace(LS_MAIN, notify_init);
1170 tvhftrace(LS_MAIN, spawn_init);
1171 tvhftrace(LS_MAIN, idnode_init);
1172 tvhftrace(LS_MAIN, config_init, opt_nobackup == 0);
1173
1174 /* Memoryinfo */
1175 idclass_register(&memoryinfo_class);
1176 memoryinfo_register(&tasklet_memoryinfo);
1177 #if ENABLE_SLOW_MEMORYINFO
1178 memoryinfo_register(&htsmsg_memoryinfo);
1179 memoryinfo_register(&htsmsg_field_memoryinfo);
1180 #endif
1181 memoryinfo_register(&pkt_memoryinfo);
1182 memoryinfo_register(&pktbuf_memoryinfo);
1183 memoryinfo_register(&pktref_memoryinfo);
1184
1185 /**
1186 * Initialize subsystems
1187 */
1188
1189 epg_in_load = 1;
1190
1191 tvhthread_create(&mtimer_tick_tid, NULL, mtimer_tick_thread, NULL, "mtick");
1192 tvhthread_create(&tasklet_tid, NULL, tasklet_thread, NULL, "tasklet");
1193
1194 tvhftrace(LS_MAIN, streaming_init);
1195 tvhftrace(LS_MAIN, tvh_hardware_init);
1196 tvhftrace(LS_MAIN, dbus_server_init, opt_dbus, opt_dbus_session);
1197 tvhftrace(LS_MAIN, intlconv_init);
1198 tvhftrace(LS_MAIN, api_init);
1199 tvhftrace(LS_MAIN, fsmonitor_init);
1200 tvhftrace(LS_MAIN, libav_init);
1201 tvhftrace(LS_MAIN, tvhtime_init);
1202 tvhftrace(LS_MAIN, profile_init);
1203 tvhftrace(LS_MAIN, imagecache_init);
1204 tvhftrace(LS_MAIN, http_client_init, opt_user_agent);
1205 tvhftrace(LS_MAIN, esfilter_init);
1206 tvhftrace(LS_MAIN, bouquet_init);
1207 tvhftrace(LS_MAIN, service_init);
1208 tvhftrace(LS_MAIN, dvb_init);
1209 #if ENABLE_MPEGTS
1210 tvhftrace(LS_MAIN, mpegts_init, adapter_mask, opt_nosatip, &opt_satip_xml,
1211 &opt_tsfile, opt_tsfile_tuner);
1212 #endif
1213 tvhftrace(LS_MAIN, channel_init);
1214 tvhftrace(LS_MAIN, bouquet_service_resolve);
1215 tvhftrace(LS_MAIN, subscription_init);
1216 tvhftrace(LS_MAIN, dvr_config_init);
1217 tvhftrace(LS_MAIN, access_init, opt_firstrun, opt_noacl);
1218 #if ENABLE_TIMESHIFT
1219 tvhftrace(LS_MAIN, timeshift_init);
1220 #endif
1221 tvhftrace(LS_MAIN, tcp_server_init);
1222 tvhftrace(LS_MAIN, webui_init, opt_xspf);
1223 #if ENABLE_UPNP
1224 tvhftrace(LS_MAIN, upnp_server_init, opt_bindaddr);
1225 #endif
1226 tvhftrace(LS_MAIN, service_mapper_init);
1227 tvhftrace(LS_MAIN, descrambler_init);
1228 tvhftrace(LS_MAIN, epggrab_init);
1229 tvhftrace(LS_MAIN, epg_init);
1230 tvhftrace(LS_MAIN, dvr_init);
1231 tvhftrace(LS_MAIN, dbus_server_start);
1232 tvhftrace(LS_MAIN, http_server_register);
1233 tvhftrace(LS_MAIN, satip_server_register);
1234 tvhftrace(LS_MAIN, htsp_register);
1235
1236 if(opt_subscribe != NULL)
1237 subscription_dummy_join(opt_subscribe, 1);
1238
1239 tvhftrace(LS_MAIN, avahi_init);
1240 tvhftrace(LS_MAIN, bonjour_init);
1241
1242 tvhftrace(LS_MAIN, epg_updated); // cleanup now all prev ref's should have been created
1243 epg_in_load = 0;
1244
1245 pthread_mutex_unlock(&global_lock);
1246
1247 /**
1248 * Wait for SIGTERM / SIGINT, but only in this thread
1249 */
1250
1251 sigemptyset(&set);
1252 sigaddset(&set, SIGTERM);
1253 sigaddset(&set, SIGINT);
1254
1255 signal(SIGTERM, doexit);
1256 signal(SIGINT, doexit);
1257
1258 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
1259
1260 tvhlog(LOG_NOTICE, LS_START, "HTS Tvheadend version %s started, "
1261 "running as PID:%d UID:%d GID:%d, CWD:%s CNF:%s",
1262 tvheadend_version,
1263 getpid(), getuid(), getgid(), getcwd(buf, sizeof(buf)),
1264 hts_settings_get_root());
1265
1266 if(opt_abort)
1267 abort();
1268
1269 tvhthread_create(&mtimer_tid, NULL, mtimer_thread, NULL, "mtimer");
1270 mainloop();
1271 pthread_mutex_lock(&global_lock);
1272 tvh_cond_signal(&mtimer_cond, 0);
1273 pthread_mutex_unlock(&global_lock);
1274 pthread_join(mtimer_tid, NULL);
1275
1276 #if ENABLE_DBUS_1
1277 tvhftrace(LS_MAIN, dbus_server_done);
1278 #endif
1279 #if ENABLE_UPNP
1280 tvhftrace(LS_MAIN, upnp_server_done);
1281 #endif
1282 tvhftrace(LS_MAIN, satip_server_done);
1283 tvhftrace(LS_MAIN, htsp_done);
1284 tvhftrace(LS_MAIN, http_server_done);
1285 tvhftrace(LS_MAIN, webui_done);
1286 tvhftrace(LS_MAIN, fsmonitor_done);
1287 tvhftrace(LS_MAIN, http_client_done);
1288 tvhftrace(LS_MAIN, tcp_server_done);
1289
1290 // Note: the locking is obviously a bit redundant, but without
1291 // we need to disable the gtimer_arm call in epg_save()
1292 pthread_mutex_lock(&global_lock);
1293 tvhftrace(LS_MAIN, epg_save);
1294
1295 #if ENABLE_TIMESHIFT
1296 tvhftrace(LS_MAIN, timeshift_term);
1297 #endif
1298 pthread_mutex_unlock(&global_lock);
1299
1300 tvhftrace(LS_MAIN, epggrab_done);
1301 #if ENABLE_MPEGTS
1302 tvhftrace(LS_MAIN, mpegts_done);
1303 #endif
1304 tvhftrace(LS_MAIN, dvr_done);
1305 tvhftrace(LS_MAIN, descrambler_done);
1306 tvhftrace(LS_MAIN, service_mapper_done);
1307 tvhftrace(LS_MAIN, service_done);
1308 tvhftrace(LS_MAIN, channel_done);
1309 tvhftrace(LS_MAIN, bouquet_done);
1310 tvhftrace(LS_MAIN, subscription_done);
1311 tvhftrace(LS_MAIN, access_done);
1312 tvhftrace(LS_MAIN, epg_done);
1313 tvhftrace(LS_MAIN, avahi_done);
1314 tvhftrace(LS_MAIN, bonjour_done);
1315 tvhftrace(LS_MAIN, imagecache_done);
1316 tvhftrace(LS_MAIN, lang_code_done);
1317 tvhftrace(LS_MAIN, api_done);
1318
1319 tvhtrace(LS_MAIN, "tasklet enter");
1320 tvh_cond_signal(&tasklet_cond, 0);
1321 pthread_join(tasklet_tid, NULL);
1322 tvhtrace(LS_MAIN, "tasklet thread end");
1323 tasklet_flush();
1324 tvhtrace(LS_MAIN, "tasklet leave");
1325 tvhtrace(LS_MAIN, "mtimer tick thread join enter");
1326 pthread_join(mtimer_tick_tid, NULL);
1327 tvhtrace(LS_MAIN, "mtimer tick thread join leave");
1328
1329 tvhftrace(LS_MAIN, dvb_done);
1330 tvhftrace(LS_MAIN, lang_str_done);
1331 tvhftrace(LS_MAIN, esfilter_done);
1332 tvhftrace(LS_MAIN, profile_done);
1333 tvhftrace(LS_MAIN, intlconv_done);
1334 tvhftrace(LS_MAIN, urlparse_done);
1335 tvhftrace(LS_MAIN, streaming_done);
1336 tvhftrace(LS_MAIN, idnode_done);
1337 tvhftrace(LS_MAIN, notify_done);
1338 tvhftrace(LS_MAIN, spawn_done);
1339
1340 tvhlog(LOG_NOTICE, LS_STOP, "Exiting HTS Tvheadend");
1341 tvhlog_end();
1342
1343 tvhftrace(LS_MAIN, config_done);
1344 tvhftrace(LS_MAIN, hts_settings_done);
1345
1346 if(opt_fork)
1347 unlink(opt_pidpath);
1348
1349 /* OpenSSL - welcome to the "cleanup" hell */
1350 ENGINE_cleanup();
1351 RAND_cleanup();
1352 CRYPTO_cleanup_all_ex_data();
1353 EVP_cleanup();
1354 CONF_modules_free();
1355 #if !defined(OPENSSL_NO_COMP)
1356 COMP_zlib_cleanup();
1357 #endif
1358 ERR_remove_state(0);
1359 ERR_free_strings();
1360 #if !defined(OPENSSL_NO_COMP) && OPENSSL_VERSION_NUMBER < 0x1010006f
1361 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
1362 #endif
1363 /* end of OpenSSL cleanup code */
1364
1365 #if ENABLE_DBUS_1
1366 extern void dbus_shutdown(void);
1367 if (opt_dbus) dbus_shutdown();
1368 #endif
1369 tvh_gettext_done();
1370 return 0;
1371 }
1372
1373 /**
1374 *
1375 */
1376 void
tvh_str_set(char ** strp,const char * src)1377 tvh_str_set(char **strp, const char *src)
1378 {
1379 free(*strp);
1380 *strp = src ? strdup(src) : NULL;
1381 }
1382
1383
1384 /**
1385 *
1386 */
1387 int
tvh_str_update(char ** strp,const char * src)1388 tvh_str_update(char **strp, const char *src)
1389 {
1390 if(src == NULL)
1391 return 0;
1392 free(*strp);
1393 *strp = strdup(src);
1394 return 1;
1395 }
1396
1397
1398 /**
1399 *
1400 */
1401 void
scopedunlock(pthread_mutex_t ** mtxp)1402 scopedunlock(pthread_mutex_t **mtxp)
1403 {
1404 pthread_mutex_unlock(*mtxp);
1405 }
1406
1407
1408 /**
1409 *
1410 */
tvheadend_capabilities_list(int check)1411 htsmsg_t *tvheadend_capabilities_list(int check)
1412 {
1413 const tvh_caps_t *tc = tvheadend_capabilities;
1414 htsmsg_t *r = htsmsg_create_list();
1415 while (tc->name) {
1416 if (!check || !tc->enabled || *tc->enabled)
1417 htsmsg_add_str(r, NULL, tc->name);
1418 tc++;
1419 }
1420 if (config.caclient_ui)
1421 htsmsg_add_str(r, NULL, "caclient_advanced");
1422 return r;
1423 }
1424
1425 /**
1426 *
1427 */
time_t_out_of_range_notify(int64_t val)1428 void time_t_out_of_range_notify(int64_t val)
1429 {
1430 tvherror(LS_MAIN, "time value out of range (%"PRId64") of time_t", val);
1431 }
1432