/* * Flush all data to live */ static int _timeshift_flush_to_live ( timeshift_t *ts, timeshift_file_t **cur_file, streaming_message_t **sm, int *wait ) { time_t pts = 0; while (*cur_file) { if (_timeshift_read(ts, cur_file, sm, wait) == -1) return -1; if (!*sm) break; if ((*sm)->sm_type == SMT_PACKET) { pts = ((th_pkt_t*)(*sm)->sm_data)->pkt_pts; tvhlog(LOG_DEBUG, "timeshift", "ts %d deliver %"PRId64" pts=%"PRItime_t, ts->id, (*sm)->sm_time, pts); } streaming_target_deliver2(ts->output, *sm); *sm = NULL; } return 0; }
static void libav_log_callback(void *ptr, int level, const char *fmt, va_list vl) { char message[8192]; char *nl; char *l; memset(message, 0, sizeof(message)); vsnprintf(message, sizeof(message), fmt, vl); l = message; if(level == AV_LOG_DEBUG) level = LOG_DEBUG; else if(level == AV_LOG_VERBOSE) level = LOG_INFO; else if(level == AV_LOG_INFO) level = LOG_NOTICE; else if(level == AV_LOG_WARNING) level = LOG_WARNING; else if(level == AV_LOG_ERROR) level = LOG_ERR; else if(level == AV_LOG_FATAL) level = LOG_CRIT; else if(level == AV_LOG_PANIC) level = LOG_EMERG; while(l < message + sizeof(message)) { nl = strstr(l, "\n"); if(nl) *nl = '\0'; if(!strlen(l)) break; tvhlog(level, "libav", "%s", l); l += strlen(message); if(!nl) break; } }
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { assert(c); /* Called whenever the client or server state changes */ switch (state) { case AVAHI_CLIENT_S_RUNNING: /* The server has startup successfully and registered its host * name on the network, so it's time to create our services */ create_services(c); break; case AVAHI_CLIENT_FAILURE: tvhlog(LOG_ERR, "AVAHI", "Client failure: %s", avahi_strerror(avahi_client_errno(c))); break; case AVAHI_CLIENT_S_COLLISION: /* Let's drop our registered services. When the server is back * in AVAHI_SERVER_RUNNING state we will register them * again with the new host name. */ case AVAHI_CLIENT_S_REGISTERING: /* The server records are now being established. This * might be caused by a host name change. We need to wait * for our own records to register until the host name is * properly esatblished. */ if(group) avahi_entry_group_reset(group); break; case AVAHI_CLIENT_CONNECTING: ; } }
static void spawn_pipe_read( th_pipe_t *p, char **_buf, int level ) { char *buf = *_buf, *s; size_t len; int r; if (buf == NULL) { buf = malloc(SPAWN_PIPE_READ_SIZE); buf[0] = '\0'; buf[SPAWN_PIPE_READ_SIZE - 1] = 0; *_buf = buf; } while (1) { len = strlen(buf); r = read(p->rd, buf + len, SPAWN_PIPE_READ_SIZE - 1 - len); if (r < 1) { if (errno == EAGAIN) break; if (ERRNO_AGAIN(errno)) continue; break; } buf[len + r] = '\0'; tvhlog_hexdump("spawn", buf + len, r); while (1) { s = buf; while (*s && *s != '\n' && *s != '\r') s++; if (*s == '\0') break; *s++ = '\0'; if (buf[0]) tvhlog(level, "spawn", "%s", buf); memmove(buf, s, strlen(s) + 1); } if (strlen(buf) == SPAWN_PIPE_READ_SIZE - 1) { tvherror("spawn", "pipe buffer full"); buf[0] = '\0'; } } }
/** * Create a new libavformat based muxer */ muxer_t* lav_muxer_create(muxer_container_type_t mc) { const char *mux_name; lav_muxer_t *lm; AVOutputFormat *fmt; switch(mc) { case MC_MPEGPS: mux_name = "dvd"; break; default: mux_name = muxer_container_type2txt(mc); break; } fmt = av_guess_format(mux_name, NULL, NULL); if(!fmt) { tvhlog(LOG_ERR, "libav", "Can't find the '%s' muxer", mux_name); return NULL; } lm = calloc(1, sizeof(lav_muxer_t)); lm->m_open_stream = lav_muxer_open_stream; lm->m_open_file = lav_muxer_open_file; lm->m_init = lav_muxer_init; lm->m_reconfigure = lav_muxer_reconfigure; lm->m_mime = lav_muxer_mime; lm->m_add_marker = lav_muxer_add_marker; lm->m_write_meta = lav_muxer_write_meta; lm->m_write_pkt = lav_muxer_write_pkt; lm->m_close = lav_muxer_close; lm->m_destroy = lav_muxer_destroy; lm->m_container = mc; lm->lm_oc = avformat_alloc_context(); lm->lm_oc->oformat = fmt; lm->lm_fd = -1; lm->lm_init = 0; return (muxer_t*)lm; }
/** * Create a new muxer */ muxer_t* muxer_create(muxer_container_type_t mc) { muxer_t *m; m = pass_muxer_create(mc); if(!m) m = tvh_muxer_create(mc); #if ENABLE_LIBAV if(!m) m = lav_muxer_create(mc); #endif if(!m) tvhlog(LOG_ERR, "mux", "Can't find a muxer that supports '%s' container", muxer_container_type2txt(mc)); return m; }
static void epggrab_ota_done ( epggrab_ota_mux_t *om, int reason ) { static const char *reasons[] = { [EPGGRAB_OTA_DONE_COMPLETE] = "complete", [EPGGRAB_OTA_DONE_TIMEOUT] = "timeout", [EPGGRAB_OTA_DONE_NO_DATA] = "no data", [EPGGRAB_OTA_DONE_STOLEN] = "stolen" }; char name[256]; mpegts_mux_t *mm; epggrab_ota_map_t *map; if (om->om_save) epggrab_ota_save(om); mm = mpegts_mux_find(om->om_mux_uuid); mpegts_mux_nice_name(mm, name, sizeof(name)); tvhdebug("epggrab", "grab done for %s (%s)", name, reasons[reason]); gtimer_disarm(&om->om_timer); gtimer_disarm(&om->om_data_timer); assert(om->om_q_type == EPGGRAB_OTA_MUX_ACTIVE); TAILQ_REMOVE(&epggrab_ota_active, om, om_q_link); om->om_q_type = EPGGRAB_OTA_MUX_IDLE; if (reason == EPGGRAB_OTA_DONE_STOLEN) { /* Do not requeue completed muxes */ if (!om->om_done && om->om_requeue) { TAILQ_INSERT_HEAD(&epggrab_ota_pending, om, om_q_link); om->om_q_type = EPGGRAB_OTA_MUX_PENDING; } else { om->om_requeue = 0; } } else if (reason == EPGGRAB_OTA_DONE_TIMEOUT) { om->om_requeue = 0; LIST_FOREACH(map, &om->om_modules, om_link) if (!map->om_complete) tvhlog(LOG_WARNING, "epggrab", "%s - data completion timeout for %s", map->om_module->name, name); } else {
/* * Update time */ void tvhtime_update ( struct tm *tm ) { time_t now; struct timeval tv; ntp_shm_t *ntp_shm; int64_t t1, t2; /* Current and reported time */ now = mktime(tm); gettimeofday(&tv, NULL); /* Delta */ t1 = now * 1000000; t2 = tv.tv_sec * 1000000 + tv.tv_usec; #if NTP_TRACE tvhlog(LOG_DEBUG, "ntp", "delta = %"PRId64" us\n", t2 - t1); #endif /* Update local clock */ if (tvhtime_update_enabled) if (llabs(t2 - t1) > tvhtime_tolerance) stime(&now); /* NTP */ if (tvhtime_ntp_enabled) { if (!(ntp_shm = ntp_shm_init())) return; ntp_shm->valid = 0; ntp_shm->count++; ntp_shm->clockTimeStampSec = now; ntp_shm->clockTimeStampUSec = 0; ntp_shm->receiveTimeStampSec = tv.tv_sec; ntp_shm->receiveTimeStampUSec = (int)tv.tv_usec; ntp_shm->count++; ntp_shm->valid = 1; } }
int diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, __u8 data_1, __u8 data_2, __u8 data_3, __u8 msg_len) { int err; struct dvb_diseqc_master_cmd message; tvhtrace("diseqc", "sending %X %X %X %X %X %X", framing_byte, address, cmd, data_1, data_2, data_3); message.msg[0] = framing_byte; message.msg[1] = address; message.msg[2] = cmd; message.msg[3] = data_1; message.msg[4] = data_2; message.msg[5] = data_3; message.msg_len = msg_len; if ((err = ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, &message))) { tvhlog(LOG_ERR, "diseqc", "error sending diseqc command"); return err; } return 0; }
void limitedlog(loglimiter_t *ll, const char *sys, const char *o, const char *event) { time_t now; char buf[64]; time(&now); ll->events++; if(ll->last == now) return; // Duplicate event if(ll->last <= now - 10) { // Too old, reset duplicate counter ll->events = 0; buf[0] = 0; } else { snprintf(buf, sizeof(buf), ", %d duplicate log lines suppressed", ll->events); } tvhlog(LOG_WARNING, sys, "%s: %s%s", o, event, buf); ll->last = now; }
/** * Close the muxer and append trailer to output */ static int lav_muxer_close(muxer_t *m) { int i; int ret = 0; lav_muxer_t *lm = (lav_muxer_t*)m; if(lm->lm_init && av_write_trailer(lm->lm_oc) < 0) { tvhlog(LOG_WARNING, "libav", "Failed to write %s trailer", muxer_container_type2txt(lm->m_container)); lm->m_errors++; ret = -1; } if(lm->lm_h264_filter) av_bitstream_filter_close(lm->lm_h264_filter); for(i=0; i<lm->lm_oc->nb_streams; i++) av_freep(&lm->lm_oc->streams[i]->codec->extradata); lm->lm_oc->nb_streams = 0; return ret; }
/* * Output packet */ static int _timeshift_read ( timeshift_t *ts, timeshift_file_t **cur_file, streaming_message_t **sm, int *wait ) { timeshift_file_t *tsf = *cur_file; ssize_t r; off_t off, ooff; if (tsf) { /* Open file */ if (tsf->rfd < 0 && !tsf->ram) { tsf->rfd = open(tsf->path, O_RDONLY); tvhtrace("timeshift", "ts %d open file %s (fd %i)", ts->id, tsf->path, tsf->rfd); if (tsf->rfd < 0) return -1; } if (tsf->rfd >= 0) if ((off = lseek(tsf->rfd, tsf->roff, SEEK_SET)) != tsf->roff) tvherror("timeshift", "ts %d seek to %s failed (off %"PRId64" != %"PRId64"): %s", ts->id, tsf->path, (int64_t)tsf->roff, (int64_t)off, strerror(errno)); /* Read msg */ ooff = tsf->roff; r = _read_msg(tsf, -1, sm); if (r < 0) { streaming_message_t *e = streaming_msg_create_code(SMT_STOP, SM_CODE_UNDEFINED_ERROR); streaming_target_deliver2(ts->output, e); tvhtrace("timeshift", "ts %d seek to %jd (fd %i)", ts->id, (intmax_t)tsf->roff, tsf->rfd); tvhlog(LOG_ERR, "timeshift", "ts %d could not read buffer", ts->id); return -1; } tvhtrace("timeshift", "ts %d seek to %jd (fd %i) read msg %p (%"PRId64")", ts->id, (intmax_t)tsf->roff, tsf->rfd, *sm, (int64_t)r); /* Incomplete */ if (r == 0) { if (tsf->rfd >= 0) { tvhtrace("timeshift", "ts %d seek to %jd (fd %i) (incomplete)", ts->id, (intmax_t)tsf->roff, tsf->rfd); if ((off = lseek(tsf->rfd, ooff, SEEK_SET)) != ooff) tvherror("timeshift", "seek to %s failed (off %"PRId64" != %"PRId64"): %s", tsf->path, (int64_t)ooff, (int64_t)off, strerror(errno)); } tsf->roff = ooff; return 0; } /* Special case - EOF */ if (r == sizeof(size_t) || tsf->roff > tsf->size) { if (tsf->rfd >= 0) close(tsf->rfd); tsf->rfd = -1; pthread_mutex_lock(&ts->rdwr_mutex); *cur_file = timeshift_filemgr_next(tsf, NULL, 0); pthread_mutex_unlock(&ts->rdwr_mutex); tsf->roff = 0; // reset *wait = 0; /* Check SMT_START index */ } else { streaming_message_t *ssm = _timeshift_find_sstart(*cur_file, (*sm)->sm_time); if (ssm && ssm->sm_data != ts->smt_start) { streaming_target_deliver2(ts->output, streaming_msg_clone(ssm)); if (ts->smt_start) streaming_start_unref(ts->smt_start); ts->smt_start = ssm->sm_data; atomic_add(&ts->smt_start->ss_refcount, 1); } } } return 0; }
/** * Filename generator * * - convert from utf8 * - avoid duplicate filenames * */ static int pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss) { char fullname[PATH_MAX]; char path[PATH_MAX]; int tally = 0; struct stat st; char *filename, *s; struct tm tm; dvr_config_t *cfg; if (de == NULL) return -1; cfg = de->de_config; strncpy(path, cfg->dvr_storage, sizeof(path)); path[sizeof(path)-1] = '\0'; /* Remove trailing slash */ if (path[strlen(path)-1] == '/') path[strlen(path)-1] = '\0'; /* Append per-day directory */ if (cfg->dvr_dir_per_day) { localtime_r(&de->de_start, &tm); strftime(fullname, sizeof(fullname), "%F", &tm); s = cleanup_filename(fullname, cfg); if (s == NULL) return -1; snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s); free(s); } /* Append per-channel directory */ if (cfg->dvr_channel_dir) { char *chname = strdup(DVR_CH_NAME(de)); s = cleanup_filename(chname, cfg); free(chname); if (s == NULL) return -1; snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s); free(s); } // TODO: per-brand, per-season /* Append per-title directory */ if (cfg->dvr_title_dir) { char *title = strdup(lang_str_get(de->de_title, NULL)); s = cleanup_filename(title, cfg); free(title); if (s == NULL) return -1; snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s); free(s); } if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0) return -1; /* Construct final name */ dvr_make_title(fullname, sizeof(fullname), de); filename = cleanup_filename(fullname, cfg); if (filename == NULL) return -1; snprintf(fullname, sizeof(fullname), "%s/%s.%s", path, filename, muxer_suffix(de->de_chain->prch_muxer, ss)); while(1) { if(stat(fullname, &st) == -1) { tvhlog(LOG_DEBUG, "dvr", "File \"%s\" -- %s -- Using for recording", fullname, strerror(errno)); break; } tvhlog(LOG_DEBUG, "dvr", "Overwrite protection, file \"%s\" exists", fullname); tally++; snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s", path, filename, tally, muxer_suffix(de->de_chain->prch_muxer, ss)); } free(filename); tvh_str_set(&de->de_filename, fullname); return 0; }
/* * Get current / new file */ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) { int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; timeshift_index_data_t *ti; char path[512]; time_t time; /* Return last file */ if (!create) return timeshift_filemgr_newest(ts); /* No space */ if (ts->full) return NULL; /* Store to file */ clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); time = tp.tv_sec / TIMESHIFT_FILE_PERIOD; tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); if (!tsf_tl || tsf_tl->time != time) { tsf_hd = TAILQ_FIRST(&ts->files); /* Close existing */ if (tsf_tl && tsf_tl->fd != -1) timeshift_filemgr_close(tsf_tl); /* Check period */ if (ts->max_time && tsf_hd && tsf_tl) { time_t d = (tsf_tl->time - tsf_hd->time) * TIMESHIFT_FILE_PERIOD; if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); tsf_hd = NULL; } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } } /* Check size */ if (!timeshift_unlimited_size && atomic_pre_add_u64(×hift_total_size, 0) >= timeshift_max_size) { /* Remove the last file (if we can) */ if (tsf_hd && !tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); /* Full */ } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } /* Create new file */ tsf_tmp = NULL; if (!ts->full) { /* Create directories */ if (!ts->path) { if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path))) return NULL; ts->path = strdup(path); } /* Create File */ snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time); tvhtrace("timeshift", "ts %d create file %s", ts->id, path); if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = calloc(1, sizeof(timeshift_file_t)); tsf_tmp->time = time; tsf_tmp->fd = fd; tsf_tmp->path = strdup(path); tsf_tmp->refcount = 0; tsf_tmp->last = getmonoclock(); TAILQ_INIT(&tsf_tmp->iframes); TAILQ_INIT(&tsf_tmp->sstart); TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { tvhtrace("timeshift", "ts %d copy smt_start to new file", ts->id); timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } } } tsf_tl = tsf_tmp; } if (tsf_tl) tsf_tl->refcount++; return tsf_tl; }
int main(int argc, char **argv) { int i; sigset_t set; #if ENABLE_LINUXDVB uint32_t adapter_mask; #endif /* Defaults */ log_stderr = 1; log_decorate = isatty(2); log_debug_to_syslog = 0; log_debug_to_console = 0; log_debug_to_path = 0; log_path = NULL; 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_debug = 0, opt_syslog = 0, opt_uidebug = 0, opt_abort = 0, opt_noacl = 0, opt_ipv6 = 0; const char *opt_config = NULL, *opt_user = NULL, *opt_group = NULL, *opt_pidpath = "/var/run/tvheadend.pid", #if ENABLE_LINUXDVB *opt_dvb_adapters = NULL, *opt_dvb_raw = NULL, #endif *opt_rawts = NULL, *opt_subscribe = NULL; 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 useraccount exist 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", "Use only specified DVB adapters", OPT_STR, &opt_dvb_adapters }, #endif { 0, NULL, "Server Connectivity", OPT_BOOL, NULL }, { '6', "ipv6", "Listen on IPv6", OPT_BOOL, &opt_ipv6 }, { 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', "debug", "Enable all debug", OPT_BOOL, &opt_debug }, { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, { 0, "uidebug", "Enable webUI debug", OPT_BOOL, &opt_uidebug }, { 'l', "log", "Log to file", OPT_STR, &log_path }, { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, { 0, "noacl", "Disable all access control checks", OPT_BOOL, &opt_noacl }, #if ENABLE_LINUXDVB { 'R', "dvbraw", "Use rawts file to create virtual adapter", OPT_STR, &opt_dvb_raw }, #endif { 'r', "rawts", "Use rawts file to generate virtual services", OPT_STR, &opt_rawts }, { 'j', "join", "Subscribe to a service permanently", OPT_STR, &opt_subscribe } }; /* Get current directory */ tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); /* Set locale */ setlocale(LC_ALL, ""); /* 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 *((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 */ log_debug_to_console = opt_debug; log_debug_to_syslog = opt_syslog; log_debug_to_path = opt_debug; tvheadend_webui_debug = opt_debug || opt_uidebug; tvhlog(LOG_INFO, "START", "initialising"); #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; } else { char *p, *r, *e; adapter_mask = 0x0; p = strtok_r((char*)opt_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); return 1; } adapter_mask |= (1 << a); p = strtok_r(NULL, ",", &r); } 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)+1); *tmp = '/'; strcpy(tmp+1, tvheadend_webroot); } if (tmp[strlen(tmp)-1] == '/') tmp[strlen(tmp)-1] = '\0'; tvheadend_webroot = tmp; } 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); } umask(0); } /* Setup logging */ log_stderr = !opt_fork; log_decorate = isatty(2); openlog("tvheadend", LOG_PID, LOG_DAEMON); /* Initialise configuration */ hts_settings_init(opt_config); /* Setup global mutexes */ pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_lock(&global_lock); time(&dispatch_clock); /* Signal handling */ sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); trap_init(argv[0]); /** * Initialize subsystems */ #if ENABLE_LIBAV libav_init(); #endif config_init(); imagecache_init(); service_init(); channels_init(); subscription_init(); access_init(opt_firstrun, opt_noacl); #if ENABLE_LINUXDVB muxes_init(); dvb_init(adapter_mask, opt_dvb_raw); #endif iptv_input_init(); #if ENABLE_V4L v4l_init(); #endif #if ENABLE_TIMESHIFT timeshift_init(); #endif tcp_server_init(opt_ipv6); http_server_init(); webui_init(); serviceprobe_init(); #if ENABLE_CWC cwc_init(); capmt_init(); #if (!ENABLE_DVBCSA) ffdecsa_init(); #endif #endif epggrab_init(); epg_init(); dvr_init(); htsp_init(); if(opt_rawts != NULL) rawts_init(opt_rawts); if(opt_subscribe != NULL) subscription_dummy_join(opt_subscribe, 1); #ifdef CONFIG_AVAHI avahi_init(); #endif 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 */ running = 1; 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, settings located in '%s'", tvheadend_version, getpid(), getuid(), getgid(), hts_settings_get_root()); if(opt_abort) abort(); mainloop(); epg_save(); #if ENABLE_TIMESHIFT timeshift_term(); #endif tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); if(opt_fork) unlink(opt_pidpath); return 0; }
static int dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) { const source_info_t *si = &ss->ss_si; const streaming_start_component_t *ssc; int i; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); de->de_mux = muxer_create(de->de_mc); if(!de->de_mux) { dvr_rec_fatal_error(de, "Unable to create muxer"); return -1; } if(pvr_generate_filename(de, ss) != 0) { dvr_rec_fatal_error(de, "Unable to create directories"); return -1; } if(muxer_open_file(de->de_mux, de->de_filename)) { dvr_rec_fatal_error(de, "Unable to open file"); return -1; } if(muxer_init(de->de_mux, ss, lang_str_get(de->de_title, NULL))) { dvr_rec_fatal_error(de, "Unable to init file"); return -1; } if(cfg->dvr_flags & DVR_TAG_FILES && de->de_bcast) { if(muxer_write_meta(de->de_mux, de->de_bcast)) { dvr_rec_fatal_error(de, "Unable to write meta data"); return -1; } } tvhlog(LOG_INFO, "dvr", "%s from " "adapter: \"%s\", " "network: \"%s\", mux: \"%s\", provider: \"%s\", " "service: \"%s\"", de->de_filename ?: lang_str_get(de->de_title, NULL), si->si_adapter ?: "<N/A>", si->si_network ?: "<N/A>", si->si_mux ?: "<N/A>", si->si_provider ?: "<N/A>", si->si_service ?: "<N/A>"); tvhlog(LOG_INFO, "dvr", " # %-16s %-4s %-10s %-12s %-11s %-8s", "type", "lang", "resolution", "aspect ratio", "sample rate", "channels"); for(i = 0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; char res[11]; char asp[6]; char sr[6]; char ch[7]; if(SCT_ISAUDIO(ssc->ssc_type)) { if(ssc->ssc_sri) snprintf(sr, sizeof(sr), "%d", sri_to_rate(ssc->ssc_sri)); else strcpy(sr, "?"); if(ssc->ssc_channels == 6) snprintf(ch, sizeof(ch), "5.1"); else if(ssc->ssc_channels == 0) strcpy(ch, "?"); else snprintf(ch, sizeof(ch), "%d", ssc->ssc_channels); } else { sr[0] = 0; ch[0] = 0; } if(SCT_ISVIDEO(ssc->ssc_type)) { if(ssc->ssc_width && ssc->ssc_height) snprintf(res, sizeof(res), "%dx%d", ssc->ssc_width, ssc->ssc_height); else strcpy(res, "?"); } else { res[0] = 0; } if(SCT_ISVIDEO(ssc->ssc_type)) { if(ssc->ssc_aspect_num && ssc->ssc_aspect_den) snprintf(asp, sizeof(asp), "%d:%d", ssc->ssc_aspect_num, ssc->ssc_aspect_den); else strcpy(asp, "?"); } else { asp[0] = 0; } tvhlog(LOG_INFO, "dvr", "%2d %-16s %-4s %-10s %-12s %-11s %-8s %s", ssc->ssc_index, streaming_component_type2txt(ssc->ssc_type), ssc->ssc_lang, res, asp, sr, ch, ssc->ssc_disabled ? "<disabled, no valid input>" : ""); } return 0; }
static void normalize_ts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) { int64_t dts, d; int checkts = SCT_ISAUDIO(tfs->tfs_type) || SCT_ISVIDEO(tfs->tfs_type); if(tf->tf_tsref == PTS_UNSET) { pkt_ref_dec(pkt); return; } pkt->pkt_dts &= PTS_MASK; pkt->pkt_pts &= PTS_MASK; /* Subtract the transport wide start offset */ dts = pkt->pkt_dts - tf->tf_tsref; if(tfs->tfs_last_dts_norm == PTS_UNSET) { if(dts < 0) { /* Early packet with negative time stamp, drop those */ pkt_ref_dec(pkt); return; } } else if(checkts) { d = dts + tfs->tfs_dts_epoch - tfs->tfs_last_dts_norm; if(d < 0 || d > 90000) { if(d < -PTS_MASK || d > -PTS_MASK + 180000) { tfs->tfs_bad_dts++; if(tfs->tfs_bad_dts < 5) { tvhlog(LOG_ERR, "parser", "transport stream %s, DTS discontinuity. " "DTS = %" PRId64 ", last = %" PRId64, streaming_component_type2txt(tfs->tfs_type), dts, tfs->tfs_last_dts_norm); } } else { /* DTS wrapped, increase upper bits */ tfs->tfs_dts_epoch += PTS_MASK + 1; tfs->tfs_bad_dts = 0; } } else { tfs->tfs_bad_dts = 0; } } dts += tfs->tfs_dts_epoch; tfs->tfs_last_dts_norm = dts; if(pkt->pkt_pts != PTS_UNSET) { /* Compute delta between PTS and DTS (and watch out for 33 bit wrap) */ int64_t ptsoff = (pkt->pkt_pts - pkt->pkt_dts) & PTS_MASK; pkt->pkt_pts = dts + ptsoff; } pkt->pkt_dts = dts; tsfixprintf("TSFIX: %-12s %d %10"PRId64" %10"PRId64" %10d %zd\n", streaming_component_type2txt(tfs->tfs_type), pkt->pkt_frametype, pkt->pkt_dts, pkt->pkt_pts, pkt->pkt_duration, pktbuf_len(pkt->pkt_payload)); streaming_message_t *sm = streaming_msg_create_pkt(pkt); streaming_target_deliver2(tf->tf_output, sm); pkt_ref_dec(pkt); }
static void _xmltv_load_grabbers ( void ) { int outlen; size_t i, p, n; char *outbuf; char name[1000]; char *tmp, *tmp2 = NULL, *path; /* Load data */ outlen = spawn_and_store_stdout(XMLTV_FIND, NULL, &outbuf); /* Process */ if ( outlen > 0 ) { p = n = i = 0; while ( i < outlen ) { if ( outbuf[i] == '\n' || outbuf[i] == '\0' ) { outbuf[i] = '\0'; sprintf(name, "XMLTV: %s", &outbuf[n]); epggrab_module_int_create(NULL, &outbuf[p], name, 3, &outbuf[p], NULL, _xmltv_parse, NULL, NULL); p = n = i + 1; } else if ( outbuf[i] == '|' ) { outbuf[i] = '\0'; n = i + 1; } i++; } free(outbuf); /* Internal search */ } else if ((tmp = getenv("PATH"))) { tvhlog(LOG_DEBUG, "epggrab", "using internal grab search"); char bin[256]; char desc[] = "--description"; char *argv[] = { NULL, desc, NULL }; path = strdup(tmp); tmp = strtok_r(path, ":", &tmp2); while (tmp) { DIR *dir; struct dirent *de; struct stat st; if ((dir = opendir(tmp))) { while ((de = readdir(dir))) { if (strstr(de->d_name, XMLTV_GRAB) != de->d_name) continue; snprintf(bin, sizeof(bin), "%s/%s", tmp, de->d_name); if (epggrab_module_find_by_id(bin)) continue; if (stat(bin, &st)) continue; if (!(st.st_mode & S_IEXEC)) continue; if (!S_ISREG(st.st_mode)) continue; if ((outlen = spawn_and_store_stdout(bin, argv, &outbuf)) > 0) { if (outbuf[outlen-1] == '\n') outbuf[outlen-1] = '\0'; snprintf(name, sizeof(name), "XMLTV: %s", outbuf); epggrab_module_int_create(NULL, bin, name, 3, bin, NULL, _xmltv_parse, NULL, NULL); free(outbuf); } } closedir(dir); } tmp = strtok_r(NULL, ":", &tmp2); } free(path); } }
/* * Timeshift thread */ void *timeshift_reader ( void *p ) { timeshift_t *ts = p; int nfds, end, run = 1, wait = -1; timeshift_file_t *cur_file = NULL; int cur_speed = 100, keyframe_mode = 0; int64_t pause_time = 0, play_time = 0, last_time = 0; int64_t now, deliver, skip_time = 0; streaming_message_t *sm = NULL, *ctrl = NULL; timeshift_index_iframe_t *tsi = NULL; streaming_skip_t *skip = NULL; time_t last_status = 0; tvhpoll_t *pd; tvhpoll_event_t ev = { 0 }; pd = tvhpoll_create(1); ev.fd = ts->rd_pipe.rd; ev.events = TVHPOLL_IN; tvhpoll_add(pd, &ev, 1); /* Output */ while (run) { // Note: Previously we allowed unlimited wait, but we now must wake periodically // to output status message if (wait < 0 || wait > 1000) wait = 1000; /* Wait for data */ if(wait) nfds = tvhpoll_wait(pd, &ev, 1, wait); else nfds = 0; wait = -1; end = 0; skip = NULL; now = getmonoclock(); /* Control */ pthread_mutex_lock(&ts->state_mutex); if (nfds == 1) { if (_read_msg(NULL, ts->rd_pipe.rd, &ctrl) > 0) { /* Exit */ if (ctrl->sm_type == SMT_EXIT) { tvhtrace("timeshift", "ts %d read exit request", ts->id); run = 0; streaming_msg_free(ctrl); ctrl = NULL; /* Speed */ } else if (ctrl->sm_type == SMT_SPEED) { int speed = ctrl->sm_code; int keyframe; /* Bound it */ if (speed > 3200) speed = 3200; if (speed < -3200) speed = -3200; /* Ignore negative */ if (ts->ondemand && (speed < 0)) speed = cur_file ? speed : 0; /* Process */ if (cur_speed != speed) { /* Live playback */ if (ts->state == TS_LIVE) { /* Reject */ if (speed >= 100) { tvhlog(LOG_DEBUG, "timeshift", "ts %d reject 1x+ in live mode", ts->id); speed = 100; /* Set position */ } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d enter timeshift mode", ts->id); timeshift_writer_flush(ts); pthread_mutex_lock(&ts->rdwr_mutex); if ((cur_file = timeshift_filemgr_get(ts, 1))) { cur_file->roff = cur_file->size; pause_time = cur_file->last; last_time = pause_time; } pthread_mutex_unlock(&ts->rdwr_mutex); } /* Buffer playback */ } else if (ts->state == TS_PLAY) { pause_time = last_time; /* Paused */ } else { } /* Check keyframe mode */ keyframe = (speed < 0) || (speed > 400); if (keyframe != keyframe_mode) { tvhlog(LOG_DEBUG, "timeshift", "using keyframe mode? %s", keyframe ? "yes" : "no"); keyframe_mode = keyframe; if (keyframe) { tsi = NULL; } } /* Update */ play_time = getmonoclock(); cur_speed = speed; if (speed != 100 || ts->state != TS_LIVE) ts->state = speed == 0 ? TS_PAUSE : TS_PLAY; tvhlog(LOG_DEBUG, "timeshift", "ts %d change speed %d", ts->id, speed); } /* Send on the message */ ctrl->sm_code = speed; streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; /* Skip/Seek */ } else if (ctrl->sm_type == SMT_SKIP) { skip = ctrl->sm_data; switch (skip->type) { case SMT_SKIP_LIVE: if (ts->state != TS_LIVE) { /* Reset */ if (ts->full) { pthread_mutex_lock(&ts->rdwr_mutex); timeshift_filemgr_flush(ts, NULL); ts->full = 0; pthread_mutex_unlock(&ts->rdwr_mutex); } /* Release */ if (sm) streaming_msg_free(sm); /* Find end */ skip_time = 0x7fffffffffffffffLL; // TODO: change this sometime! } break; case SMT_SKIP_ABS_TIME: if (ts->pts_delta == 0) { tvhlog(LOG_ERR, "timeshift", "ts %d abs skip not possible no PTS delta", ts->id); skip = NULL; break; } /* -fallthrough */ case SMT_SKIP_REL_TIME: /* Convert */ skip_time = ts_rescale(skip->time, 1000000); tvhlog(LOG_DEBUG, "timeshift", "ts %d skip %"PRId64" requested %"PRId64, ts->id, skip_time, skip->time); /* Live playback (stage1) */ if (ts->state == TS_LIVE) { pthread_mutex_lock(&ts->rdwr_mutex); if ((cur_file = timeshift_filemgr_get(ts, !ts->ondemand))) { cur_file->roff = cur_file->size; last_time = cur_file->last; } else { tvhlog(LOG_ERR, "timeshift", "ts %d failed to get current file", ts->id); skip = NULL; } pthread_mutex_unlock(&ts->rdwr_mutex); } /* May have failed */ if (skip) { skip_time += (skip->type == SMT_SKIP_ABS_TIME) ? ts->pts_delta : last_time; tvhlog(LOG_DEBUG, "timeshift", "ts %d skip last_time %"PRId64" pts_delta %"PRId64, ts->id, skip_time - ts->pts_delta, ts->pts_delta); /* Live (stage2) */ if (ts->state == TS_LIVE) { if (skip_time >= now) { tvhlog(LOG_DEBUG, "timeshift", "ts %d skip ignored, already live", ts->id); skip = NULL; } else { ts->state = TS_PLAY; } } } /* OK */ if (skip) { /* Adjust time */ play_time = now; pause_time = skip_time; tsi = NULL; /* Clear existing packet */ if (sm) streaming_msg_free(sm); sm = NULL; } break; default: tvhlog(LOG_ERR, "timeshift", "ts %d invalid/unsupported skip type: %d", ts->id, skip->type); skip = NULL; break; } /* Error */ if (!skip) { ((streaming_skip_t*)ctrl->sm_data)->type = SMT_SKIP_ERROR; streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; } /* Ignore */ } else { streaming_msg_free(ctrl); ctrl = NULL; } } } /* Status message */ if (now >= (last_status + 1000000)) { streaming_message_t *tsm; timeshift_status_t *status; timeshift_index_iframe_t *fst, *lst; status = calloc(1, sizeof(timeshift_status_t)); fst = _timeshift_first_frame(ts); lst = _timeshift_last_frame(ts); status->full = ts->full; status->shift = ts->state <= TS_LIVE ? 0 : ts_rescale_i(now - last_time, 1000000); if (lst && fst && lst != fst && ts->pts_delta != PTS_UNSET) { status->pts_start = ts_rescale_i(fst->time - ts->pts_delta, 1000000); status->pts_end = ts_rescale_i(lst->time - ts->pts_delta, 1000000); } else { status->pts_start = PTS_UNSET; status->pts_end = PTS_UNSET; } tsm = streaming_msg_create_data(SMT_TIMESHIFT_STATUS, status); streaming_target_deliver2(ts->output, tsm); last_status = now; } /* Done */ if (!run || !cur_file || ((ts->state != TS_PLAY && !skip))) { pthread_mutex_unlock(&ts->state_mutex); continue; } /* Calculate delivery time */ deliver = (now - play_time) + TIMESHIFT_PLAY_BUF; deliver = (deliver * cur_speed) / 100; deliver = (deliver + pause_time); /* Determine next packet */ if (!sm) { /* Rewind or Fast forward (i-frame only) */ if (skip || keyframe_mode) { timeshift_file_t *tsf = NULL; int64_t req_time; /* Time */ if (!skip) req_time = last_time + ((cur_speed < 0) ? -1 : 1); else req_time = skip_time; tvhlog(LOG_DEBUG, "timeshift", "ts %d skip to %"PRId64" from %"PRId64, ts->id, req_time - ts->pts_delta, last_time - ts->pts_delta); /* Find */ pthread_mutex_lock(&ts->rdwr_mutex); end = _timeshift_skip(ts, req_time, last_time, cur_file, &tsf, &tsi); pthread_mutex_unlock(&ts->rdwr_mutex); if (tsi) tvhlog(LOG_DEBUG, "timeshift", "ts %d skip found pkt @ %"PRId64, ts->id, tsi->time - ts->pts_delta); /* File changed (close) */ if ((tsf != cur_file) && cur_file && cur_file->rfd >= 0) { close(cur_file->rfd); cur_file->rfd = -1; } /* Position */ if (cur_file) cur_file->refcount--; if ((cur_file = tsf) != NULL) { if (tsi) cur_file->roff = tsi->pos; else cur_file->roff = 0; } } /* Find packet */ if (_timeshift_read(ts, &cur_file, &sm, &wait) == -1) { pthread_mutex_unlock(&ts->state_mutex); break; } } /* Send skip response */ if (skip) { if (sm && sm->sm_type == SMT_PACKET) { th_pkt_t *pkt = sm->sm_data; skip->time = pkt->pkt_pts; skip->type = SMT_SKIP_ABS_TIME; tvhlog(LOG_DEBUG, "timeshift", "ts %d skip to pts %"PRId64" ok, time %"PRId64, ts->id, ts_rescale(skip->time, 1000000), sm->sm_time - ts->pts_delta); } else { /* Report error */ skip->type = SMT_SKIP_ERROR; skip = NULL; tvhlog(LOG_DEBUG, "timeshift", "ts %d skip failed (%d)", ts->id, sm ? sm->sm_type : -1); } streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; } /* Deliver */ if (sm && (skip || (((cur_speed < 0) && (sm->sm_time >= deliver)) || ((cur_speed > 0) && (sm->sm_time <= deliver))))) { if (sm->sm_type == SMT_PACKET && tvhtrace_enabled()) { th_pkt_t *pkt = sm->sm_data; tvhtrace("timeshift", "ts %d pkt out - stream %d type %c pts %10"PRId64 " dts %10"PRId64 " dur %10d len %6zu time %14"PRItime_t, ts->id, pkt->pkt_componentindex, pkt_frametype_to_char(pkt->pkt_frametype), ts_rescale(pkt->pkt_pts, 1000000), ts_rescale(pkt->pkt_dts, 1000000), pkt->pkt_duration, pktbuf_len(pkt->pkt_payload), sm->sm_time - ts->pts_delta); } last_time = sm->sm_time; streaming_target_deliver2(ts->output, sm); sm = NULL; wait = 0; } else if (sm) { if (cur_speed > 0) wait = (sm->sm_time - deliver) / 1000; else wait = (deliver - sm->sm_time) / 1000; if (wait == 0) wait = 1; tvhtrace("timeshift", "ts %d wait %d", ts->id, wait); } /* Terminate */ if (!cur_file || end != 0) { if (!end) end = (cur_speed > 0) ? 1 : -1; /* Back to live (unless buffer is full) */ if (end == 1 && !ts->full) { tvhlog(LOG_DEBUG, "timeshift", "ts %d eob revert to live mode", ts->id); ts->state = TS_LIVE; cur_speed = 100; ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; /* Flush timeshift buffer to live */ if (_timeshift_flush_to_live(ts, &cur_file, &sm, &wait) == -1) break; /* Close file (if open) */ if (cur_file && cur_file->rfd >= 0) { close(cur_file->rfd); cur_file->rfd = -1; } /* Flush ALL files */ if (ts->ondemand) timeshift_filemgr_flush(ts, NULL); /* Pause */ } else { if (cur_speed <= 0) { cur_speed = 0; ts->state = TS_PAUSE; } else { cur_speed = 100; ts->state = TS_PLAY; play_time = now; } tvhlog(LOG_DEBUG, "timeshift", "ts %d sob speed %d", ts->id, cur_speed); pause_time = last_time; ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; } /* Flush unwanted */ } else if (ts->ondemand && cur_file) { pthread_mutex_lock(&ts->rdwr_mutex); timeshift_filemgr_flush(ts, cur_file); pthread_mutex_unlock(&ts->rdwr_mutex); } pthread_mutex_unlock(&ts->state_mutex); } /* Cleanup */ tvhpoll_destroy(pd); if (cur_file && cur_file->rfd >= 0) { close(cur_file->rfd); cur_file->rfd = -1; } if (sm) streaming_msg_free(sm); if (ctrl) streaming_msg_free(ctrl); tvhtrace("timeshift", "ts %d exit reader thread", ts->id); return NULL; }
/* * Output packet */ static int _timeshift_read ( timeshift_t *ts, timeshift_file_t **cur_file, off_t *cur_off, int *fd, streaming_message_t **sm, int *wait ) { if (*cur_file) { /* Open file */ if (*fd == -1) { #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d open file %s", ts->id, (*cur_file)->path); #endif *fd = open((*cur_file)->path, O_RDONLY); } if (*cur_off) { #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d seek to %lu", ts->id, *cur_off); #endif lseek(*fd, *cur_off, SEEK_SET); } /* Read msg */ ssize_t r = _read_msg(*fd, sm); if (r < 0) { streaming_message_t *e = streaming_msg_create_code(SMT_STOP, SM_CODE_UNDEFINED_ERROR); streaming_target_deliver2(ts->output, e); tvhlog(LOG_ERR, "timeshift", "ts %d could not read buffer", ts->id); return -1; } #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d read msg %p (%ld)", ts->id, *sm, r); #endif /* Incomplete */ if (r == 0) { lseek(*fd, *cur_off, SEEK_SET); return 0; } /* Update */ *cur_off += r; /* Special case - EOF */ if (r == sizeof(size_t) || *cur_off > (*cur_file)->size) { close(*fd); *fd = -1; pthread_mutex_lock(&ts->rdwr_mutex); *cur_file = timeshift_filemgr_next(*cur_file, NULL, 0); pthread_mutex_unlock(&ts->rdwr_mutex); *cur_off = 0; // reset *wait = 0; /* Check SMT_START index */ } else { streaming_message_t *ssm = _timeshift_find_sstart(*cur_file, (*sm)->sm_time); if (ssm && ssm->sm_data != ts->smt_start) { streaming_target_deliver2(ts->output, streaming_msg_clone(ssm)); if (ts->smt_start) streaming_start_unref(ts->smt_start); ts->smt_start = ssm->sm_data; atomic_add(&ts->smt_start->ss_refcount, 1); } } } return 0; }
htsmsg_t * dvb_mux_preconf_get_node(int fetype, const char *node) { const struct region *r; const struct network *n; int nr, nn, i; htsmsg_t *out, *e; switch(fetype) { case FE_QAM: r = regions_DVBC; nr = sizeof(regions_DVBC) / sizeof(regions_DVBC[0]); break; case FE_QPSK: r = regions_DVBS; nr = sizeof(regions_DVBS) / sizeof(regions_DVBS[0]); break; case FE_OFDM: r = regions_DVBT; nr = sizeof(regions_DVBT) / sizeof(regions_DVBT[0]); break; case FE_ATSC: r = regions_ATSC; nr = sizeof(regions_ATSC) / sizeof(regions_ATSC[0]); break; default: tvhlog(LOG_ERR, "DVB", "No built-in config for fetype %d", fetype); return NULL; } out = htsmsg_create_list(); if(!strcmp(node, "root")) { for(i = 0; i < nr; i++) { e = htsmsg_create_map(); htsmsg_add_u32(e, "leaf", 0); htsmsg_add_str(e, "text", r[i].name); htsmsg_add_str(e, "id", r[i].name); htsmsg_add_msg(out, NULL, e); } return out; } for(i = 0; i < nr; i++) if(!strcmp(node, r[i].name)) break; if(i == nr) return out; n = r[i].networks; nn = r[i].nnetworks; for(i = 0; i < nn; i++) { e = htsmsg_create_map(); htsmsg_add_u32(e, "leaf", 1); htsmsg_add_str(e, "text", n[i].name); htsmsg_add_str(e, "id", n[i].name); htsmsg_add_msg(out, NULL, e); } return out; }
static void satip_discovery_http_closed(http_client_t *hc, int errn) { satip_discovery_t *d = hc->hc_aux; char *s; htsmsg_t *xml = NULL, *tags, *root, *device; const char *friendlyname, *manufacturer, *manufacturerURL, *modeldesc; const char *modelname, *modelnum, *serialnum; const char *presentation, *tunercfg, *udn, *uuid; const char *cs, *arg; satip_device_info_t info; char errbuf[100]; char *argv[10]; int i, n; s = http_arg_get(&hc->hc_args, "Content-Type"); if (s) { n = http_tokenize(s, argv, ARRAY_SIZE(argv), ';'); if (n <= 0 || strcasecmp(s, "text/xml")) { errn = ENOENT; s = NULL; } } if (errn != 0 || s == NULL || hc->hc_code != 200 || hc->hc_data_size == 0 || hc->hc_data == NULL) { tvhlog(LOG_ERR, "satip", "Cannot get %s: %s", d->location, strerror(errn)); return; } if (tvhtrace_enabled()) { tvhtrace("satip", "received XML description from %s", hc->hc_host); tvhlog_hexdump("satip", hc->hc_data, hc->hc_data_size); } if (d->myaddr == NULL || d->myaddr[0] == '\0') { struct sockaddr_storage ip; socklen_t addrlen = sizeof(ip); errbuf[0] = '\0'; getsockname(hc->hc_fd, (struct sockaddr *)&ip, &addrlen); inet_ntop(ip.ss_family, IP_IN_ADDR(ip), errbuf, sizeof(errbuf)); free(d->myaddr); d->myaddr = strdup(errbuf); } s = hc->hc_data + hc->hc_data_size - 1; while (s != hc->hc_data && *s != '/') s--; if (s != hc->hc_data) s--; if (strncmp(s, "</root>", 7)) return; /* Parse */ xml = htsmsg_xml_deserialize(hc->hc_data, errbuf, sizeof(errbuf)); hc->hc_data = NULL; if (!xml) { tvhlog(LOG_ERR, "satip_discovery_desc", "htsmsg_xml_deserialize error %s", errbuf); goto finish; } if ((tags = htsmsg_get_map(xml, "tags")) == NULL) goto finish; if ((root = htsmsg_get_map(tags, "root")) == NULL) goto finish; if ((device = htsmsg_get_map(root, "tags")) == NULL) goto finish; if ((device = htsmsg_get_map(device, "device")) == NULL) goto finish; if ((device = htsmsg_get_map(device, "tags")) == NULL) goto finish; if ((cs = htsmsg_xml_get_cdata_str(device, "deviceType")) == NULL) goto finish; if (strcmp(cs, "urn:ses-com:device:SatIPServer:1")) goto finish; if ((friendlyname = htsmsg_xml_get_cdata_str(device, "friendlyName")) == NULL) goto finish; if ((manufacturer = htsmsg_xml_get_cdata_str(device, "manufacturer")) == NULL) goto finish; if ((manufacturerURL = htsmsg_xml_get_cdata_str(device, "manufacturerURL")) == NULL) manufacturerURL = ""; if ((modeldesc = htsmsg_xml_get_cdata_str(device, "modelDescription")) == NULL) modeldesc = ""; if ((modelname = htsmsg_xml_get_cdata_str(device, "modelName")) == NULL) goto finish; if ((modelnum = htsmsg_xml_get_cdata_str(device, "modelNumber")) == NULL) modelnum = ""; if ((serialnum = htsmsg_xml_get_cdata_str(device, "serialNumber")) == NULL) serialnum = ""; if ((presentation = htsmsg_xml_get_cdata_str(device, "presentationURL")) == NULL) presentation = ""; if ((udn = htsmsg_xml_get_cdata_str(device, "UDN")) == NULL) goto finish; if ((tunercfg = htsmsg_xml_get_cdata_str(device, "urn:ses-com:satipX_SATIPCAP")) == NULL) tunercfg = ""; uuid = NULL; n = http_tokenize((char *)udn, argv, ARRAY_SIZE(argv), ':'); for (i = 0; i < n+1; i++) if (argv[i] && strcmp(argv[i], "uuid") == 0) { uuid = argv[++i]; break; } if (uuid == NULL || (d->uuid[0] && strcmp(uuid, d->uuid))) goto finish; info.rtsp_port = 554; info.srcs = 4; arg = http_arg_get(&hc->hc_args, "X-SATIP-RTSP-Port"); if (arg) { i = atoi(arg); if (i > 0 && i < 65535) info.rtsp_port = i; } arg = http_arg_get(&hc->hc_args, "X-SATIP-Sources"); if (arg) { i = atoi(arg); if (i > 0 && i < 128) info.srcs = i; } info.myaddr = strdup(d->myaddr); info.addr = strdup(d->url.host); info.uuid = strdup(uuid); info.bootid = strdup(d->bootid); info.configid = strdup(d->configid); info.deviceid = strdup(d->deviceid); info.location = strdup(d->location); info.server = strdup(d->server); info.friendlyname = strdup(friendlyname); info.manufacturer = strdup(manufacturer); info.manufacturerURL = strdup(manufacturerURL); info.modeldesc = strdup(modeldesc); info.modelname = strdup(modelname); info.modelnum = strdup(modelnum); info.serialnum = strdup(serialnum); info.presentation = strdup(presentation); info.tunercfg = strdup(tunercfg); htsmsg_destroy(xml); xml = NULL; pthread_mutex_lock(&global_lock); if (!satip_device_find(info.uuid)) satip_device_create(&info); pthread_mutex_unlock(&global_lock); free(info.myaddr); free(info.location); free(info.server); free(info.addr); free(info.uuid); free(info.bootid); free(info.configid); free(info.deviceid); free(info.friendlyname); free(info.manufacturer); free(info.manufacturerURL); free(info.modeldesc); free(info.modelname); free(info.modelnum); free(info.serialnum); free(info.presentation); free(info.tunercfg); finish: htsmsg_destroy(xml); }
static satip_device_t * satip_device_create( satip_device_info_t *info ) { satip_device_t *sd = calloc(1, sizeof(satip_device_t)); tvh_uuid_t uuid; htsmsg_t *conf = NULL, *feconf = NULL; char *argv[10], *tunercfg; int i, j, n, m, fenum, v2, save = 0; dvb_fe_type_t type; char buf2[60]; sd->sd_inload = 1; satip_device_calc_uuid(&uuid, info->uuid); conf = hts_settings_load("input/satip/adapters/%s", uuid.hex); /* some sane defaults */ sd->sd_fast_switch = 1; sd->sd_fullmux_ok = 1; sd->sd_pids_len = 127; sd->sd_pids_max = 32; sd->sd_pids_deladd = 1; sd->sd_sig_scale = 240; sd->sd_dbus_allow = 1; if (!tvh_hardware_create0((tvh_hardware_t*)sd, &satip_device_class, uuid.hex, conf)) { /* Note: sd is freed in above fcn */ return NULL; } pthread_mutex_init(&sd->sd_tune_mutex, NULL); TAILQ_INIT(&sd->sd_frontends); /* we may check if uuid matches, but the SHA hash should be enough */ if (sd->sd_info.uuid) free(sd->sd_info.uuid); #define ASSIGN(x) sd->sd_info.x = info->x; info->x = NULL ASSIGN(myaddr); ASSIGN(addr); ASSIGN(uuid); ASSIGN(bootid); ASSIGN(configid); ASSIGN(deviceid); ASSIGN(server); ASSIGN(location); ASSIGN(friendlyname); ASSIGN(manufacturer); ASSIGN(manufacturerURL); ASSIGN(modeldesc); ASSIGN(modelname); ASSIGN(modelnum); ASSIGN(serialnum); ASSIGN(presentation); ASSIGN(tunercfg); #undef ASSIGN sd->sd_info.rtsp_port = info->rtsp_port; sd->sd_info.srcs = info->srcs; /* * device specific hacks */ satip_device_hack(sd); if (conf) feconf = htsmsg_get_map(conf, "frontends"); save = !conf || !feconf; tunercfg = sd->sd_tunercfg; if (tunercfg == NULL) tunercfg = sd->sd_tunercfg = strdup("Auto"); if (strncmp(tunercfg, "DVB", 3) && strncmp(tunercfg, "ATSC", 4)) tunercfg = sd->sd_info.tunercfg; n = http_tokenize(tvh_strdupa(tunercfg), argv, 10, ','); for (i = m = 0, fenum = 1; i < n; i++) { type = DVB_TYPE_NONE; v2 = 0; if (strncmp(argv[i], "DVBS2-", 6) == 0) { type = DVB_TYPE_S; m = atoi(argv[i] + 6); v2 = 1; } else if (strncmp(argv[i], "DVBS-", 5) == 0) { type = DVB_TYPE_S; m = atoi(argv[i] + 5); } else if (strncmp(argv[i], "DVBT2-", 6) == 0) { type = DVB_TYPE_T; m = atoi(argv[i] + 6); v2 = 1; } else if (strncmp(argv[i], "DVBT-", 5) == 0) { type = DVB_TYPE_T; m = atoi(argv[i] + 5); } else if (strncmp(argv[i], "DVBC2-", 6) == 0) { type = DVB_TYPE_C; m = atoi(argv[i] + 6); v2 = 1; } else if (strncmp(argv[i], "DVBC-", 5) == 0) { type = DVB_TYPE_C; m = atoi(argv[i] + 5); } else if (strncmp(argv[i], "ATSC-", 5) == 0) { type = DVB_TYPE_ATSC; m = atoi(argv[i] + 5); } else if (strncmp(argv[i], "DVBCB-", 6) == 0) { m = atoi(argv[i] + 6); v2 = 2; } if (type == DVB_TYPE_NONE) { tvhlog(LOG_ERR, "satip", "%s: bad tuner type [%s]", satip_device_nicename(sd, buf2, sizeof(buf2)), argv[i]); } else if (m < 0 || m > 32) { tvhlog(LOG_ERR, "satip", "%s: bad tuner count [%s]", satip_device_nicename(sd, buf2, sizeof(buf2)), argv[i]); } else { sd->sd_nosave = 1; for (j = 0; j < m; j++) if (satip_frontend_create(feconf, sd, type, v2, fenum)) fenum++; sd->sd_nosave = 0; } } if (save) satip_device_save(sd); sd->sd_inload = 0; htsmsg_destroy(conf); satip_device_dbus_notify(sd, "start"); return sd; }
int main(int argc, char **argv) { int c; int forkaway = 0; FILE *pidfile; const char *pidpath = "/var/run/tvheadend.pid"; struct group *grp; struct passwd *pw; const char *usernam = NULL; const char *groupnam = NULL; int logfacility = LOG_DAEMON; int createdefault = 0; sigset_t set; const char *homedir; const char *rawts_input = NULL; const char *join_transport = NULL; const char *confpath = NULL; char *p, *endp; uint32_t adapter_mask = 0xffffffff; int crash = 0; // make sure the timezone is set tzset(); while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:s")) != -1) { switch(c) { case 'a': adapter_mask = 0x0; p = strtok(optarg, ","); if (p != NULL) { do { int adapter = strtol(p, &endp, 10); if (*endp != 0 || adapter < 0 || adapter > 31) { fprintf(stderr, "Invalid adapter number '%s'\n", p); return 1; } adapter_mask |= (1 << adapter); } while ((p = strtok(NULL, ",")) != NULL); if (adapter_mask == 0x0) { fprintf(stderr, "No adapters specified!\n"); return 1; } } else { usage(argv[0]); } break; case 'A': crash = 1; break; case 'f': forkaway = 1; break; case 'p': pidpath = optarg; break; case 'u': usernam = optarg; break; case 'g': groupnam = optarg; break; case 'c': confpath = optarg; break; case 'd': log_debug_to_console = 1; break; case 's': log_debug_to_syslog = 1; break; case 'C': createdefault = 1; break; case 'r': rawts_input = optarg; break; case 'j': join_transport = optarg; break; default: usage(argv[0]); break; } } signal(SIGPIPE, handle_sigpipe); grp = getgrnam(groupnam ?: "video"); pw = usernam ? getpwnam(usernam) : NULL; if(forkaway) { if(daemon(0, 0)) { exit(2); } pidfile = fopen(pidpath, "w+"); if(pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } if(grp != NULL) { setgid(grp->gr_gid); } else { setgid(1); } if(pw != NULL) { setuid(pw->pw_uid); } else { setuid(1); } if(pw != NULL) { homedir = pw->pw_dir; setenv("HOME", homedir, 1); } umask(0); } log_stderr = !forkaway; log_decorate = isatty(2); sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); openlog("tvheadend", LOG_PID, logfacility); hts_settings_init(confpath); pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_lock(&global_lock); time(&dispatch_clock); trap_init(argv[0]); /** * Initialize subsystems */ xmltv_init(); /* Must be initialized before channels */ service_init(); channels_init(); access_init(createdefault); tcp_server_init(); #if ENABLE_LINUXDVB dvb_init(adapter_mask); #endif iptv_input_init(); #if ENABLE_V4L v4l_init(); #endif http_server_init(); webui_init(tvheadend_dataroot()); serviceprobe_init(); cwc_init(); capmt_init(); epg_init(); dvr_init(); htsp_init(); ffdecsa_init(); if(rawts_input != NULL) rawts_init(rawts_input); if(join_transport != NULL) subscription_dummy_join(join_transport, 1); #ifdef CONFIG_AVAHI avahi_init(); #endif pthread_mutex_unlock(&global_lock); /** * Wait for SIGTERM / SIGINT, but only in this thread */ running = 1; 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, settings located in '%s', " "dataroot: %s", tvheadend_version, getpid(), getuid(), getgid(), hts_settings_get_root(), tvheadend_dataroot() ?: "<Embedded file system>"); if(crash) abort(); mainloop(); epg_save(); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); if(forkaway) unlink("/var/run/tvheadend.pid"); return 0; }
/** * Filename generator * * - convert from utf8 * - avoid duplicate filenames * */ static int pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss) { char fullname[1000]; char path[500]; int tally = 0; struct stat st; char filename[1000]; struct tm tm; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); dvr_make_title(filename, sizeof(filename), de); cleanupfilename(filename,cfg->dvr_flags); snprintf(path, sizeof(path), "%s", cfg->dvr_storage); /* Remove trailing slash */ if (path[strlen(path)-1] == '/') path[strlen(path)-1] = '\0'; /* Append per-day directory */ if(cfg->dvr_flags & DVR_DIR_PER_DAY) { localtime_r(&de->de_start, &tm); strftime(fullname, sizeof(fullname), "%F", &tm); cleanupfilename(fullname,cfg->dvr_flags); snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", fullname); } /* Append per-channel directory */ if(cfg->dvr_flags & DVR_DIR_PER_CHANNEL) { char *chname = strdup(DVR_CH_NAME(de)); cleanupfilename(chname,cfg->dvr_flags); snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", chname); free(chname); } // TODO: per-brand, per-season /* Append per-title directory */ if(cfg->dvr_flags & DVR_DIR_PER_TITLE) { char *title = strdup(lang_str_get(de->de_title, NULL)); cleanupfilename(title,cfg->dvr_flags); snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", title); free(title); } /* */ if(makedirs(path, 0777) != 0) { return -1; } /* Construct final name */ snprintf(fullname, sizeof(fullname), "%s/%s.%s", path, filename, muxer_suffix(de->de_mux, ss)); while(1) { if(stat(fullname, &st) == -1) { tvhlog(LOG_DEBUG, "dvr", "File \"%s\" -- %s -- Using for recording", fullname, strerror(errno)); break; } tvhlog(LOG_DEBUG, "dvr", "Overwrite protection, file \"%s\" exists", fullname); tally++; snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s", path, filename, tally, muxer_suffix(de->de_mux, ss)); } tvh_str_set(&de->de_filename, fullname); return 0; }
int main(int argc, char **argv) { int c; int forkaway = 0; FILE *pidfile; const char *pidpath = "/var/run/tvheadend.pid"; struct group *grp; struct passwd *pw; char *webroot; const char *usernam = NULL; const char *groupnam = NULL; int logfacility = LOG_DAEMON; int createdefault = 0; sigset_t set; const char *homedir; const char *rawts_input = NULL; #if ENABLE_LINUXDVB const char *dvb_rawts_input = NULL; #endif const char *join_transport = NULL; const char *confpath = NULL; char *p, *endp; uint32_t adapter_mask = 0xffffffff; int crash = 0; webui_port = 9981; htsp_port = 9982; gid_t gid; uid_t uid; /* Get current directory */ tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); /* Set locale */ setlocale(LC_ALL, ""); // make sure the timezone is set tzset(); while((c = getopt(argc, argv, "Aa:fp:u:g:c:Chdr:j:sw:e:E:R:W:")) != -1) { switch(c) { case 'a': adapter_mask = 0x0; p = strtok(optarg, ","); if (p != NULL) { do { int adapter = strtol(p, &endp, 10); if (*endp != 0 || adapter < 0 || adapter > 31) { fprintf(stderr, "Invalid adapter number '%s'\n", p); return 1; } adapter_mask |= (1 << adapter); } while ((p = strtok(NULL, ",")) != NULL); if (adapter_mask == 0x0) { fprintf(stderr, "No adapters specified!\n"); return 1; } } else { usage(argv[0]); } break; case 'A': crash = 1; break; case 'f': forkaway = 1; break; case 'p': pidpath = optarg; break; case 'w': webui_port = atoi(optarg); break; case 'e': htsp_port = atoi(optarg); break; case 'E': htsp_port_extra = atoi(optarg); break; case 'u': usernam = optarg; break; case 'g': groupnam = optarg; break; case 'c': confpath = optarg; break; case 'd': log_debug_to_console = 1; break; case 's': log_debug_to_syslog = 1; break; case 'C': createdefault = 1; break; case 'r': rawts_input = optarg; break; #if ENABLE_LINUXDVB case 'R': dvb_rawts_input = optarg; break; #endif case 'j': join_transport = optarg; break; case 'W': webroot = malloc(strlen(optarg) + (*optarg == '/' ? 0 : 1)); if (*optarg != '/') { *webroot = '/'; strcpy(webroot+1, optarg); } else { strcpy(webroot, optarg); } if (webroot[strlen(webroot)-1] == '/') webroot[strlen(webroot)-1] = '\0'; tvheadend_webroot = webroot; break; default: usage(argv[0]); } } signal(SIGPIPE, handle_sigpipe); log_stderr = 1; log_decorate = isatty(2); if(forkaway) { grp = getgrnam(groupnam ?: "video"); pw = usernam ? getpwnam(usernam) : NULL; pidfile = fopen(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); } umask(0); } log_stderr = !forkaway; log_decorate = isatty(2); sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); openlog("tvheadend", LOG_PID, logfacility); hts_settings_init(confpath); pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_lock(&global_lock); time(&dispatch_clock); trap_init(argv[0]); /** * Initialize subsystems */ config_init(); imagecache_init(); service_init(); channels_init(); subscription_init(); access_init(createdefault); #if ENABLE_LINUXDVB muxes_init(); dvb_init(adapter_mask, dvb_rawts_input); #endif iptv_input_init(); #if ENABLE_V4L v4l_init(); #endif tcp_server_init(); http_server_init(); webui_init(); serviceprobe_init(); #if ENABLE_CWC cwc_init(); capmt_init(); #if (!ENABLE_DVBCSA) ffdecsa_init(); #endif #endif epggrab_init(); epg_init(); dvr_init(); htsp_init(); if(rawts_input != NULL) rawts_init(rawts_input); if(join_transport != NULL) subscription_dummy_join(join_transport, 1); #ifdef CONFIG_AVAHI avahi_init(); #endif 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 */ running = 1; 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, settings located in '%s'", tvheadend_version, getpid(), getuid(), getgid(), hts_settings_get_root()); if(crash) abort(); mainloop(); epg_save(); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); if(forkaway) unlink("/var/run/tvheadend.pid"); return 0; }
static void * serviceprobe_thread(void *aux) { service_t *t; th_subscription_t *s; int was_doing_work = 0; streaming_queue_t sq; streaming_message_t *sm; // transport_feed_status_t status; int run; const char *err; channel_t *ch; uint32_t checksubscr; pthread_mutex_lock(&global_lock); streaming_queue_init(&sq, 0); err = NULL; while(1) { while((t = TAILQ_FIRST(&serviceprobe_queue)) == NULL) { if(was_doing_work) { tvhlog(LOG_INFO, "serviceprobe", "Now idle"); was_doing_work = 0; } pthread_cond_wait(&serviceprobe_cond, &global_lock); } if(!was_doing_work) { tvhlog(LOG_INFO, "serviceprobe", "Starting"); was_doing_work = 1; } if (t->s_dvb_mux_instance) checksubscr = !t->s_dvb_mux_instance->tdmi_adapter->tda_skip_checksubscr; else checksubscr = 1; if (checksubscr) { tvhlog(LOG_INFO, "serviceprobe", "%20s: checking...", t->s_svcname); s = subscription_create_from_service(t, "serviceprobe", &sq.sq_st, 0, NULL, NULL, "serviceprobe"); if(s == NULL) { t->s_sp_onqueue = 0; TAILQ_REMOVE(&serviceprobe_queue, t, s_sp_link); tvhlog(LOG_INFO, "serviceprobe", "%20s: could not subscribe", t->s_svcname); continue; } } service_ref(t); pthread_mutex_unlock(&global_lock); if (checksubscr) { run = 1; pthread_mutex_lock(&sq.sq_mutex); while(run) { while((sm = TAILQ_FIRST(&sq.sq_queue)) == NULL) pthread_cond_wait(&sq.sq_cond, &sq.sq_mutex); TAILQ_REMOVE(&sq.sq_queue, sm, sm_link); pthread_mutex_unlock(&sq.sq_mutex); if(sm->sm_type == SMT_SERVICE_STATUS) { int status = sm->sm_code; if(status & TSS_PACKETS) { run = 0; err = NULL; } else if(status & (TSS_GRACEPERIOD | TSS_ERRORS)) { run = 0; err = service_tss2text(status); } } streaming_msg_free(sm); pthread_mutex_lock(&sq.sq_mutex); } streaming_queue_clear(&sq.sq_queue); pthread_mutex_unlock(&sq.sq_mutex); } else { err = NULL; } pthread_mutex_lock(&global_lock); if (checksubscr) { subscription_unsubscribe(s); } if(t->s_status != SERVICE_ZOMBIE) { if(err != NULL) { tvhlog(LOG_INFO, "serviceprobe", "%20s: skipped: %s", t->s_svcname, err); } else if(t->s_ch == NULL) { int channum = t->s_channel_number; const char *str; if (!channum && t->s_dvb_mux_instance->tdmi_adapter->tda_sidtochan) channum = t->s_dvb_service_id; ch = channel_find_by_name(t->s_svcname, 1, channum); service_map_channel(t, ch, 1); tvhlog(LOG_INFO, "serviceprobe", "%20s: mapped to channel \"%s\"", t->s_svcname, t->s_svcname); if(service_is_tv(t)) { channel_tag_map(ch, channel_tag_find_by_name("TV channels", 1), 1); tvhlog(LOG_INFO, "serviceprobe", "%20s: joined tag \"%s\"", t->s_svcname, "TV channels"); } switch(t->s_servicetype) { case ST_SDTV: case ST_AC_SDTV: case ST_EX_SDTV: case ST_DN_SDTV: case ST_SK_SDTV: case ST_NE_SDTV: str = "SDTV"; break; case ST_HDTV: case ST_AC_HDTV: case ST_EX_HDTV: case ST_EP_HDTV: case ST_ET_HDTV: case ST_DN_HDTV: str = "HDTV"; break; case ST_RADIO: str = "Radio"; break; default: str = NULL; } if(str != NULL) { channel_tag_map(ch, channel_tag_find_by_name(str, 1), 1); tvhlog(LOG_INFO, "serviceprobe", "%20s: joined tag \"%s\"", t->s_svcname, str); } if(t->s_provider != NULL) { channel_tag_map(ch, channel_tag_find_by_name(t->s_provider, 1), 1); tvhlog(LOG_INFO, "serviceprobe", "%20s: joined tag \"%s\"", t->s_svcname, t->s_provider); } channel_save(ch); } t->s_sp_onqueue = 0; TAILQ_REMOVE(&serviceprobe_queue, t, s_sp_link); } service_unref(t); } return NULL; }
int diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, uint32_t version, uint32_t repeats) { int i = (lnb_num % 4) * 4 + voltage * 2 + (band ? 1 : 0); int j = lnb_num / 4; int k, err; tvhtrace("diseqc", "fe_fd=%i, lnb_num=%i, voltage=%i, band=%i, version=%i, repeats=%i", fe_fd, lnb_num, voltage, band, version, repeats); /* verify lnb number and diseqc data */ if(lnb_num < 0 || lnb_num >=64 || i < 0 || i >= 16 || j < 0 || j >= 16) return -1; /* turn off continuous tone */ tvhtrace("diseqc", "disabling continuous tone"); if ((err = ioctl(fe_fd, FE_SET_TONE, SEC_TONE_OFF))) { tvhlog(LOG_ERR, "diseqc", "error trying to turn off continuous tone"); return err; } /* set lnb voltage */ tvhtrace("diseqc", "setting lnb voltage to %iV", (i/2) % 2 ? 18 : 13); if ((err = ioctl(fe_fd, FE_SET_VOLTAGE, (i/2) % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13))) { tvhlog(LOG_ERR, "diseqc", "error setting lnb voltage"); return err; } msleep(15); if (repeats == 0) { /* uncommited msg, wait 15ms, commited msg */ if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x39, 0xF0 | j, 0, 0, 4))) return err; msleep(15); if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) return err; } else { /* commited msg, 25ms, uncommited msg, 25ms, commited msg, etc */ if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) return err; for (k = 0; k < repeats; k++) { msleep(25); if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x39, 0xF0 | j, 0, 0, 4))) return err; msleep(25); if ((err = diseqc_send_msg(fe_fd, 0xE1, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) return err; } } msleep(15); /* set toneburst */ tvhtrace("diseqc", (i/4) % 2 ? "sending mini diseqc B" : "sending mini diseqc A"); if ((err = ioctl(fe_fd, FE_DISEQC_SEND_BURST, (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A))) { tvhlog(LOG_ERR, "diseqc", "error sending mini diseqc command"); return err; } msleep(15); /* set continuous tone */ tvhtrace("diseqc", i % 2 ? "enabling continous tone" : "disabling continuous tone"); if ((err = ioctl(fe_fd, FE_SET_TONE, i % 2 ? SEC_TONE_ON : SEC_TONE_OFF))) { tvhlog(LOG_ERR, "diseqc", "error setting continuous tone"); return err; } return 0; }
int main(int argc, char **argv) { int i; sigset_t set; #if ENABLE_MPEGTS uint32_t adapter_mask = 0; #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; gid_t gid = -1; uid_t uid = -1; char buf[512]; FILE *pidfile = NULL; extern int dvb_bouquets_parse; main_tid = pthread_self(); /* Setup global mutexes */ pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_init(&tasklet_lock, NULL); pthread_mutex_init(&atomic_lock, NULL); pthread_cond_init(>imer_cond, NULL); pthread_cond_init(&tasklet_cond, NULL); TAILQ_INIT(&tasklets); /* Defaults */ tvheadend_webui_port = 9981; tvheadend_webroot = NULL; tvheadend_htsp_port = 9982; tvheadend_htsp_port_extra = 0; time(&dispatch_clock); /* Command line options */ int opt_help = 0, opt_version = 0, opt_fork = 0, opt_firstrun = 0, opt_stderr = 0, opt_syslog = 0, opt_nosyslog = 0, opt_uidebug = 0, opt_abort = 0, opt_noacl = 0, opt_fileline = 0, opt_threadid = 0, opt_libav = 0, opt_ipv6 = 0, opt_satip_rtsp = 0, #if ENABLE_TSFILE opt_tsfile_tuner = 0, #endif opt_dump = 0, opt_xspf = 0, opt_dbus = 0, opt_dbus_session = 0, opt_nobackup = 0, opt_nobat = 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, *opt_user_agent = NULL; str_list_t opt_satip_xml = { .max = 10, .num = 0, .str = calloc(10, sizeof(char*)) }; str_list_t opt_tsfile = { .max = 10, .num = 0, .str = calloc(10, sizeof(char*)) }; cmdline_opt_t cmdline_opts[] = { { 0, NULL, N_("Generic Options"), OPT_BOOL, NULL }, { 'h', "help", N_("Show this page"), OPT_BOOL, &opt_help }, { 'v', "version", N_("Show version information"),OPT_BOOL, &opt_version }, { 0, NULL, N_("Service Configuration"), OPT_BOOL, NULL }, { 'c', "config", N_("Alternate config path"), OPT_STR, &opt_config }, { 'B', "nobackup", N_("Don't backup config tree at upgrade"), OPT_BOOL, &opt_nobackup }, { 'f', "fork", N_("Fork and run as daemon"), OPT_BOOL, &opt_fork }, { 'u', "user", N_("Run as user"), OPT_STR, &opt_user }, { 'g', "group", N_("Run as group"), OPT_STR, &opt_group }, { 'p', "pid", N_("Alternate pid path"), OPT_STR, &opt_pidpath }, { 'C', "firstrun", N_("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/create\n" "access-control from within the Tvheadend UI"), OPT_BOOL, &opt_firstrun }, #if ENABLE_DBUS_1 { 'U', "dbus", N_("Enable DBus"), OPT_BOOL, &opt_dbus }, { 'e', "dbus_session", N_("DBus - use the session message bus instead system one"), OPT_BOOL, &opt_dbus_session }, #endif #if ENABLE_LINUXDVB { 'a', "adapters", N_("Only use specified DVB adapters (comma separated)"), OPT_STR, &opt_dvb_adapters }, #endif #if ENABLE_SATIP_SERVER { 0, "satip_rtsp", N_("SAT>IP RTSP port number for server\n" "(default: -1 = disable, 0 = webconfig, standard port is 554)"), OPT_INT, &opt_satip_rtsp }, #endif #if ENABLE_SATIP_CLIENT { 0, "satip_xml", N_("URL with the SAT>IP server XML location"), OPT_STR_LIST, &opt_satip_xml }, #endif { 0, NULL, N_("Server Connectivity"), OPT_BOOL, NULL }, { '6', "ipv6", N_("Listen on IPv6"), OPT_BOOL, &opt_ipv6 }, { 'b', "bindaddr", N_("Specify bind address"), OPT_STR, &opt_bindaddr}, { 0, "http_port", N_("Specify alternative http port"), OPT_INT, &tvheadend_webui_port }, { 0, "http_root", N_("Specify alternative http webroot"), OPT_STR, &tvheadend_webroot }, { 0, "htsp_port", N_("Specify alternative htsp port"), OPT_INT, &tvheadend_htsp_port }, { 0, "htsp_port2", N_("Specify extra htsp port"), OPT_INT, &tvheadend_htsp_port_extra }, { 0, "useragent", N_("Specify User-Agent header for the http client"), OPT_STR, &opt_user_agent }, { 0, "xspf", N_("Use XSPF playlist instead of M3U"), OPT_BOOL, &opt_xspf }, { 0, NULL, N_("Debug Options"), OPT_BOOL, NULL }, { 'd', "stderr", N_("Enable debug on stderr"), OPT_BOOL, &opt_stderr }, { 's', "syslog", N_("Enable debug to syslog"), OPT_BOOL, &opt_syslog }, { 'S', "nosyslog", N_("Disable syslog (all msgs)"), OPT_BOOL, &opt_nosyslog }, { 'l', "logfile", N_("Enable debug to file"), OPT_STR, &opt_logpath }, { 0, "debug", N_("Enable debug subsystems"), OPT_STR, &opt_log_debug }, #if ENABLE_TRACE { 0, "trace", N_("Enable trace subsystems"), OPT_STR, &opt_log_trace }, #endif { 0, "fileline", N_("Add file and line numbers to debug"), OPT_BOOL, &opt_fileline }, { 0, "threadid", N_("Add the thread ID to debug"), OPT_BOOL, &opt_threadid }, #if ENABLE_LIBAV { 0, "libav", N_("More verbose libav log"), OPT_BOOL, &opt_libav }, #endif { 0, "uidebug", N_("Enable webUI debug (non-minified JS)"), OPT_BOOL, &opt_uidebug }, { 'A', "abort", N_("Immediately abort"), OPT_BOOL, &opt_abort }, { 'D', "dump", N_("Enable coredumps for daemon"), OPT_BOOL, &opt_dump }, { 0, "noacl", N_("Disable all access control checks"), OPT_BOOL, &opt_noacl }, { 0, "nobat", N_("Disable DVB bouquets"), OPT_BOOL, &opt_nobat }, { 'j', "join", N_("Subscribe to a service permanently"), OPT_STR, &opt_subscribe }, #if ENABLE_TSFILE || ENABLE_TSDEBUG { 0, NULL, N_("Testing options"), OPT_BOOL, NULL }, { 0, "tsfile_tuners", N_("Number of tsfile tuners"), OPT_INT, &opt_tsfile_tuner }, { 0, "tsfile", N_("tsfile input (mux file)"), OPT_STR_LIST, &opt_tsfile }, #endif #if ENABLE_TSDEBUG { 0, "tsdebug", N_("Output directory for tsdebug"), OPT_STR, &tvheadend_tsdebug }, #endif }; /* Get current directory */ tvheadend_cwd0 = dirname(tvh_strdupa(argv[0])); tvheadend_cwd = dirname(tvh_strdupa(tvheadend_cwd0)); /* 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 (opt_nobat) dvb_bouquets_parse = 0; #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) { fprintf(stderr, _("Invalid adapter number '%s'\n"), p); free(dvb_adapters); return 1; } adapter_mask |= (1 << a); p = strtok_r(NULL, ",", &r); } free(dvb_adapters); if (!adapter_mask) { fprintf(stderr, "%s", _("No adapters specified!\n")); 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_nosyslog) log_options &= ~(TVHLOG_OPT_SYSLOG|TVHLOG_OPT_DBG_SYSLOG); if (opt_fileline) log_options |= TVHLOG_OPT_FILELINE; if (opt_threadid) log_options |= TVHLOG_OPT_THREAD; if (opt_libav) log_options |= TVHLOG_OPT_LIBAV; 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); tvhinfo("main", "Log started"); signal(SIGPIPE, handle_sigpipe); // will be redundant later signal(SIGILL, handle_sigill); // see handler.. /* Set priviledges */ if(opt_fork || opt_group || opt_user) { const char *homedir; struct group *grp = getgrnam(opt_group ?: "video"); struct passwd *pw = opt_user ? getpwnam(opt_user) : NULL; if(grp != NULL) { gid = grp->gr_gid; } else { gid = 1; } if (pw != NULL) { if (getuid() != pw->pw_uid) { gid_t glist[16]; int gnum; gnum = get_user_groups(pw, glist, ARRAY_SIZE(glist)); if (gnum > 0 && setgroups(gnum, glist)) { char buf[256] = ""; int i; for (i = 0; i < gnum; i++) snprintf(buf + strlen(buf), sizeof(buf) - 1 - strlen(buf), ",%d", glist[i]); tvhlog(LOG_ALERT, "START", "setgroups(%s) failed, do you have permission?", buf+1); return 1; } } uid = pw->pw_uid; homedir = pw->pw_dir; setenv("HOME", homedir, 1); } else { uid = 1; } } uuid_init(); config_boot(opt_config, gid, uid); tcp_server_preinit(opt_ipv6); http_server_init(opt_bindaddr); // bind to ports only htsp_init(opt_bindaddr); // bind to ports only satip_server_init(opt_satip_rtsp); // bind to ports only if (opt_fork) pidfile = tvh_fopen(opt_pidpath, "w+"); if (gid != -1 && (getgid() != gid) && setgid(gid)) { tvhlog(LOG_ALERT, "START", "setgid(%d) failed, do you have permission?", gid); return 1; } if (uid != -1 && (getuid() != uid) && setuid(uid)) { tvhlog(LOG_ALERT, "START", "setuid(%d) failed, do you have permission?", uid); return 1; } /* Daemonise */ if(opt_fork) { 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]); /* SSL library init */ OPENSSL_config(NULL); SSL_load_error_strings(); SSL_library_init(); /* Initialise configuration */ notify_init(); idnode_init(); spawn_init(); config_init(opt_nobackup == 0); /** * Initialize subsystems */ epg_in_load = 1; tvhthread_create(&tasklet_tid, NULL, tasklet_thread, NULL); dbus_server_init(opt_dbus, opt_dbus_session); intlconv_init(); api_init(); fsmonitor_init(); libav_init(); tvhtime_init(); profile_init(); imagecache_init(); http_client_init(opt_user_agent); esfilter_init(); bouquet_init(); service_init(); dvb_init(); #if ENABLE_MPEGTS mpegts_init(adapter_mask, &opt_satip_xml, &opt_tsfile, opt_tsfile_tuner); #endif channel_init(); bouquet_service_resolve(); subscription_init(); dvr_config_init(); access_init(opt_firstrun, opt_noacl); #if ENABLE_TIMESHIFT timeshift_init(); #endif tcp_server_init(); webui_init(opt_xspf); #if ENABLE_UPNP upnp_server_init(opt_bindaddr); #endif service_mapper_init(); descrambler_init(); epggrab_init(); epg_init(); dvr_init(); dbus_server_start(); http_server_register(); satip_server_register(); htsp_register(); if(opt_subscribe != NULL) subscription_dummy_join(opt_subscribe, 1); avahi_init(); bonjour_init(); epg_updated(); // cleanup now all prev ref's should have been created epg_in_load = 0; 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(); #if ENABLE_DBUS_1 tvhftrace("main", dbus_server_done); #endif #if ENABLE_UPNP tvhftrace("main", upnp_server_done); #endif tvhftrace("main", satip_server_done); tvhftrace("main", htsp_done); tvhftrace("main", http_server_done); tvhftrace("main", webui_done); tvhftrace("main", fsmonitor_done); tvhftrace("main", http_client_done); tvhftrace("main", tcp_server_done); // 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); #if ENABLE_MPEGTS tvhftrace("main", mpegts_done); #endif tvhftrace("main", descrambler_done); tvhftrace("main", service_mapper_done); tvhftrace("main", service_done); tvhftrace("main", channel_done); tvhftrace("main", bouquet_done); tvhftrace("main", dvr_done); tvhftrace("main", subscription_done); tvhftrace("main", access_done); tvhftrace("main", epg_done); tvhftrace("main", avahi_done); tvhftrace("main", bonjour_done); tvhftrace("main", imagecache_done); tvhftrace("main", lang_code_done); tvhftrace("main", api_done); tvhtrace("main", "tasklet enter"); pthread_cond_signal(&tasklet_cond); pthread_join(tasklet_tid, NULL); tvhtrace("main", "tasklet thread end"); tasklet_flush(); tvhtrace("main", "tasklet leave"); tvhftrace("main", hts_settings_done); tvhftrace("main", dvb_done); tvhftrace("main", lang_str_done); tvhftrace("main", esfilter_done); tvhftrace("main", profile_done); tvhftrace("main", intlconv_done); tvhftrace("main", urlparse_done); tvhftrace("main", idnode_done); tvhftrace("main", notify_done); tvhftrace("main", spawn_done); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); tvhlog_end(); tvhftrace("main", config_done); if(opt_fork) unlink(opt_pidpath); #if ENABLE_TSFILE free(opt_tsfile.str); #endif free(opt_satip_xml.str); /* OpenSSL - welcome to the "cleanup" hell */ ENGINE_cleanup(); RAND_cleanup(); CRYPTO_cleanup_all_ex_data(); EVP_cleanup(); CONF_modules_free(); #ifndef OPENSSL_NO_COMP COMP_zlib_cleanup(); #endif ERR_remove_state(0); ERR_free_strings(); #ifndef OPENSSL_NO_COMP sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); #endif /* end of OpenSSL cleanup code */ #if ENABLE_DBUS_1 extern void dbus_shutdown(void); if (opt_dbus) dbus_shutdown(); #endif return 0; } /** * */ void tvh_str_set(char **strp, const char *src) { free(*strp); *strp = src ? strdup(src) : NULL; } /** * */ int tvh_str_update(char **strp, const char *src) { if(src == NULL) return 0; free(*strp); *strp = strdup(src); return 1; } /** * */ void scopedunlock(pthread_mutex_t **mtxp) { pthread_mutex_unlock(*mtxp); }
/* * Get current / new file */ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) { int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; timeshift_index_data_t *ti; char path[512]; /* Return last file */ if (!create) return TAILQ_LAST(&ts->files, timeshift_file_list); /* No space */ if (ts->full) return NULL; /* Store to file */ clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); if (!tsf_tl || tsf_tl->time != tp.tv_sec) { tsf_hd = TAILQ_FIRST(&ts->files); /* Close existing */ if (tsf_tl && tsf_tl->fd != -1) timeshift_filemgr_close(tsf_tl); /* Check period */ if (ts->max_time && tsf_hd && tsf_tl) { time_t d = tsf_tl->time - tsf_hd->time; if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); } else { #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); #endif ts->full = 1; } } } /* Check size */ // TODO: need to implement this /* Create new file */ tsf_tmp = NULL; if (!ts->full) { snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, tp.tv_sec); #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d create file %s", ts->id, path); #endif if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = calloc(1, sizeof(timeshift_file_t)); tsf_tmp->time = tp.tv_sec; tsf_tmp->fd = fd; tsf_tmp->path = strdup(path); tsf_tmp->refcount = 0; tsf_tmp->last = getmonoclock(); TAILQ_INIT(&tsf_tmp->iframes); TAILQ_INIT(&tsf_tmp->sstart); TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d copy smt_start to new file", ts->id); #endif timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } } } tsf_tl = tsf_tmp; } return tsf_tl; }