void mpegts_init ( int linuxdvb_mask, str_list_t *satip_client, str_list_t *tsfiles, int tstuners ) { /* Register classes (avoid API 400 errors due to not yet defined) */ idclass_register(&mpegts_network_class); idclass_register(&mpegts_mux_class); idclass_register(&mpegts_service_class); /* Network scanner */ #if ENABLE_MPEGTS mpegts_network_scan_init(); #endif /* Setup DVB networks */ #if ENABLE_MPEGTS_DVB dvb_network_init(); #endif /* TS files */ #if ENABLE_TSFILE if(tsfiles->num) { int i; tsfile_init(tstuners ?: tsfiles->num); for (i = 0; i < tsfiles->num; i++) tsfile_add_file(tsfiles->str[i]); }
int main(int argc, char **argv) { int i; sigset_t set; #if ENABLE_LINUXDVB uint32_t adapter_mask; #endif int log_level = LOG_INFO; int log_options = TVHLOG_OPT_MILLIS | TVHLOG_OPT_STDERR | TVHLOG_OPT_SYSLOG; const char *log_debug = NULL, *log_trace = NULL; char buf[512]; main_tid = pthread_self(); /* Setup global mutexes */ pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_init(&atomic_lock, NULL); pthread_cond_init(>imer_cond, NULL); /* Defaults */ tvheadend_webui_port = 9981; tvheadend_webroot = NULL; tvheadend_htsp_port = 9982; tvheadend_htsp_port_extra = 0; /* Command line options */ int opt_help = 0, opt_version = 0, opt_fork = 0, opt_firstrun = 0, opt_stderr = 0, opt_syslog = 0, opt_uidebug = 0, opt_abort = 0, opt_noacl = 0, opt_fileline = 0, opt_threadid = 0, opt_ipv6 = 0, opt_tsfile_tuner = 0, opt_dump = 0; const char *opt_config = NULL, *opt_user = NULL, *opt_group = NULL, *opt_logpath = NULL, *opt_log_debug = NULL, *opt_log_trace = NULL, *opt_pidpath = "/var/run/tvheadend.pid", #if ENABLE_LINUXDVB *opt_dvb_adapters = NULL, #endif *opt_bindaddr = NULL, *opt_subscribe = NULL; str_list_t opt_tsfile = { .max = 10, .num = 0, .str = calloc(10, sizeof(char*)) }; cmdline_opt_t cmdline_opts[] = { { 0, NULL, "Generic Options", OPT_BOOL, NULL }, { 'h', "help", "Show this page", OPT_BOOL, &opt_help }, { 'v', "version", "Show version infomation", OPT_BOOL, &opt_version }, { 0, NULL, "Service Configuration", OPT_BOOL, NULL }, { 'c', "config", "Alternate config path", OPT_STR, &opt_config }, { 'f', "fork", "Fork and run as daemon", OPT_BOOL, &opt_fork }, { 'u', "user", "Run as user", OPT_STR, &opt_user }, { 'g', "group", "Run as group", OPT_STR, &opt_group }, { 'p', "pid", "Alternate pid path", OPT_STR, &opt_pidpath }, { 'C', "firstrun", "If no user account exists then create one with\n" "no username and no password. Use with care as\n" "it will allow world-wide administrative access\n" "to your Tvheadend installation until you edit\n" "the access-control from within the Tvheadend UI", OPT_BOOL, &opt_firstrun }, #if ENABLE_LINUXDVB { 'a', "adapters", "Only use specified DVB adapters (comma separated)", OPT_STR, &opt_dvb_adapters }, #endif { 0, NULL, "Server Connectivity", OPT_BOOL, NULL }, { '6', "ipv6", "Listen on IPv6", OPT_BOOL, &opt_ipv6 }, { 'b', "bindaddr", "Specify bind address", OPT_STR, &opt_bindaddr}, { 0, "http_port", "Specify alternative http port", OPT_INT, &tvheadend_webui_port }, { 0, "http_root", "Specify alternative http webroot", OPT_STR, &tvheadend_webroot }, { 0, "htsp_port", "Specify alternative htsp port", OPT_INT, &tvheadend_htsp_port }, { 0, "htsp_port2", "Specify extra htsp port", OPT_INT, &tvheadend_htsp_port_extra }, { 0, NULL, "Debug Options", OPT_BOOL, NULL }, { 'd', "stderr", "Enable debug on stderr", OPT_BOOL, &opt_stderr }, { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, { 'l', "logfile", "Enable debug to file", OPT_STR, &opt_logpath }, { 0, "debug", "Enable debug subsystems", OPT_STR, &opt_log_debug }, #if ENABLE_TRACE { 0, "trace", "Enable trace subsystems", OPT_STR, &opt_log_trace }, #endif { 0, "fileline", "Add file and line numbers to debug", OPT_BOOL, &opt_fileline }, { 0, "threadid", "Add the thread ID to debug", OPT_BOOL, &opt_threadid }, { 0, "uidebug", "Enable webUI debug (non-minified JS)", OPT_BOOL, &opt_uidebug }, { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, { 'D', "dump", "Enable coredumps for daemon", OPT_BOOL, &opt_dump }, { 0, "noacl", "Disable all access control checks", OPT_BOOL, &opt_noacl }, { 'j', "join", "Subscribe to a service permanently", OPT_STR, &opt_subscribe }, { 0, NULL, "TODO: testing", OPT_BOOL, NULL }, { 0, "tsfile_tuners", "Number of tsfile tuners", OPT_INT, &opt_tsfile_tuner }, { 0, "tsfile", "tsfile input (mux file)", OPT_STR_LIST, &opt_tsfile }, }; /* Get current directory */ tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); /* Set locale */ setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* make sure the timezone is set */ tzset(); /* Process command line */ for (i = 1; i < argc; i++) { /* Find option */ cmdline_opt_t *opt = cmdline_opt_find(cmdline_opts, ARRAY_SIZE(cmdline_opts), argv[i]); if (!opt) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), "invalid option specified [%s]", argv[i]); /* Process */ if (opt->type == OPT_BOOL) *((int*)opt->param) = 1; else if (++i == argc) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), "option %s requires a value", opt->lopt); else if (opt->type == OPT_INT) *((int*)opt->param) = atoi(argv[i]); else if (opt->type == OPT_STR_LIST) { str_list_t *strl = opt->param; if (strl->num < strl->max) strl->str[strl->num++] = argv[i]; } else *((char**)opt->param) = argv[i]; /* Stop processing */ if (opt_help) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), NULL); if (opt_version) show_version(argv[0]); } /* Additional cmdline processing */ #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; } else { char *p, *e; char *r = NULL; char *dvb_adapters = strdup(opt_dvb_adapters); adapter_mask = 0x0; p = strtok_r(dvb_adapters, ",", &r); while (p) { int a = strtol(p, &e, 10); if (*e != 0 || a < 0 || a > 31) { tvhlog(LOG_ERR, "START", "Invalid adapter number '%s'", p); free(dvb_adapters); return 1; } adapter_mask |= (1 << a); p = strtok_r(NULL, ",", &r); } free(dvb_adapters); if (!adapter_mask) { tvhlog(LOG_ERR, "START", "No adapters specified!"); return 1; } } #endif if (tvheadend_webroot) { char *tmp; if (*tvheadend_webroot == '/') tmp = strdup(tvheadend_webroot); else { tmp = malloc(strlen(tvheadend_webroot)+2); *tmp = '/'; strcpy(tmp+1, tvheadend_webroot); } if (tmp[strlen(tmp)-1] == '/') tmp[strlen(tmp)-1] = '\0'; tvheadend_webroot = tmp; } tvheadend_webui_debug = opt_uidebug; /* Setup logging */ if (isatty(2)) log_options |= TVHLOG_OPT_DECORATE; if (opt_stderr || opt_syslog || opt_logpath) { if (!opt_log_trace && !opt_log_debug) log_debug = "all"; log_level = LOG_DEBUG; if (opt_stderr) log_options |= TVHLOG_OPT_DBG_STDERR; if (opt_syslog) log_options |= TVHLOG_OPT_DBG_SYSLOG; if (opt_logpath) log_options |= TVHLOG_OPT_DBG_FILE; } if (opt_fileline) log_options |= TVHLOG_OPT_FILELINE; if (opt_threadid) log_options |= TVHLOG_OPT_THREAD; if (opt_log_trace) { log_level = LOG_TRACE; log_trace = opt_log_trace; } if (opt_log_debug) log_debug = opt_log_debug; tvhlog_init(log_level, log_options, opt_logpath); tvhlog_set_debug(log_debug); tvhlog_set_trace(log_trace); signal(SIGPIPE, handle_sigpipe); // will be redundant later /* Daemonise */ if(opt_fork) { const char *homedir; gid_t gid; uid_t uid; struct group *grp = getgrnam(opt_group ?: "video"); struct passwd *pw = opt_user ? getpwnam(opt_user) : NULL; FILE *pidfile = fopen(opt_pidpath, "w+"); if(grp != NULL) { gid = grp->gr_gid; } else { gid = 1; } if (pw != NULL) { if (getuid() != pw->pw_uid) { gid_t glist[10]; int gnum; gnum = get_user_groups(pw, glist, 10); if (setgroups(gnum, glist)) { tvhlog(LOG_ALERT, "START", "setgroups() failed, do you have permission?"); return 1; } } uid = pw->pw_uid; homedir = pw->pw_dir; setenv("HOME", homedir, 1); } else { uid = 1; } if ((getgid() != gid) && setgid(gid)) { tvhlog(LOG_ALERT, "START", "setgid() failed, do you have permission?"); return 1; } if ((getuid() != uid) && setuid(uid)) { tvhlog(LOG_ALERT, "START", "setuid() failed, do you have permission?"); return 1; } if(daemon(0, 0)) { exit(2); } if(pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } /* Make dumpable */ if (opt_dump) { #ifdef PLATFORM_LINUX if (chdir("/tmp")) tvhwarn("START", "failed to change cwd to /tmp"); prctl(PR_SET_DUMPABLE, 1); #else tvhwarn("START", "Coredumps not implemented on your platform"); #endif } umask(0); } tvheadend_running = 1; /* Start log thread (must be done post fork) */ tvhlog_start(); /* Alter logging */ if (opt_fork) tvhlog_options &= ~TVHLOG_OPT_STDERR; if (!isatty(2)) tvhlog_options &= ~TVHLOG_OPT_DECORATE; /* Initialise clock */ pthread_mutex_lock(&global_lock); time(&dispatch_clock); /* Signal handling */ sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); trap_init(argv[0]); /* Initialise configuration */ uuid_init(); idnode_init(); config_init(opt_config); /** * Initialize subsystems */ api_init(); fsmonitor_init(); #if ENABLE_LIBAV libav_init(); transcoding_init(); #endif imagecache_init(); service_init(); #if ENABLE_TSFILE if(opt_tsfile.num) { tsfile_init(opt_tsfile_tuner ?: opt_tsfile.num); for (i = 0; i < opt_tsfile.num; i++) tsfile_add_file(opt_tsfile.str[i]); } #endif #if ENABLE_MPEGTS_DVB dvb_network_init(); #endif #if ENABLE_IPTV iptv_init(); #endif #if ENABLE_LINUXDVB linuxdvb_init(adapter_mask); #endif channel_init(); subscription_init(); access_init(opt_firstrun, opt_noacl); #if ENABLE_TIMESHIFT timeshift_init(); #endif http_client_init(); tcp_server_init(opt_ipv6); http_server_init(opt_bindaddr); webui_init(); service_mapper_init(); descrambler_init(); epggrab_init(); epg_init(); dvr_init(); htsp_init(opt_bindaddr); if(opt_subscribe != NULL) subscription_dummy_join(opt_subscribe, 1); avahi_init(); epg_updated(); // cleanup now all prev ref's should have been created pthread_mutex_unlock(&global_lock); /** * Wait for SIGTERM / SIGINT, but only in this thread */ sigemptyset(&set); sigaddset(&set, SIGTERM); sigaddset(&set, SIGINT); signal(SIGTERM, doexit); signal(SIGINT, doexit); pthread_sigmask(SIG_UNBLOCK, &set, NULL); tvhlog(LOG_NOTICE, "START", "HTS Tvheadend version %s started, " "running as PID:%d UID:%d GID:%d, CWD:%s CNF:%s", tvheadend_version, getpid(), getuid(), getgid(), getcwd(buf, sizeof(buf)), hts_settings_get_root()); if(opt_abort) abort(); mainloop(); tvhftrace("main", htsp_done); tvhftrace("main", http_server_done); tvhftrace("main", webui_done); tvhftrace("main", http_client_done); tvhftrace("main", fsmonitor_done); #if ENABLE_MPEGTS_DVB tvhftrace("main", dvb_network_done); #endif #if ENABLE_IPTV tvhftrace("main", iptv_done); #endif #if ENABLE_LINUXDVB tvhftrace("main", linuxdvb_done); #endif #if ENABLE_TSFILE tvhftrace("main", tsfile_done); #endif // Note: the locking is obviously a bit redundant, but without // we need to disable the gtimer_arm call in epg_save() pthread_mutex_lock(&global_lock); tvhftrace("main", epg_save); #if ENABLE_TIMESHIFT tvhftrace("main", timeshift_term); #endif pthread_mutex_unlock(&global_lock); tvhftrace("main", epggrab_done); tvhftrace("main", tcp_server_done); tvhftrace("main", descrambler_done); tvhftrace("main", service_mapper_done); tvhftrace("main", service_done); tvhftrace("main", channel_done); tvhftrace("main", dvr_done); tvhftrace("main", subscription_done); tvhftrace("main", access_done); tvhftrace("main", epg_done); tvhftrace("main", avahi_done); tvhftrace("main", imagecache_done); tvhftrace("main", idnode_done); tvhftrace("main", lang_code_done); tvhftrace("main", api_done); tvhftrace("main", config_done); tvhftrace("main", hts_settings_done); tvhftrace("main", dvb_done); tvhftrace("main", lang_str_done); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); tvhlog_end(); if(opt_fork) unlink(opt_pidpath); free(opt_tsfile.str); return 0; }