示例#1
0
/*
 * 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;
}
示例#2
0
文件: libav.c 项目: 7eggert/tvheadend
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;
    }
}
示例#3
0
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:
    ;
  }
}
示例#4
0
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';
    }
  }
}
示例#5
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;
}
示例#6
0
/**
 * 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;
}
示例#7
0
文件: otamux.c 项目: JPP1/tvheadend
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 {
示例#8
0
/*
 * 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;
  }
}
示例#9
0
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;
}
示例#10
0
文件: main.c 项目: shahxeb/tvheadend
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;
}
示例#11
0
/**
 * 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;
}
示例#12
0
/*
 * 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;
}
示例#13
0
文件: dvr_rec.c 项目: JPP1/tvheadend
/**
 * 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;
}
示例#14
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(&timeshift_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;
}
示例#15
0
文件: main.c 项目: shahxeb/tvheadend
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;

}
示例#16
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;
}
示例#17
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);
}
示例#18
0
文件: xmltv.c 项目: Cjcr/tvheadend
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);
  }
}
示例#19
0
/*
 * 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;
}
示例#20
0
/*
 * 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;
}
示例#21
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;
}
示例#22
0
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);
}
示例#23
0
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;
}
示例#24
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;
  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;

}
示例#25
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;
}
示例#26
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;

}
示例#27
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;
}
示例#28
0
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;
}
示例#29
0
文件: main.c 项目: bigbig6/tvheadend
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(&gtimer_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);
}
示例#30
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];

  /* 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;
}