Rename tvheadend_newui to tvheadend

This commit is contained in:
Andreas Öman 2008-08-25 16:22:50 +00:00
parent 63524f157b
commit ce4669b418
50 changed files with 8503 additions and 7280 deletions

View file

@ -3,7 +3,7 @@
SRCS = main.c dispatch.c channels.c transports.c teletext.c psi.c \
subscriptions.c mux.c tsdemux.c buffer.c tcp.c \
resolver.c tsmux.c parsers.c bitstream.c parser_h264.c spawn.c \
notify.c intercom.c access.c serviceprobe.c
notify.c intercom.c access.c serviceprobe.c dtable.c
SRCS += http.c
@ -34,34 +34,14 @@ SRCS += FFdecsa.c
#
VPATH += webui
SRCS += webui.c
SRCS += webui.c extjs.c comet.c
#
# Embedded AJAX user interface
#
JSSRCS += tvheadend.js extensions.js acleditor.js cwceditor.js \
dvb.js
VPATH += ajaxui
SRCS += ajaxui.c ajaxui_mailbox.c ajaxui_channels.c \
ajaxui_config.c ajaxui_config_channels.c ajaxui_config_dvb.c \
ajaxui_config_transport.c ajaxui_config_xmltv.c \
ajaxui_config_access.c ajaxui_config_cwc.c
JSSRCS += tvheadend.js
CSSSRCS += ajaxui.css
VPATH += ajaxui/images
GIFSRCS+= sbbody_l.gif sbbody_r.gif sbhead_l.gif sbhead_r.gif
PNGSRCS+= mapped.png unmapped.png
VPATH += ajaxui/prototype
JSSRCS += prototype.js
VPATH += ajaxui/scriptaculous
JSSRCS += builder.js controls.js dragdrop.js effects.js scriptaculous.js \
slider.js
CSSSRCS += ext.css
PROGPATH = $(HTS_BUILD_ROOT)/tvheadend_newui
PROG = tvheadend
MAN = tvheadend.1
CFLAGS += -g -Wall -Werror -O2 -mmmx

472
access.c
View file

@ -31,117 +31,12 @@
#include "tvhead.h"
#include "access.h"
#include "dtable.h"
static int atally;
struct access_entry_queue access_entries;
static void access_load(void);
/**
*
*/
void
access_init(void)
{
TAILQ_INIT(&access_entries);
access_load();
}
/**
*
*/
static access_entry_t *
access_alloc(void)
{
access_entry_t *ae;
ae = calloc(1, sizeof(access_entry_t));
TAILQ_INSERT_TAIL(&access_entries, ae, ae_link);
ae->ae_tally = ++atally;
return ae;
}
/**
*
*/
static access_entry_t *
access_add_network(const char *prefix)
{
access_entry_t *ae;
char buf[100];
int prefixlen;
char *p;
struct in_addr ip;
char title[100];
if(strlen(prefix) > 90)
return NULL;
strcpy(buf, prefix);
p = strchr(buf, '/');
if(p) {
*p++ = 0;
prefixlen = atoi(p);
if(prefixlen > 32)
return NULL;
} else {
prefixlen = 32;
}
ip.s_addr = inet_addr(buf);
ae = access_alloc();
ae->ae_ip.s_addr = ip.s_addr;
ae->ae_prefixlen = prefixlen;
ae->ae_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0;
ae->ae_network = ntohl(ip.s_addr) & ae->ae_netmask;
ip.s_addr = htonl(ae->ae_network);
snprintf(title, sizeof(title), "%s/%d", inet_ntoa(ip), prefixlen);
ae->ae_title = strdup(title);
return ae;
}
/**
*
*/
static access_entry_t *
access_add_user(const char *username)
{
access_entry_t *ae;
ae = access_alloc();
ae->ae_username = strdup(username);
ae->ae_title = strdup(username);
return ae;
}
/**
*
*/
access_entry_t *
access_add(const char *id)
{
access_entry_t *ae;
if(isdigit(id[0]))
ae = access_add_network(id);
else
ae = access_add_user(id);
if(ae != NULL)
access_save();
return ae;
}
/**
@ -158,168 +53,295 @@ access_verify(const char *username, const char *password,
TAILQ_FOREACH(ae, &access_entries, ae_link) {
if(access_is_prefix(ae)) {
if((b & ae->ae_netmask) == ae->ae_network)
bits |= ae->ae_rights;
if(ae->ae_username[0] != '*') {
/* acl entry requires username to match */
printf("Need user access\n");
if(username == NULL)
continue; /* Didn't get one */
} else {
if(username != NULL &&
!strcmp(ae->ae_username, username) &&
!strcmp(ae->ae_password, password))
bits |= ae->ae_rights;
if(strcmp(ae->ae_username, username) ||
strcmp(ae->ae_password, password))
continue; /* username/password mismatch */
}
}
if((b & ae->ae_netmask) != ae->ae_network)
continue; /* IP based access mismatches */
bits |= ae->ae_rights;
}
return (mask & bits) == mask ? 0 : -1;
}
/**
*
*/
access_entry_t *
access_by_id(int id)
static void
access_update_flag(access_entry_t *ae, int flag, int bo)
{
access_entry_t *ae;
TAILQ_FOREACH(ae, &access_entries, ae_link) {
if(ae->ae_tally == id)
return ae;
}
return NULL;
if(bo)
ae->ae_rights |= flag;
else
ae->ae_rights &= ~flag;
}
/**
*
*/
void
access_delete(access_entry_t *ae)
static void
access_set_prefix(access_entry_t *ae, const char *prefix)
{
TAILQ_REMOVE(&access_entries, ae, ae_link);
free(ae->ae_title);
char buf[100];
int prefixlen;
char *p;
if(strlen(prefix) > 90)
return;
strcpy(buf, prefix);
p = strchr(buf, '/');
if(p) {
*p++ = 0;
prefixlen = atoi(p);
if(prefixlen > 32)
return;
} else {
prefixlen = 32;
}
ae->ae_ip.s_addr = inet_addr(buf);
ae->ae_prefixlen = prefixlen;
ae->ae_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0;
ae->ae_network = ntohl(ae->ae_ip.s_addr) & ae->ae_netmask;
}
/**
*
*/
static access_entry_t *
access_entry_find(const char *id, int create)
{
access_entry_t *ae;
char buf[20];
if(id != NULL) {
TAILQ_FOREACH(ae, &access_entries, ae_link)
if(!strcmp(ae->ae_id, id))
return ae;
}
if(create == 0)
return NULL;
ae = calloc(1, sizeof(access_entry_t));
if(id == NULL) {
atally++;
snprintf(buf, sizeof(buf), "%d", atally);
id = buf;
} else {
atally = atoi(id);
}
ae->ae_id = strdup(id);
ae->ae_username = strdup("*");
ae->ae_password = strdup("*");
ae->ae_comment = strdup("New entry");
TAILQ_INSERT_TAIL(&access_entries, ae, ae_link);
return ae;
}
/**
*
*/
static void
access_entry_destroy(access_entry_t *ae)
{
free(ae->ae_id);
free(ae->ae_username);
free(ae->ae_password);
TAILQ_REMOVE(&access_entries, ae, ae_link);
free(ae);
access_save();
}
/**
*
*/
static void
access_save_right(FILE *fp, access_entry_t *ae, const char *right, int v)
static htsmsg_t *
access_record_build(access_entry_t *ae)
{
fprintf(fp, "\t%s = %d\n", right, !!(ae->ae_rights & v));
}
htsmsg_t *e = htsmsg_create();
char buf[100];
/**
*
*/
static void
access_load_right(struct config_head *h, access_entry_t *ae,
const char *right, int v)
{
int on = atoi(config_get_str_sub(h, right, "0"));
htsmsg_add_u32(e, "enabled", !!ae->ae_enabled);
htsmsg_add_str(e, "username", ae->ae_username);
htsmsg_add_str(e, "password", ae->ae_password);
htsmsg_add_str(e, "comment", ae->ae_comment);
snprintf(buf, sizeof(buf), "%s/%d", inet_ntoa(ae->ae_ip), ae->ae_prefixlen);
htsmsg_add_str(e, "prefix", buf);
htsmsg_add_u32(e, "streaming", ae->ae_rights & ACCESS_STREAMING ? 1 : 0);
htsmsg_add_u32(e, "pvr" , ae->ae_rights & ACCESS_RECORDER ? 1 : 0);
htsmsg_add_u32(e, "webui" , ae->ae_rights & ACCESS_WEB_INTERFACE ? 1 : 0);
htsmsg_add_u32(e, "admin" , ae->ae_rights & ACCESS_ADMIN ? 1 : 0);
htsmsg_add_str(e, "id", ae->ae_id);
if(on)
ae->ae_rights |= v;
return e;
}
/**
*
*/
static htsmsg_t *
access_record_get_all(void *opaque)
{
htsmsg_t *r = htsmsg_create_array();
access_entry_t *ae;
TAILQ_FOREACH(ae, &access_entries, ae_link)
htsmsg_add_msg(r, NULL, access_record_build(ae));
return r;
}
/**
*
*/
static htsmsg_t *
access_record_get(void *opaque, const char *id)
{
access_entry_t *ae;
if((ae = access_entry_find(id, 0)) == NULL)
return NULL;
return access_record_build(ae);
}
/**
*
*/
static htsmsg_t *
access_record_create(void *opaque)
{
return access_record_build(access_entry_find(NULL, 1));
}
/**
*
*/
static htsmsg_t *
access_record_update(void *opaque, const char *id, htsmsg_t *values,
int maycreate)
{
access_entry_t *ae;
const char *s;
uint32_t u32;
if((ae = access_entry_find(id, maycreate)) == NULL)
return NULL;
if((s = htsmsg_get_str(values, "username")) != NULL) {
free(ae->ae_username);
ae->ae_username = strdup(s);
}
if((s = htsmsg_get_str(values, "comment")) != NULL) {
free(ae->ae_comment);
ae->ae_comment = strdup(s);
}
if((s = htsmsg_get_str(values, "password")) != NULL) {
free(ae->ae_password);
ae->ae_password = strdup(s);
}
if((s = htsmsg_get_str(values, "prefix")) != NULL)
access_set_prefix(ae, s);
if(!htsmsg_get_u32(values, "enabled", &u32))
ae->ae_enabled = u32;
if(!htsmsg_get_u32(values, "streaming", &u32))
access_update_flag(ae, ACCESS_STREAMING, u32);
if(!htsmsg_get_u32(values, "pvr", &u32))
access_update_flag(ae, ACCESS_RECORDER, u32);
if(!htsmsg_get_u32(values, "admin", &u32))
access_update_flag(ae, ACCESS_ADMIN, u32);
if(!htsmsg_get_u32(values, "webui", &u32))
access_update_flag(ae, ACCESS_WEB_INTERFACE, u32);
return access_record_build(ae);
}
/**
*
*/
static int
access_record_delete(void *opaque, const char *id)
{
access_entry_t *ae;
if((ae = access_entry_find(id, 0)) == NULL)
return -1;
access_entry_destroy(ae);
return 0;
}
/**
*
*/
static const dtable_class_t access_dtc = {
.dtc_record_get = access_record_get,
.dtc_record_get_all = access_record_get_all,
.dtc_record_create = access_record_create,
.dtc_record_update = access_record_update,
.dtc_record_delete = access_record_delete,
};
/**
*
*/
void
access_save(void)
access_init(void)
{
dtable_t *dt;
htsmsg_t *r;
access_entry_t *ae;
char buf[400];
FILE *fp;
snprintf(buf, sizeof(buf), "%s/access.cfg", settings_dir);
if((fp = settings_open_for_write(buf)) == NULL)
return;
TAILQ_INIT(&access_entries);
TAILQ_FOREACH(ae, &access_entries, ae_link) {
if(access_is_prefix(ae)) {
fprintf(fp, "prefix {\n");
fprintf(fp, "\tid = %s\n", ae->ae_title);
} else {
fprintf(fp, "user {\n");
fprintf(fp, "\tname = %s\n", ae->ae_username);
if(ae->ae_password != NULL)
fprintf(fp, "\tpassword = %s\n", ae->ae_password);
}
dt = dtable_create(&access_dtc, "accesscontrol", NULL);
access_save_right(fp, ae, "streaming", ACCESS_STREAMING);
access_save_right(fp, ae, "rec", ACCESS_RECORDER_VIEW);
access_save_right(fp, ae, "recedit", ACCESS_RECORDER_CHANGE);
access_save_right(fp, ae, "admin", ACCESS_ADMIN);
access_save_right(fp, ae, "webui", ACCESS_WEB_INTERFACE);
access_save_right(fp, ae, "access", ACCESS_ADMIN_ACCESS);
if(dtable_load(dt) == 0) {
/* No records available */
ae = access_entry_find(NULL, 1);
fprintf(fp, "}\n");
}
fclose(fp);
}
free(ae->ae_comment);
ae->ae_comment = strdup("Default access entry");
/**
*
*/
static void
access_load(void)
{
char buf[400];
access_entry_t *ae;
const char *name;
struct config_head cl;
config_entry_t *ce;
ae->ae_enabled = 1;
ae->ae_rights = 0xffffffff;
TAILQ_INIT(&cl);
r = access_record_build(ae);
dtable_record_store(dt, ae->ae_id, r);
htsmsg_destroy(r);
snprintf(buf, sizeof(buf), "%s/access.cfg", settings_dir);
if(config_read_file0(buf, &cl)) {
ae = access_add_network("0.0.0.0/0");
ae->ae_rights = ACCESS_FULL;
access_save();
return;
}
fprintf(stderr, "Notice: Created default access controle entry\n");
TAILQ_FOREACH(ce, &cl, ce_link) {
if(ce->ce_type != CFG_SUB)
continue;
if(!strcasecmp("user", ce->ce_key)) {
if((name = config_get_str_sub(&ce->ce_sub, "name", NULL)) == NULL)
continue;
ae = access_add_user(name);
if((name = config_get_str_sub(&ce->ce_sub, "password", NULL)) != NULL)
ae->ae_password = strdup(name);
} else if(!strcasecmp("prefix", ce->ce_key)) {
if((name = config_get_str_sub(&ce->ce_sub, "id", NULL)) == NULL)
continue;
ae = access_add_network(name);
} else {
continue;
}
if(ae == NULL)
continue;
access_load_right(&ce->ce_sub, ae, "streaming", ACCESS_STREAMING);
access_load_right(&ce->ce_sub, ae, "rec", ACCESS_RECORDER_VIEW);
access_load_right(&ce->ce_sub, ae, "recedit", ACCESS_RECORDER_CHANGE);
access_load_right(&ce->ce_sub, ae, "admin", ACCESS_ADMIN);
access_load_right(&ce->ce_sub, ae, "webui", ACCESS_WEB_INTERFACE);
access_load_right(&ce->ce_sub, ae, "access", ACCESS_ADMIN_ACCESS);
}
}

View file

@ -24,17 +24,18 @@ TAILQ_HEAD(access_entry_queue, access_entry);
extern struct access_entry_queue access_entries;
#define access_is_prefix(ae) ((ae)->ae_username == NULL)
typedef struct access_entry {
int ae_tally;
char *ae_title;
char *ae_id;
TAILQ_ENTRY(access_entry) ae_link;
char *ae_username;
char *ae_password;
char *ae_comment;
struct in_addr ae_ip;
int ae_prefixlen;
int ae_enabled;
uint32_t ae_rights;
uint32_t ae_network; /* derived from ae_ip */
@ -43,11 +44,9 @@ typedef struct access_entry {
#define ACCESS_STREAMING 0x1
#define ACCESS_RECORDER_VIEW 0x2
#define ACCESS_RECORDER_CHANGE 0x4
#define ACCESS_WEB_INTERFACE 0x2
#define ACCESS_RECORDER 0x4
#define ACCESS_ADMIN 0x8
#define ACCESS_WEB_INTERFACE 0x10
#define ACCESS_ADMIN_ACCESS 0x20
#define ACCESS_FULL 0x3f
@ -62,12 +61,12 @@ int access_verify(const char *username, const char *password,
void access_init(void);
access_entry_t *access_add(const char *id);
//access_entry_t *access_add(const char *id);
access_entry_t *access_by_id(int id);
//access_entry_t *access_by_id(int id);
void access_delete(access_entry_t *ae);
//void access_delete(access_entry_t *ae);
void access_save(void);
//void access_save(void);
#endif /* ACCESS_H_ */

305
cwc.c
View file

@ -39,6 +39,8 @@
#include "cwc.h"
#include "notify.h"
#include "dtable.h"
static int cwc_tally;
#define CWC_KEEPALIVE_INTERVAL 600
@ -119,7 +121,7 @@ cwc_set_state(cwc_t *cwc, int newstate, const char *errtxt)
if(newstate == CWC_STATE_IDLE)
cwc->cwc_errtxt = errtxt;
notify_cwc_status_change(cwc);
//notify_cwc_status_change(cwc);
}
@ -263,8 +265,8 @@ des_make_login_key(cwc_t *cwc, uint8_t *k)
static void
des_make_session_key(cwc_t *cwc)
{
uint8_t des14[14], *k2 = (uint8_t *)cwc->cwc_password;
int i, l = strlen(cwc->cwc_password);
uint8_t des14[14], *k2 = (uint8_t *)cwc->cwc_password_salted;
int i, l = strlen(cwc->cwc_password_salted);
memcpy(des14, cwc->cwc_confedkey, 14);
@ -385,7 +387,7 @@ cwc_dispatch_card_data_reply(cwc_t *cwc, uint8_t msgtype,
cwc->cwc_caid = (msg[4] << 8) | msg[5];
n = psi_caid2name(cwc->cwc_caid) ?: "Unknown";
syslog(LOG_INFO, "cwc: %s: Connected as user 0x%02x "
tvhlog(LOG_INFO, "cwc", "%s: Connected as user 0x%02x "
"to a %s-card [0x%04x : %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x] "
"with %d providers",
cwc->cwc_tcp_session.tcp_hostname,
@ -412,7 +414,7 @@ cwc_send_login(cwc_t *cwc)
{
uint8_t buf[CWS_NETMSGSIZE];
int ul = strlen(cwc->cwc_username) + 1;
int pl = strlen(cwc->cwc_password) + 1;
int pl = strlen(cwc->cwc_password_salted) + 1;
cwc_set_state(cwc, CWC_STATE_WAIT_LOGIN_ACK, NULL);
@ -420,7 +422,7 @@ cwc_send_login(cwc_t *cwc)
buf[1] = 0;
buf[2] = ul + pl;
memcpy(buf + 3, cwc->cwc_username, ul);
memcpy(buf + 3 + ul, cwc->cwc_password, pl);
memcpy(buf + 3 + ul, cwc->cwc_password_salted, pl);
cwc_send_msg(cwc, buf, ul + pl + 3, 0);
}
@ -434,14 +436,21 @@ cwc_dispatch_login_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
{
switch(msgtype) {
case MSG_CLIENT_2_SERVER_LOGIN_ACK:
tvhlog(LOG_INFO, "cwc", "%s: Login ok",
cwc->cwc_tcp_session.tcp_hostname);
des_make_session_key(cwc);
cwc_send_data_req(cwc);
return 0;
case MSG_CLIENT_2_SERVER_LOGIN_NAK:
tvhlog(LOG_INFO, "cwc", "%s: Authentication denied",
cwc->cwc_tcp_session.tcp_hostname);
return EACCES;
default:
tvhlog(LOG_INFO, "cwc", "%s: Invalid response (msgcode = %d) during login",
cwc->cwc_tcp_session.tcp_hostname, msgtype);
return EBADMSG;
}
}
@ -479,7 +488,7 @@ cwc_dispatch_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
if(len < 19) {
if(ct->ct_keystate != CT_FORBIDDEN) {
syslog(LOG_ERR,
tvhlog(LOG_ERR, "cwc",
"Can not descramble \"%s\" for service \"%s\", access denied",
t->tht_identifier, t->tht_svcname);
ct->ct_keystate = CT_FORBIDDEN;
@ -489,7 +498,7 @@ cwc_dispatch_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
}
if(ct->ct_keystate != CT_RESOLVED)
syslog(LOG_INFO,
tvhlog(LOG_INFO, "cwc",
"Obtained key for \"%s\" for service \"%s\"",
t->tht_identifier, t->tht_svcname);
@ -774,6 +783,7 @@ cwc_transport_start(th_transport_t *t)
}
}
#if 0
/**
*
@ -914,25 +924,14 @@ cwc_add(const char *hostname, const char *porttxt, const char *username,
return NULL;
}
/**
*
*/
cwc_t *
cwc_find(int id)
{
cwc_t *cwc;
TAILQ_FOREACH(cwc, &cwcs, cwc_link)
if(cwc->cwc_id == id)
break;
return cwc;
}
#endif
/**
*
*/
void
cwc_delete(cwc_t *cwc)
static void
cwc_destroy(cwc_t *cwc)
{
cwc_transport_t *ct;
@ -941,22 +940,251 @@ cwc_delete(cwc_t *cwc)
TAILQ_REMOVE(&cwcs, cwc, cwc_link);
free((void *)cwc->cwc_password);
free((void *)cwc->cwc_password_salted);
free((void *)cwc->cwc_username);
tcp_destroy_client(&cwc->cwc_tcp_session);
cwc_save();
}
/**
*
*/
static cwc_t *
cwc_entry_find(const char *id, int create)
{
char buf[20];
cwc_t *cwc;
if(id != NULL) {
TAILQ_FOREACH(cwc, &cwcs, cwc_link)
if(!strcmp(cwc->cwc_id, id))
return cwc;
}
if(create == 0)
return NULL;
if(id == NULL) {
cwc_tally++;
snprintf(buf, sizeof(buf), "%d", cwc_tally);
id = buf;
} else {
cwc_tally = atoi(id);
}
cwc = tcp_create_client(NULL, 11000, sizeof(cwc_t),
"cwc", cwc_tcp_callback, 0);
cwc->cwc_id = strdup(id);
cwc->cwc_username = NULL;
cwc->cwc_password = NULL;
TAILQ_INSERT_TAIL(&cwcs, cwc, cwc_link);
return cwc;
}
/**
*
*/
static htsmsg_t *
cwc_record_build(cwc_t *cwc)
{
htsmsg_t *e = htsmsg_create();
char buf[100];
htsmsg_add_str(e, "id", cwc->cwc_id);
htsmsg_add_u32(e, "enabled", !!cwc->cwc_enabled);
htsmsg_add_str(e, "hostname", cwc->cwc_tcp_session.tcp_hostname ?: "");
htsmsg_add_u32(e, "port", cwc->cwc_tcp_session.tcp_port);
htsmsg_add_str(e, "username", cwc->cwc_username ?: "");
htsmsg_add_str(e, "password", cwc->cwc_password ?: "");
snprintf(buf, sizeof(buf),
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x",
cwc->cwc_confedkey[0x0],
cwc->cwc_confedkey[0x1],
cwc->cwc_confedkey[0x2],
cwc->cwc_confedkey[0x3],
cwc->cwc_confedkey[0x4],
cwc->cwc_confedkey[0x5],
cwc->cwc_confedkey[0x6],
cwc->cwc_confedkey[0x7],
cwc->cwc_confedkey[0x8],
cwc->cwc_confedkey[0x9],
cwc->cwc_confedkey[0xa],
cwc->cwc_confedkey[0xb],
cwc->cwc_confedkey[0xc],
cwc->cwc_confedkey[0xd]);
htsmsg_add_str(e, "deskey", buf);
htsmsg_add_str(e, "comment", cwc->cwc_comment ?: "");
return e;
}
/**
*
*/
static int
nibble(char c)
{
switch(c) {
case '0' ... '9':
return c - '0';
case 'a' ... 'f':
return c - 'a' + 10;
case 'A' ... 'F':
return c - 'A' + 10;
default:
return 0;
}
}
/**
*
*/
static htsmsg_t *
cwc_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate)
{
cwc_t *cwc;
const char *s;
uint32_t u32;
uint8_t key[14];
int u, l, i;
if((cwc = cwc_entry_find(id, maycreate)) == NULL)
return NULL;
/* Disable while changing */
tcp_enable_disable(&cwc->cwc_tcp_session, 0);
if((s = htsmsg_get_str(values, "username")) != NULL) {
free(cwc->cwc_username);
cwc->cwc_username = strdup(s);
}
if((s = htsmsg_get_str(values, "password")) != NULL) {
free(cwc->cwc_password);
free(cwc->cwc_password_salted);
cwc->cwc_password = strdup(s);
cwc->cwc_password_salted = strdup(cwc_krypt(s, "$1$abcdefgh$"));
}
if((s = htsmsg_get_str(values, "comment")) != NULL) {
free(cwc->cwc_comment);
cwc->cwc_comment = strdup(s);
}
if((s = htsmsg_get_str(values, "hostname")) != NULL) {
tcp_set_hostname(&cwc->cwc_tcp_session, s);
}
if(!htsmsg_get_u32(values, "enabled", &u32))
cwc->cwc_enabled = u32;
if(!htsmsg_get_u32(values, "port", &u32))
cwc->cwc_tcp_session.tcp_port = u32;
if((s = htsmsg_get_str(values, "deskey")) != NULL) {
for(i = 0; i < 14; i++) {
while(*s != 0 && !isxdigit(*s)) s++;
if(*s == 0)
break;
u = nibble(*s++);
while(*s != 0 && !isxdigit(*s)) s++;
if(*s == 0)
break;
l = nibble(*s++);
key[i] = (u << 4) | l;
}
memcpy(cwc->cwc_confedkey, key, 14);
}
if(cwc->cwc_enabled)
tcp_enable_disable(&cwc->cwc_tcp_session, 1);
return cwc_record_build(cwc);
}
/**
*
*/
static int
cwc_entry_delete(void *opaque, const char *id)
{
cwc_t *cwc;
if((cwc = cwc_entry_find(id, 0)) == NULL)
return -1;
cwc_destroy(cwc);
return 0;
}
/**
*
*/
static htsmsg_t *
cwc_entry_get_all(void *opaque)
{
htsmsg_t *r = htsmsg_create_array();
cwc_t *cwc;
TAILQ_FOREACH(cwc, &cwcs, cwc_link)
htsmsg_add_msg(r, NULL, cwc_record_build(cwc));
return r;
}
/**
*
*/
void
cwc_set_enable(cwc_t *cwc, int enabled)
static htsmsg_t *
cwc_entry_get(void *opaque, const char *id)
{
tcp_enable_disable(&cwc->cwc_tcp_session, enabled);
cwc_save();
cwc_t *cwc;
if((cwc = cwc_entry_find(id, 0)) == NULL)
return NULL;
return cwc_record_build(cwc);
}
/**
*
*/
/**
*
*/
static htsmsg_t *
cwc_entry_create(void *opaque)
{
return cwc_record_build(cwc_entry_find(NULL, 1));
}
/**
*
*/
static const dtable_class_t cwc_dtc = {
.dtc_record_get = cwc_entry_get,
.dtc_record_get_all = cwc_entry_get_all,
.dtc_record_create = cwc_entry_create,
.dtc_record_update = cwc_entry_update,
.dtc_record_delete = cwc_entry_delete,
};
/**
*
@ -964,29 +1192,12 @@ cwc_set_enable(cwc_t *cwc, int enabled)
void
cwc_init(void)
{
struct config_head cl;
config_entry_t *ce;
char buf[400];
dtable_t *dt;
TAILQ_INIT(&cwcs);
snprintf(buf, sizeof(buf), "%s/cwc.cfg", settings_dir);
TAILQ_INIT(&cl);
config_read_file0(buf, &cl);
TAILQ_FOREACH(ce, &cl, ce_link) {
if(ce->ce_type != CFG_SUB || strcasecmp("cwc", ce->ce_key))
continue;
cwc_add(config_get_str_sub(&ce->ce_sub, "hostname", NULL),
config_get_str_sub(&ce->ce_sub, "port", NULL),
config_get_str_sub(&ce->ce_sub, "username", NULL),
config_get_str_sub(&ce->ce_sub, "password", NULL),
config_get_str_sub(&ce->ce_sub, "deskey", NULL),
config_get_str_sub(&ce->ce_sub, "enabled", NULL),
0, 0);
}
dt = dtable_create(&cwc_dtc, "cwc", NULL);
dtable_load(dt);
}
/**

10
cwc.h
View file

@ -54,17 +54,21 @@ typedef struct cwc {
/* From configuration */
uint8_t cwc_confedkey[14];
const char *cwc_username;
const char *cwc_password; /* salted version */
char *cwc_username;
char *cwc_password;
char *cwc_password_salted; /* salted version */
char *cwc_comment;
dtimer_t cwc_idle_timer;
dtimer_t cwc_send_ka_timer;
int cwc_id;
char *cwc_id;
const char *cwc_errtxt;
int cwc_enabled;
} cwc_t;

192
dtable.c Normal file
View file

@ -0,0 +1,192 @@
/**
* Dtable (dyanmic, data, etc) table
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <ctype.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <libhts/htssettings.h>
#include "tvhead.h"
#include "dtable.h"
static LIST_HEAD(, dtable) dtables;
/**
*
*/
dtable_t *
dtable_create(const dtable_class_t *dtc, const char *name, void *opaque)
{
dtable_t *dt = calloc(1, sizeof(dtable_t));
dt->dt_opaque = opaque;
dt->dt_tablename = strdup(name);
dt->dt_dtc = dtc;
LIST_INSERT_HEAD(&dtables, dt, dt_link);
return dt;
}
/**
*
*/
int
dtable_load(dtable_t *dt)
{
htsmsg_t *l, *c, *m;
htsmsg_field_t *f;
const char *id;
int records = 0;
if((l = hts_settings_load("%s", dt->dt_tablename)) != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_msg_by_field(f)) == NULL)
continue;
if((id = htsmsg_get_str(c, "id")) == NULL)
continue;
m = dt->dt_dtc->dtc_record_update(dt->dt_opaque, id, c, 1);
if(m != NULL) {
records++;
htsmsg_destroy(m);
}
}
}
return records;
}
/**
*
*/
dtable_t *
dtable_find(const char *name)
{
dtable_t *dt;
LIST_FOREACH(dt, &dtables, dt_link)
if(!strcmp(dt->dt_tablename, name))
break;
return dt;
}
/**
*
*/
int
dtable_record_update_by_array(dtable_t *dt, htsmsg_t *msg)
{
htsmsg_t *c, *update;
htsmsg_field_t *f;
const char *id;
TAILQ_FOREACH(f, &msg->hm_fields, hmf_link) {
if((c = htsmsg_get_msg_by_field(f)) == NULL)
continue;
if((id = htsmsg_get_str(c, "id")) == NULL)
continue;
if((update = dt->dt_dtc->dtc_record_update(dt->dt_opaque, id, c, 0))
!= NULL) {
/* Data changed */
hts_settings_save(update, "%s/%s", dt->dt_tablename, id);
htsmsg_destroy(update);
}
}
return 0;
}
/**
*
*/
void
dtable_record_delete(dtable_t *dt, const char *id)
{
dt->dt_dtc->dtc_record_delete(dt->dt_opaque, id);
hts_settings_remove("%s/%s", dt->dt_tablename, id);
}
/**
*
*/
int
dtable_record_delete_by_array(dtable_t *dt, htsmsg_t *msg)
{
htsmsg_field_t *f;
const char *id;
TAILQ_FOREACH(f, &msg->hm_fields, hmf_link) {
if((id = htsmsg_field_get_string(f)) != NULL)
dtable_record_delete(dt, id);
}
return 0;
}
/**
*
*/
htsmsg_t *
dtable_record_create(dtable_t *dt)
{
htsmsg_t *r;
const char *id;
if((r = dt->dt_dtc->dtc_record_create(dt->dt_opaque)) == NULL)
return NULL;
if((id = htsmsg_get_str(r, "id")) == NULL) {
htsmsg_destroy(r);
return NULL;
}
hts_settings_save(r, "%s/%s", dt->dt_tablename, id);
return r;
}
/**
*
*/
htsmsg_t *
dtable_record_get_all(dtable_t *dt)
{
return dt->dt_dtc->dtc_record_get_all(dt->dt_opaque);
}
/**
*
*/
void
dtable_record_store(dtable_t *dt, const char *id, htsmsg_t *r)
{
hts_settings_save(r, "%s/%s", dt->dt_tablename, id);
}

72
dtable.h Normal file
View file

@ -0,0 +1,72 @@
/**
* Dtable (dyanmic, data, etc) table
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DTABLE_H__
#define DTABLE_H__
#include <libhts/htsmsg.h>
#include <libhts/htsbuf.h>
typedef struct dtable_class {
const char *dtc_name;
htsmsg_t *(*dtc_record_get_all)(void *opaque);
htsmsg_t *(*dtc_record_get)(void *opaque, const char *id);
htsmsg_t *(*dtc_record_create)(void *opaque);
htsmsg_t *(*dtc_record_update)(void *opaque, const char *id,
htsmsg_t *values, int maycreate);
int (*dtc_record_delete)(void *opaque, const char *id);
} dtable_class_t;
typedef struct dtable {
LIST_ENTRY(dtable) dt_link;
void *dt_opaque;
char *dt_tablename;
const dtable_class_t *dt_dtc;
} dtable_t;
dtable_t *dtable_create(const dtable_class_t *dtc, const char *name,
void *opaque);
int dtable_load(dtable_t *dt);
dtable_t *dtable_find(const char *name);
int dtable_record_update_by_array(dtable_t *dt, htsmsg_t *msg);
void dtable_record_delete(dtable_t *dt, const char *id);
int dtable_record_delete_by_array(dtable_t *dt, htsmsg_t *msg);
htsmsg_t *dtable_record_create(dtable_t *dt);
htsmsg_t *dtable_record_get_all(dtable_t *dt);
void dtable_record_store(dtable_t *dt, const char *id, htsmsg_t *r);
#endif /* DTABLE_H__ */

294
dvb.c
View file

@ -34,7 +34,7 @@
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <libhts/htscfg.h>
#include <libhts/htssettings.h>
#include "tvhead.h"
#include "dispatch.h"
@ -55,12 +55,11 @@ struct th_dvb_mux_instance_tree dvb_muxes;
static void dvb_mux_scanner(void *aux, int64_t now);
static void dvb_fec_monitor(void *aux, int64_t now);
static int dvb_tda_load(th_dvb_adapter_t *tda);
static void dvb_tdmi_load(th_dvb_mux_instance_t *tdmi);
static void dvb_transport_config_change(th_transport_t *t);
static void dvb_transport_save(th_transport_t *t);
static const char *dvb_source_name(th_transport_t *t);
static int dvb_transport_quality(th_transport_t *t);
static th_dvb_adapter_t *
tda_alloc(void)
{
@ -86,7 +85,8 @@ dvb_add_adapter(const char *path)
fe = open(fname, O_RDWR | O_NONBLOCK);
if(fe == -1) {
if(errno != ENOENT)
syslog(LOG_ALERT, "Unable to open %s -- %s\n", fname, strerror(errno));
tvhlog(LOG_ALERT, "dvb",
"Unable to open %s -- %s\n", fname, strerror(errno));
return;
}
@ -104,7 +104,7 @@ dvb_add_adapter(const char *path)
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
if(ioctl(tda->tda_fe_fd, FE_GET_INFO, tda->tda_fe_info)) {
syslog(LOG_ALERT, "%s: Unable to query adapter\n", fname);
tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter\n", fname);
close(fe);
free(tda);
return;
@ -134,11 +134,10 @@ dvb_add_adapter(const char *path)
tda->tda_displayname = strdup(tda->tda_fe_info->name);
syslog(LOG_INFO, "Found adapter %s (%s)", path, tda->tda_fe_info->name);
tvhlog(LOG_INFO, "dvb",
"Found adapter %s (%s)", path, tda->tda_fe_info->name);
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
dvb_tda_load(tda);
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
dvb_fe_start(tda);
}
@ -150,9 +149,10 @@ void
dvb_init(void)
{
char path[200];
struct dirent *d;
DIR *dir;
int i;
htsmsg_t *l, *c;
htsmsg_field_t *f;
const char *name, *s;
int i, type;
th_dvb_adapter_t *tda;
TAILQ_INIT(&dvb_adapters);
@ -162,37 +162,38 @@ dvb_init(void)
dvb_add_adapter(path);
}
/* Load any stale adapters */
l = hts_settings_load("dvbadapters");
if(l != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_msg_by_field(f)) == NULL)
continue;
if(dvb_adapter_find_by_identifier(f->hmf_name) != NULL) {
/* Already loaded */
continue;
}
snprintf(path, sizeof(path), "%s/dvbadapters", settings_dir);
if((name = htsmsg_get_str(c, "displayname")) == NULL)
continue;
if((dir = opendir(path)) == NULL)
return;
if((s = htsmsg_get_str(c, "type")) == NULL ||
(type = dvb_str_to_adaptertype(s)) < 0)
continue;
while((d = readdir(dir)) != NULL) {
if(d->d_name[0] != '_')
continue;
tda = tda_alloc();
tda->tda_identifier = strdup(f->hmf_name);
tda->tda_displayname = strdup(name);
tda->tda_type = type;
if(dvb_adapter_find_by_identifier(d->d_name) != NULL) {
/* Already loaded */
printf("%s is already loaded\n", d->d_name);
continue;
}
tda = tda_alloc();
tda = calloc(1, sizeof(th_dvb_adapter_t));
tda->tda_identifier = strdup(d->d_name);
printf("Loading stale adapter %s\n", tda->tda_identifier);
if(dvb_tda_load(tda) == 0) {
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
} else {
printf("Error while loading adapter %s -- settings file is corrupt\n",
tda->tda_identifier);
}
htsmsg_destroy(l);
}
closedir(dir);
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
dvb_tdmi_load(tda);
}
@ -232,7 +233,7 @@ dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
t->tht_start_feed = dvb_start_feed;
t->tht_stop_feed = dvb_stop_feed;
t->tht_config_change = dvb_transport_config_change;
t->tht_config_change = dvb_transport_save;
t->tht_sourcename = dvb_source_name;
t->tht_dvb_mux_instance = tdmi;
t->tht_quality_index = dvb_transport_quality;
@ -242,6 +243,47 @@ dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
}
/**
*
*/
static void
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "quality", 100 + tdmi->tdmi_quality * 2);
notify_by_msg("dvbmux", m);
}
/**
*
*/
static void
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_str(m, "status", tdmi->tdmi_last_status);
notify_by_msg("dvbmux", m);
}
/**
*
*/
static void
dvb_adapter_notify_reload(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tda->tda_identifier);
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("dvbadapter", m);
}
/**
*
@ -271,7 +313,7 @@ dvb_fec_monitor(void *aux, int64_t now)
if(v == TDMI_FEC_ERR_HISTOGRAM_SIZE) {
if(LIST_FIRST(&tda->tda_transports) != NULL) {
syslog(LOG_ERR,
tvhlog(LOG_ERR, "dvb",
"\"%s\": Constant rate of FEC errors (average at %d / s), "
"last %d seconds, flushing subscribers\n",
tdmi->tdmi_identifier, vv,
@ -286,13 +328,13 @@ dvb_fec_monitor(void *aux, int64_t now)
if(dvb_mux_status(tdmi, 1) != NULL) {
if(tdmi->tdmi_quality > -50) {
tdmi->tdmi_quality--;
notify_tdmi_qual_change(tdmi);
dvb_notify_mux_quality(tdmi);
}
} else {
if(tdmi->tdmi_quality < 0) {
tdmi->tdmi_quality++;
notify_tdmi_qual_change(tdmi);
dvb_notify_mux_quality(tdmi);
}
}
@ -300,7 +342,7 @@ dvb_fec_monitor(void *aux, int64_t now)
if(s != tdmi->tdmi_last_status) {
tdmi->tdmi_last_status = s;
notify_tdmi_status_change(tdmi);
dvb_notify_mux_status(tdmi);
}
}
@ -374,7 +416,7 @@ tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b)
th_dvb_mux_instance_t *
dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
int polarisation, int switchport,
uint16_t tsid, const char *network, int flags)
uint16_t tsid, const char *network, const char *source)
{
th_dvb_mux_instance_t *tdmi;
static th_dvb_mux_instance_t *skel;
@ -430,11 +472,12 @@ dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp);
if(flags & DVB_MUX_SAVE) {
dvb_tda_save(tda);
notify_tda_change(tda);
} else if(flags & DVB_MUX_LOAD) {
dvb_tdmi_load(tdmi);
if(source != NULL) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source);
dvb_tdmi_save(tdmi);
dvb_adapter_notify_reload(tda);
}
return tdmi;
@ -483,44 +526,18 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_WAITING)
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
pthread_mutex_lock(&tda->tda_lock);
dvb_fe_flush(tdmi);
dvb_mux_unref(tdmi);
pthread_mutex_unlock(&tda->tda_lock);
dvb_tda_save(tda);
}
/**
* Save config for the given adapter
*/
void
dvb_tda_save(th_dvb_adapter_t *tda)
{
th_dvb_mux_instance_t *tdmi;
FILE *fp;
char buf[400];
snprintf(buf, sizeof(buf), "%s/dvbadapters/%s",
settings_dir, tda->tda_identifier);
if((fp = settings_open_for_write(buf)) == NULL)
return;
fprintf(fp, "type = %s\n", dvb_adaptertype_to_str(tda->tda_type));
fprintf(fp, "displayname = %s\n", tda->tda_displayname);
RB_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
fprintf(fp, "mux {\n");
dvb_mux_store(fp, tdmi);
fprintf(fp, "}\n");
}
fclose(fp);
}
#if 0
/**
* Load config for the given adapter
@ -588,7 +605,8 @@ dvb_tda_load(th_dvb_adapter_t *tda)
0);
if(v != NULL)
syslog(LOG_ALERT, "Unable to init saved mux on %s -- %s\n",
tvhlog(LOG_ALERT, "dvb",
"Unable to init saved mux on %s -- %s\n",
tda->tda_identifier, v);
}
config_free0(&cl);
@ -597,117 +615,36 @@ dvb_tda_load(th_dvb_adapter_t *tda)
config_free0(&cl);
return -1;
}
#endif
/**
* Save config for the given mux
*/
void
dvb_tdmi_save(th_dvb_mux_instance_t *tdmi)
{
th_transport_t *t;
FILE *fp;
char buf[400];
snprintf(buf, sizeof(buf), "%s/dvbmuxes/%s",
settings_dir, tdmi->tdmi_identifier);
if((fp = settings_open_for_write(buf)) == NULL)
return;
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
fprintf(fp, "service {\n");
fprintf(fp, "\tservice_id = %d\n", t->tht_dvb_service_id);
fprintf(fp, "\tpmt = %d\n", t->tht_pmt);
fprintf(fp, "\tstype = %d\n", t->tht_servicetype);
fprintf(fp, "\tscrambled = %d\n", t->tht_scrambled);
if(t->tht_provider != NULL)
fprintf(fp, "\tprovider = %s\n", t->tht_provider);
if(t->tht_svcname)
fprintf(fp, "\tservicename = %s\n", t->tht_svcname);
if(t->tht_chname)
fprintf(fp, "\tchannelname = %s\n", t->tht_chname);
fprintf(fp, "\tmapped = %d\n", t->tht_ch ? 1 : 0);
psi_save_transport(fp, t);
fprintf(fp, "}\n");
}
fclose(fp);
}
/**
* Load config for the given mux
*/
static void
dvb_tdmi_load(th_dvb_mux_instance_t *tdmi)
dvb_transport_save(th_transport_t *t)
{
struct config_head cl;
config_entry_t *ce;
char buf[400];
const char *v;
int sid, pmt;
th_transport_t *t;
htsmsg_t *m = htsmsg_create();
snprintf(buf, sizeof(buf), "%s/dvbmuxes/%s",
settings_dir, tdmi->tdmi_identifier);
TAILQ_INIT(&cl);
config_read_file0(buf, &cl);
htsmsg_add_u32(m, "service_id", t->tht_dvb_service_id);
htsmsg_add_u32(m, "pmt", t->tht_pmt);
htsmsg_add_u32(m, "stype", t->tht_servicetype);
htsmsg_add_u32(m, "scrambled", t->tht_scrambled);
TAILQ_FOREACH(ce, &cl, ce_link) {
if(ce->ce_type != CFG_SUB || strcasecmp("service", ce->ce_key))
continue;
if(t->tht_provider != NULL)
htsmsg_add_str(m, "provider", t->tht_provider);
sid = atoi(config_get_str_sub(&ce->ce_sub, "service_id", "0"));
pmt = atoi(config_get_str_sub(&ce->ce_sub, "pmt", "0"));
if(sid < 1 || pmt < 1)
continue;
t = dvb_find_transport(tdmi, sid, pmt, NULL);
t->tht_servicetype = atoi(config_get_str_sub(&ce->ce_sub, "stype", "0"));
t->tht_scrambled = atoi(config_get_str_sub(&ce->ce_sub, "scrambled", "0"));
if(t->tht_svcname != NULL)
htsmsg_add_str(m, "servicename", t->tht_svcname);
v = config_get_str_sub(&ce->ce_sub, "provider", "unknown");
free((void *)t->tht_provider);
t->tht_provider = strdup(v);
if(t->tht_chname != NULL)
htsmsg_add_str(m, "channelname", t->tht_chname);
v = config_get_str_sub(&ce->ce_sub, "servicename", "unknown");
free((void *)t->tht_svcname);
t->tht_svcname = strdup(v);
v = config_get_str_sub(&ce->ce_sub, "channelname", NULL);
if(v != NULL) {
free((void *)t->tht_chname);
t->tht_chname = strdup(v);
} else {
t->tht_chname = strdup(t->tht_svcname);
}
psi_load_transport(&ce->ce_sub, t);
if(atoi(config_get_str_sub(&ce->ce_sub, "mapped", "0"))) {
transport_map_channel(t, NULL);
}
}
config_free0(&cl);
htsmsg_add_u32(m, "mapped", !!t->tht_ch);
psi_get_transport_settings(m, t);
hts_settings_save(m, "dvbtransports/%s", t->tht_identifier);
htsmsg_destroy(m);
}
/**
* Called when config changes for the given transport
*/
static void
dvb_transport_config_change(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
dvb_tdmi_save(tdmi);
}
/**
* Called to get quality for the given transport
@ -762,7 +699,8 @@ dvb_tda_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
tdmi_src->tdmi_switchport,
tdmi_src->tdmi_transport_stream_id,
tdmi_src->tdmi_network,
0);
"copy operation");
assert(tdmi_dst != NULL);

8
dvb.h
View file

@ -50,18 +50,12 @@ void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi);
th_transport_t *dvb_find_transport(th_dvb_mux_instance_t *tdmi,
uint16_t sid, int pmt_pid, int *created);
#define DVB_MUX_SAVE 0x1
#define DVB_MUX_LOAD 0x2
th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
struct dvb_frontend_parameters *fe_param,
int polarisation, int switchport,
uint16_t tsid, const char *network,
int flags);
const char *logprefix);
void dvb_tdmi_save(th_dvb_mux_instance_t *tdmi);
void dvb_tda_save(th_dvb_adapter_t *tda);
void dvb_mux_unref(th_dvb_mux_instance_t *tdmi);

View file

@ -52,7 +52,8 @@ dvb_dvr_init(th_dvb_adapter_t *tda)
dvr = open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK);
if(dvr == -1) {
syslog(LOG_ALERT, "%s: unable to open dvr\n", tda->tda_dvr_path);
tvhlog(LOG_ALERT, "dvb",
"%s: unable to open dvr\n", tda->tda_dvr_path);
return -1;
}
@ -179,7 +180,7 @@ dvb_start_feed(th_transport_t *t, unsigned int weight, int status,
if(fd == -1) {
st->st_demuxer_fd = -1;
syslog(LOG_ERR,
tvhlog(LOG_ERR, "dvb",
"\"%s\" unable to open demuxer \"%s\" for pid %d -- %s",
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
continue;
@ -193,7 +194,7 @@ dvb_start_feed(th_transport_t *t, unsigned int weight, int status,
dmx_param.flags = DMX_IMMEDIATE_START;
if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) {
syslog(LOG_ERR,
tvhlog(LOG_ERR, "dvb",
"\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s",
t->tht_name, tda->tda_demux_path, pid, strerror(errno));
close(fd);

View file

@ -33,6 +33,7 @@
#include <linux/dvb/dmx.h>
#include <libhts/htscfg.h>
#include <syslog.h>
#include "tvhead.h"
#include "dispatch.h"
@ -217,7 +218,7 @@ tdmi_stop(th_dvb_mux_instance_t *tdmi)
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
tdmi->tdmi_state = TDMI_IDLE;
notify_tdmi_state_change(tdmi);
// notify_tdmi_state_change(tdmi);
time(&tdmi->tdmi_lost_adapter);
}
@ -234,7 +235,7 @@ dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state)
if(tdmi->tdmi_state != state) {
tdmi->tdmi_state = state;
notify_tdmi_state_change(tdmi);
// notify_tdmi_state_change(tdmi);
}
if(tda->tda_mux_current == tdmi)
@ -247,7 +248,8 @@ dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state)
if(maylog) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
syslog(LOG_DEBUG, "\"%s\" tuning to mux \"%s\"", tda->tda_rootpath, buf);
tvhlog(LOG_DEBUG, "dvb",
"\"%s\" tuning to mux \"%s\"", tda->tda_rootpath, buf);
}
/* Add tables which will be activated once the tuning is completed */

View file

@ -23,6 +23,8 @@
#include <string.h>
#include <ctype.h>
#include <libhts/htssettings.h>
#include <linux/dvb/frontend.h>
#include "tvhead.h"
@ -32,6 +34,58 @@
#include "dvb_muxconfig.h"
#include "dvb_support.h"
#include "transports.h"
#include "notify.h"
/**
* Save config for the given adapter
*/
void
dvb_tda_save(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
htsmsg_add_str(m, "displayname", tda->tda_displayname);
hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier);
htsmsg_destroy(m);
}
/**
*
*/
static htsmsg_t *
dvb_tda_createmsg(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tda->tda_identifier);
return m;
}
/**
*
*/
void
dvb_tda_set_displayname(th_dvb_adapter_t *tda, const char *s)
{
htsmsg_t *m = dvb_tda_createmsg(tda);
free(tda->tda_displayname);
tda->tda_displayname = strdup(s);
dvb_tda_save(tda);
htsmsg_add_str(m, "name", tda->tda_displayname);
notify_by_msg("dvbadapter", m);
}
static struct strtab fectab[] = {
{ "NONE", FEC_NONE },
@ -93,158 +147,165 @@ static struct strtab poltab[] = {
};
/**
*
*/
void
dvb_mux_store(FILE *fp, th_dvb_mux_instance_t *tdmi)
dvb_tdmi_save(th_dvb_mux_instance_t *tdmi)
{
struct dvb_frontend_parameters *f = &tdmi->tdmi_fe_params;
fprintf(fp, "\ttransportstreamid = %d\n", tdmi->tdmi_transport_stream_id);
if(tdmi->tdmi_network != NULL)
fprintf(fp, "\tnetwork = %s\n", tdmi->tdmi_network);
htsmsg_t *m = htsmsg_create();
htsmsg_add_u32(m, "transportstreamid", tdmi->tdmi_transport_stream_id);
if(tdmi->tdmi_network != NULL)
htsmsg_add_str(m, "network", tdmi->tdmi_network);
htsmsg_add_u32(m, "frequency", f->frequency);
fprintf(fp, "\tfrequency = %d\n", f->frequency);
switch(tdmi->tdmi_adapter->tda_type) {
case FE_OFDM:
fprintf(fp, "\tbandwidth = %s\n",
val2str(f->u.ofdm.bandwidth, bwtab));
htsmsg_add_str(m, "bandwidth",
val2str(f->u.ofdm.bandwidth, bwtab));
fprintf(fp, "\tconstellation = %s\n",
htsmsg_add_str(m, "constellation",
val2str(f->u.ofdm.constellation, qamtab));
fprintf(fp, "\ttransmission_mode = %s\n",
htsmsg_add_str(m, "transmission_mode",
val2str(f->u.ofdm.transmission_mode, modetab));
fprintf(fp, "\tguard_interval = %s\n",
htsmsg_add_str(m, "guard_interval",
val2str(f->u.ofdm.guard_interval, guardtab));
fprintf(fp, "\thierarchy = %s\n",
htsmsg_add_str(m, "hierarchy",
val2str(f->u.ofdm.hierarchy_information, hiertab));
fprintf(fp, "\tfec_hi = %s\n",
htsmsg_add_str(m, "fec_hi",
val2str(f->u.ofdm.code_rate_HP, fectab));
fprintf(fp, "\tfec_lo = %s\n",
htsmsg_add_str(m, "fec_lo",
val2str(f->u.ofdm.code_rate_LP, fectab));
break;
case FE_QPSK:
fprintf(fp, "\tsymbol_rate = %d\n", f->u.qpsk.symbol_rate);
htsmsg_add_u32(m, "symbol_rate", f->u.qpsk.symbol_rate);
fprintf(fp, "\tfec = %s\n",
htsmsg_add_str(m, "fec",
val2str(f->u.qpsk.fec_inner, fectab));
fprintf(fp, "\tpolarisation = %s\n",
htsmsg_add_str(m, "polarisation",
val2str(tdmi->tdmi_polarisation, poltab));
fprintf(fp, "\tswitchport = %d\n", tdmi->tdmi_switchport);
htsmsg_add_u32(m, "switchport", tdmi->tdmi_switchport);
break;
case FE_QAM:
fprintf(fp, "\tsymbol_rate = %d\n", f->u.qam.symbol_rate);
htsmsg_add_u32(m, "symbol_rate", f->u.qam.symbol_rate);
fprintf(fp, "\tfec = %s\n",
htsmsg_add_str(m, "fec",
val2str(f->u.qam.fec_inner, fectab));
fprintf(fp, "\tconstellation = %s\n",
htsmsg_add_str(m, "constellation",
val2str(f->u.qam.modulation, qamtab));
break;
case FE_ATSC:
break;
}
hts_settings_save(m, "dvbmuxes/%s/%s",
tdmi->tdmi_adapter->tda_identifier, tdmi->tdmi_identifier);
htsmsg_destroy(m);
}
/**
*
*/
const char *
dvb_mux_create_str(th_dvb_adapter_t *tda,
const char *tsidstr,
const char *network,
const char *freqstr,
const char *symratestr,
const char *qamstr,
const char *fecstr,
const char *fechistr,
const char *feclostr,
const char *bwstr,
const char *tmodestr,
const char *guardstr,
const char *hierstr,
const char *polstr,
const char *switchportstr,
int save)
static const char *
dvb_tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m)
{
struct dvb_frontend_parameters f;
const char *s;
int r;
int polarisation = 0, switchport = 0;
int polarisation = 0;
unsigned int switchport = 0;
unsigned int tsid;
memset(&f, 0, sizeof(f));
f.inversion = INVERSION_AUTO;
htsmsg_get_u32(m, "frequency", &f.frequency);
f.frequency = freqstr ? atoi(freqstr) : 0;
if(f.frequency == 0)
return "Invalid frequency";
switch(tda->tda_type) {
case FE_OFDM:
if(bwstr == NULL || (r = str2val(bwstr, bwtab)) < 0)
s = htsmsg_get_str(m, "bandwidth");
if(s == NULL || (r = str2val(s, bwtab)) < 0)
return "Invalid bandwidth";
f.u.ofdm.bandwidth = r;
if(qamstr == NULL || (r = str2val(qamstr, qamtab)) < 0)
s = htsmsg_get_str(m, "constellation");
if(s == NULL || (r = str2val(s, qamtab)) < 0)
return "Invalid QAM constellation";
f.u.ofdm.constellation = r;
if(tmodestr == NULL || (r = str2val(tmodestr, modetab)) < 0)
s = htsmsg_get_str(m, "transmission_mode");
if(s == NULL || (r = str2val(s, modetab)) < 0)
return "Invalid transmission mode";
f.u.ofdm.transmission_mode = r;
if(guardstr == NULL || (r = str2val(guardstr, guardtab)) < 0)
s = htsmsg_get_str(m, "guard_interval");
if(s == NULL || (r = str2val(s, guardtab)) < 0)
return "Invalid guard interval";
f.u.ofdm.guard_interval = r;
if(hierstr == NULL || (r = str2val(hierstr, hiertab)) < 0)
s = htsmsg_get_str(m, "hierarchy");
if(s == NULL || (r = str2val(s, hiertab)) < 0)
return "Invalid heirarchy information";
f.u.ofdm.hierarchy_information = r;
if(fechistr == NULL || (r = str2val(fechistr, fectab)) < 0)
printf("hifec = %s\n", fechistr);
s = htsmsg_get_str(m, "fec_hi");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid hi-FEC";
f.u.ofdm.code_rate_HP = r;
if(feclostr == NULL || (r = str2val(feclostr, fectab)) < 0)
s = htsmsg_get_str(m, "fec_lo");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid lo-FEC";
f.u.ofdm.code_rate_LP = r;
break;
case FE_QPSK:
f.u.qpsk.symbol_rate = symratestr ? atoi(symratestr) : 0;
htsmsg_get_u32(m, "symbol_rate", &f.u.qpsk.symbol_rate);
if(f.u.qpsk.symbol_rate == 0)
return "Invalid symbol rate";
if(fecstr == NULL || (r = str2val(fecstr, fectab)) < 0)
s = htsmsg_get_str(m, "fec");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid FEC";
f.u.qpsk.fec_inner = r;
if(polstr == NULL || (r = str2val(polstr, poltab)) < 0)
s = htsmsg_get_str(m, "polarisation");
if(s == NULL || (r = str2val(s, poltab)) < 0)
return "Invalid polarisation";
polarisation = r;
switchport = atoi(switchportstr ?: "0");
htsmsg_get_u32(m, "switchport", &switchport);
break;
case FE_QAM:
f.u.qam.symbol_rate = symratestr ? atoi(symratestr) : 0;
htsmsg_get_u32(m, "symbol_rate", &f.u.qam.symbol_rate);
if(f.u.qam.symbol_rate == 0)
return "Invalid symbol rate";
if(qamstr == NULL || (r = str2val(qamstr, qamtab)) < 0)
s = htsmsg_get_str(m, "constellation");
if(s == NULL || (r = str2val(s, qamtab)) < 0)
return "Invalid QAM constellation";
f.u.qam.modulation = r;
if(fecstr == NULL || (r = str2val(fecstr, fectab)) < 0)
s = htsmsg_get_str(m, "fec");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid FEC";
f.u.qam.fec_inner = r;
break;
@ -253,45 +314,60 @@ dvb_mux_create_str(th_dvb_adapter_t *tda,
break;
}
dvb_mux_create(tda, &f, polarisation, switchport, atoi(tsidstr),
network, save ? DVB_MUX_SAVE : DVB_MUX_LOAD);
if(htsmsg_get_u32(m, "transportstreamid", &tsid))
tsid = 0xffff;
dvb_mux_create(tda, &f, polarisation, switchport,
tsid, htsmsg_get_str(m, "network"), NULL);
return NULL;
}
#include "linuxtv_muxes.h"
int
dvb_mux_preconf_get(unsigned int n, const char **namep, const char **commentp)
/**
*
*/
void
dvb_tdmi_load(th_dvb_adapter_t *tda)
{
if(n >= sizeof(networks) / sizeof(networks[0]))
return -1;
htsmsg_t *l, *c;
htsmsg_field_t *f;
if(namep != NULL)
*namep = networks[n].name;
if(commentp != NULL)
*commentp = networks[n].comment;
return networks[n].type;
if((l = hts_settings_load("dvbmuxes/%s", tda->tda_identifier)) == NULL)
return;
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_msg_by_field(f)) == NULL)
continue;
dvb_tdmi_create_by_msg(tda, c);
}
htsmsg_destroy(l);
}
int
dvb_mux_preconf_add(th_dvb_adapter_t *tda, unsigned int n)
/**
* A big list of all known DVB networks (from linuxtv.org)
*/
#include "linuxtv_muxes.h"
/**
*
*/
static void
dvb_mux_preconf_add(th_dvb_adapter_t *tda, const struct mux *m, int num,
const char *source)
{
struct dvb_frontend_parameters f;
struct mux *m;
int i;
int polarisation, switchport;
int polarisation;
int switchport;
if(n >= sizeof(networks) / sizeof(networks[0]))
return -1;
printf("m = %p, num = %d\n", m, num);
m = networks[n].muxes;
for(i = 0; i < networks[n].nmuxes; i++) {
for(i = 0; i < num; i++) {
polarisation = 0;
switchport = 0;
@ -337,9 +413,114 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, unsigned int n)
}
dvb_mux_create(tda, &f, polarisation, switchport, 0xffff, NULL,
DVB_MUX_LOAD);
source);
m++;
}
dvb_tda_save(tda);
}
/**
*
*/
int
dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id)
{
const struct region *r;
const struct network *n;
int nr, nn, i, j;
char source[100];
snprintf(source, sizeof(source), "built-in configuration from \"%s\"", id);
switch(tda->tda_type) {
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;
default:
return -1;
}
for(i = 0; i < nr; i++) {
n = r[i].networks;
nn = r[i].nnetworks;
for(j = 0; j < nn; j++) {
if(!strcmp(n[j].name, id)) {
dvb_mux_preconf_add(tda, n[j].muxes, n[j].nmuxes, source);
break;
}
}
}
return 0;
}
/**
*
*/
htsmsg_t *
dvb_mux_preconf_get_node(int fetype, const char *node)
{
const struct region *r;
const struct network *n;
int nr, nn, i;
htsmsg_t *out, *e;
switch(fetype) {
case FE_QAM:
r = regions_DVBC;
nr = sizeof(regions_DVBC) / sizeof(regions_DVBC[0]);
break;
case FE_QPSK:
r = regions_DVBS;
nr = sizeof(regions_DVBS) / sizeof(regions_DVBS[0]);
break;
case FE_OFDM:
r = regions_DVBT;
nr = sizeof(regions_DVBT) / sizeof(regions_DVBT[0]);
break;
default:
return NULL;
}
out = htsmsg_create_array();
if(!strcmp(node, "root")) {
for(i = 0; i < nr; i++) {
e = htsmsg_create();
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();
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;
}

View file

@ -19,7 +19,13 @@
#ifndef DVB_MUXCONFIG_H_
#define DVB_MUXCONFIG_H_
void dvb_mux_store(FILE *fp, th_dvb_mux_instance_t *tdmi);
#include <libhts/htsmsg.h>
void dvb_tda_save(th_dvb_adapter_t *tda);
void dvb_tda_set_displayname(th_dvb_adapter_t *tda, const char *s);
void dvb_tdmi_save(th_dvb_mux_instance_t *tdmi);
const char *dvb_mux_create_str(th_dvb_adapter_t *tda,
const char *tsidstr,
@ -38,9 +44,10 @@ const char *dvb_mux_create_str(th_dvb_adapter_t *tda,
const char *switchportstr,
int save);
int dvb_mux_preconf_get(unsigned int n, const char **namep,
const char **commentp);
htsmsg_t *dvb_mux_preconf_get_node(int fetype, const char *node);
int dvb_mux_preconf_add(th_dvb_adapter_t *tda, unsigned int n);
int dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id);
void dvb_tdmi_load(th_dvb_adapter_t *tda);
#endif /* DVB_MUXCONFIG_H */

View file

@ -292,18 +292,45 @@ dvb_mux_status(th_dvb_mux_instance_t *tdmi, int nullisok)
return txt;
}
/**
*
*/
static const char *
nicenum(char *x, size_t siz, unsigned int v)
{
if(v < 1000)
snprintf(x, siz, "%d", v);
else if(v < 1000000)
snprintf(x, siz, "%d,%03d", v / 1000, v % 1000);
else if(v < 1000000000)
snprintf(x, siz, "%d,%03d,%03d",
v / 1000000, (v % 1000000) / 1000, v % 1000);
else
snprintf(x, siz, "%d,%03d,%03d,%03d",
v / 1000000000, (v % 1000000000) / 1000000,
(v % 1000000) / 1000, v % 1000);
return x;
}
/**
*
*/
void
dvb_mux_nicename(char *buf, size_t size, th_dvb_mux_instance_t *tdmi)
{
if(tdmi->tdmi_adapter->tda_type == FE_QPSK)
snprintf(buf, size, "%dkHz %s port %d", tdmi->tdmi_fe_params.frequency,
char freq[50];
if(tdmi->tdmi_adapter->tda_type == FE_QPSK) {
nicenum(freq, sizeof(freq), tdmi->tdmi_fe_params.frequency);
snprintf(buf, size, "%s kHz %s port %d", freq,
dvb_polarisation_to_str_long(tdmi->tdmi_polarisation),
tdmi->tdmi_switchport);
else
snprintf(buf, size, "%dHz", tdmi->tdmi_fe_params.frequency);
} else {
nicenum(freq, sizeof(freq), tdmi->tdmi_fe_params.frequency / 1000);
snprintf(buf, size, "%s kHz", freq);
}
}
/**

View file

@ -36,6 +36,7 @@
#include "tvhead.h"
#include "dispatch.h"
#include "dvb.h"
#include "dvb_muxconfig.h"
#include "dvb_support.h"
#include "epg.h"
#include "transports.h"
@ -438,7 +439,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
}
if(change) {
dvb_tdmi_save(tdmi);
notify_tdmi_services_change(tdmi);
// notify_tdmi_services_change(tdmi);
}
}
@ -567,7 +568,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
fe_param.u.qam.fec_inner = fec_tab[ptr[10] & 0x07];
dvb_mux_create(tdmi->tdmi_adapter, &fe_param, 0, 0, tsid, NULL,
DVB_MUX_SAVE);
"automatic mux discovery");
}
/**
@ -601,7 +602,8 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
pol = (ptr[6] >> 5) & 0x03;
dvb_mux_create(tdmi->tdmi_adapter, &fe_param, pol, tdmi->tdmi_switchport,
tsid, NULL, DVB_MUX_SAVE);
tsid, NULL,
"automatic mux discovery");
}
@ -644,7 +646,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(strcmp(tdmi->tdmi_network ?: "", networkname)) {
free((void *)tdmi->tdmi_network);
tdmi->tdmi_network = strdup(networkname);
notify_tdmi_name_change(tdmi);
//notify_tdmi_name_change(tdmi);
dvb_tda_save(tdmi->tdmi_adapter);
}
break;
@ -712,7 +714,7 @@ dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
v ^= t->tht_pmt_seen;
if(v) {
dvb_tdmi_save(tdmi);
notify_tdmi_services_change(tdmi);
//notify_tdmi_services_change(tdmi);
}
return;
}

View file

@ -33,6 +33,7 @@
#include <libxml/tree.h>
#include <libhts/htscfg.h>
#include <syslog.h>
#include "tvhead.h"
#include "channels.h"
@ -304,7 +305,7 @@ xmltv_transfer_events(xmltv_grabber_t *xg)
how = 1;
}
if(strcmp(xc->xc_bestmatch ?: "", ch->ch_name)) {
syslog(LOG_DEBUG,
tvhlog(LOG_DEBUG, "xmltv",
"xmltv: mapped \"%s\" (%s) to \"%s\" by %s",
xc->xc_displayname, xc->xc_name, ch->ch_name,
how ? "consequtive-event-matching" : "name");
@ -608,7 +609,7 @@ icom_cb(void *opaque, htsmsg_t *m)
xg->xg_status = v;
dtimer_arm(&xg->xg_grab_timer, regrab, xg, v == XMLTV_GRAB_OK ? 3600 : 60);
dtimer_arm(&xg->xg_xfer_timer, xmltv_xfer, xg, 1);
notify_xmltv_grabber_status_change(xg);
// notify_xmltv_grabber_status_change(xg);
}
htsmsg_destroy(m);
}

View file

@ -29,6 +29,7 @@
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <syslog.h>
#include <libavformat/avformat.h>
#include <libavutil/avstring.h>
@ -94,7 +95,8 @@ tffm_set_state(th_ffmuxer_t *tffm, int status)
break;
}
syslog(LOG_INFO, "%s - Entering state \"%s\"", tffm->tffm_printname, tp);
syslog(LOG_INFO,
"%s - Entering state \"%s\"", tffm->tffm_printname, tp);
tffm->tffm_state = status;
}
@ -151,7 +153,7 @@ tffm_open(th_ffmuxer_t *tffm, th_transport_t *t,
codec = avcodec_find_decoder(codec_id);
if(codec == NULL) {
syslog(LOG_ERR,
tvhlog(LOG_ERR,
"%s - Cannot find codec for %s, ignoring stream",
printname, codec_name);
continue;

View file

@ -33,6 +33,8 @@
#include <inttypes.h>
#include <syslog.h>
#include <libhts/htscfg.h>
#include "tvhead.h"

View file

@ -274,7 +274,8 @@ cr_streamport(client_t *c, char **argv, int argc)
c->c_ipaddr.s_addr = inet_addr(argv[0]);
c->c_port = atoi(argv[1]);
syslog(LOG_INFO, "%s registers UDP stream target %s:%d",
tvhlog(LOG_INFO, "htsclient",
"%s registers UDP stream target %s:%d",
tcp_logname(&c->c_tcp_session), inet_ntoa(c->c_ipaddr), c->c_port);
return 0;

View file

@ -31,6 +31,7 @@
#include <arpa/inet.h>
#include <errno.h>
#include <linux/netdevice.h>
#include <syslog.h>
#include <libhts/htscfg.h>

View file

@ -28,6 +28,7 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <syslog.h>
#include "tvhead.h"
#include "iptv_output.h"

File diff suppressed because it is too large Load diff

46
main.c
View file

@ -26,6 +26,7 @@
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <syslog.h>
#include <pwd.h>
#include <grp.h>
@ -59,12 +60,14 @@
#include "spawn.h"
#include "ffmuxer.h"
#include "xbmsp.h"
#include "ajaxui/ajaxui.h"
//#include "ajaxui/ajaxui.h"
#include "webui/webui.h"
#include "access.h"
#include "serviceprobe.h"
#include <libhts/htsparachute.h>
#include <libhts/htssettings.h>
int running;
int startupcounter;
@ -189,6 +192,9 @@ main(int argc, char **argv)
openlog("tvheadend", LOG_PID, logfacility);
hts_settings_init("tvheadend2");
if(settingspath == NULL && (homedir = getenv("HOME")) != NULL) {
snprintf(buf2, sizeof(buf2), "%s/.hts", homedir);
@ -273,14 +279,14 @@ main(int argc, char **argv)
autorec_init();
epg_init();
xmltv_init();
// xmltv_init();
subscriptions_init();
// htmlui_start();
webui_start();
ajaxui_start();
// ajaxui_start();
avgen_init();
@ -305,7 +311,7 @@ main(int argc, char **argv)
output_multicast_setup();
client_start();
p = atoi(config_get_str("http-server-port", "9980"));
p = atoi(config_get_str("http-server-port", "9981"));
if(p)
http_start(p);
@ -433,3 +439,35 @@ settings_open_for_write(const char *name)
return fp;
}
/**
*
*/
void
tvhlog(int severity, const char *subsys, const char *fmt, ...)
{
va_list ap;
htsmsg_t *m;
char buf[512];
int l;
l = snprintf(buf, sizeof(buf), "%s: ", subsys);
va_start(ap, fmt);
vsnprintf(buf + l, sizeof(buf) - l, fmt, ap);
va_end(ap);
syslog(severity, "%s", buf);
/**
*
*/
m = htsmsg_create();
htsmsg_add_str(m, "notificationClass", "logmessage");
htsmsg_add_str(m, "logtxt", buf);
comet_mailbox_add_message(m);
htsmsg_destroy(m);
}

View file

@ -16,19 +16,18 @@ echo char hierarchy\;
echo char polarisation\;
echo }\;
find $1/dvb-s -type f | sort | xargs ./muxbuilder 1 FE_QPSK
find $1/dvb-t -type f | sort | xargs ./muxbuilder 1 FE_OFDM
find $1/dvb-c -type f | sort | xargs ./muxbuilder 1 FE_QAM
echo struct {
echo int type\;
echo struct network {
echo const char *name\;
echo struct mux *muxes\;
echo int nmuxes\;
echo const char *comment\;
echo } networks[] = {
find $1/dvb-s -type f | sort | xargs ./muxbuilder 2 FE_QPSK
find $1/dvb-t -type f | sort | xargs ./muxbuilder 2 FE_OFDM
find $1/dvb-c -type f | sort | xargs ./muxbuilder 2 FE_QAM
echo }\;
echo const struct mux *muxes\;
echo const int nmuxes\;
echo }\;
echo struct region {
echo const char *name\;
echo const struct network *networks\;
echo const int nnetworks\;
echo }\;
find $1/dvb-s -type f | sort | xargs ./muxbuilder DVBS
find $1/dvb-t -type f | sort | xargs ./muxbuilder DVBT
find $1/dvb-c -type f | sort | xargs ./muxbuilder DVBC

View file

@ -6,7 +6,6 @@
#include <strings.h>
#include <ctype.h>
int pass;
char *type;
struct strtab {
@ -163,16 +162,143 @@ dvb_c_config(const char *l)
/**
*
*/
typedef struct network {
char *structname;
char *displayname;
char *comment;
struct network *next;
} network_t;
typedef struct region {
char *shortname;
char *longname;
struct network *networks;
struct region *next;
} region_t;
region_t *regions;
static region_t *
find_region(const char *name, const char *longname)
{
region_t *c, *n, **p;
for(c = regions; c != NULL; c = c->next)
if(!strcmp(name, c->shortname))
return c;
n = malloc(sizeof(region_t));
n->shortname = strdup(name);
n->longname = strdup(longname);
n->networks = NULL;
if(regions == NULL)
regions = n;
else {
p = &regions;
while((c = *p) != NULL) {
if(strcmp(c->longname, longname) > 0)
break;
p = &c->next;
}
n->next = *p;
*p = n;
}
return n;
}
static network_t *
add_network(region_t *x, const char *name)
{
network_t *m, *n, **p;
n = calloc(1, sizeof(network_t));
n->structname = strdup(name);
if(x->networks == NULL)
x->networks = n;
else {
p = &x->networks;
while((m = *p) != NULL) {
if(strcmp(m->structname, name) > 0)
break;
p = &m->next;
}
n->next = *p;
*p = n;
}
return n;
}
static const struct {
const char *code;
const char *name;
} tldlist[] = {
{"at", "Austria"},
{"au", "Australia"},
{"be", "Belgium"},
{"ch", "Switzerland"},
{"cz", "Czech Republic"},
{"de", "Germany"},
{"dk", "Denmark"},
{"es", "Spain"},
{"fi", "Finland"},
{"fr", "France"},
{"gr", "Greece"},
{"hr", "Croatia"},
{"is", "Iceland"},
{"it", "Italy"},
{"lu", "Luxembourg"},
{"lv", "Latvia"},
{"nl", "Netherlands"},
{"no", "Norway"},
{"nz", "New Zealand"},
{"pl", "Poland"},
{"se", "Sweden"},
{"sk", "Slovakia"},
{"tw", "Taiwan"},
{"uk", "United Kingdom"},
};
static const char *
tldcode2longname(const char *tld)
{
int i;
for(i = 0; i < sizeof(tldlist) / sizeof(tldlist[0]); i++)
if(!strcmp(tld, tldlist[i].code))
return tldlist[i].name;
fprintf(stderr, "Unable to translate tld %s\n", tld);
exit(1);
}
static void
convert_file(char *fname)
scan_file(char *fname)
{
FILE *fp;
char line[200];
int l;
char c, *s;
char smartname[200];
const char *bn;
char *bn;
int gotcomment = 0;
region_t *co;
network_t *ne;
char *name, *displayname;
char buf[100];
fp = fopen(fname, "r");
if(fp == NULL) {
@ -188,10 +314,35 @@ convert_file(char *fname)
}
smartname[l] = 0;
if(pass == 1) {
printf("struct mux muxlist_%s_%s[] = {\n", type, smartname);
name = basename(fname);
if(!strcmp(type, "DVBS")) {
displayname = name;
co = find_region("geo", "Geosynchronous Orbit");
} else {
displayname = name + 3;
buf[0] = name[0];
buf[1] = name[1];
buf[2] = 0;
co = find_region(buf, tldcode2longname(buf));
}
snprintf(buf, sizeof(buf), "%s_%s", type, smartname);
printf("static const struct mux muxes_%s[] = {\n", buf);
ne = add_network(co, buf);
bn = ne->displayname = strdup(displayname);
while(*bn) {
if(*bn == '_') *bn = ' ';
bn++;
}
// ne->muxlistname = strdup(buf);
#if 0
if(pass == 2) {
printf("{\n");
printf("\t.type = %s,\n", type);
@ -201,6 +352,7 @@ convert_file(char *fname)
type, smartname);
printf("\t.comment = ");
}
#endif
while(!feof(fp)) {
memset(line, 0, sizeof(line));
@ -217,65 +369,116 @@ convert_file(char *fname)
if(gotcomment)
break;
if(pass != 2)
break;
s = line + 2;
if(strstr(s, " freq "))
break;
printf("\"%s\"\n", s);
// ne->comment = strdup(s);
gotcomment = 1;
break;
case 'C':
if(pass == 1)
dvb_c_config(line + 1);
dvb_c_config(line + 1);
break;
case 'T':
if(pass == 1)
dvb_t_config(line + 1);
dvb_t_config(line + 1);
break;
case 'S':
if(pass == 1)
dvb_s_config(line + 1);
dvb_s_config(line + 1);
break;
default:
break;
}
}
if(pass == 2) {
if(gotcomment == 0)
printf("\"\"\n");
printf("},\n");
}
if(pass == 1) {
printf("};\n\n");
}
printf("};\n\n");
fclose(fp);
}
static void
dump_networks(region_t *c)
{
network_t *n;
printf("static const struct network networks_%s_%s[] = {\n",
type, c->shortname);
for(n = c->networks; n != NULL; n = n->next) {
printf("\t{\n");
printf("\t\t.name = \"%s\",\n", n->displayname);
printf("\t\t.muxes = muxes_%s,\n", n->structname);
printf("\t\t.nmuxes = sizeof(muxes_%s) / sizeof(struct mux),\n",
n->structname);
if(n->comment)
printf("\t\t.comment = \"%s\",\n", n->comment);
printf("\t},\n");
}
printf("};\n\n");
}
static void
dump_regions(void)
{
region_t *r;
printf("static const struct region regions_%s[] = {\n", type);
for(r = regions; r != NULL; r = r->next) {
printf("\t{\n");
printf("\t\t.name = \"%s\",\n", r->longname);
printf("\t\t.networks = networks_%s_%s,\n", type, r->shortname);
printf("\t\t.nnetworks = sizeof(networks_%s_%s) / sizeof(struct network),\n",
type, r->shortname);
printf("\t},\n");
}
printf("};\n\n");
}
int
main(int argc, char **argv)
{
int i;
region_t *c;
if(argc < 3)
if(argc < 2)
return 1;
pass = atoi(argv[1]);
if(pass < 1 || pass > 2)
return 2;
type = argv[1];
#if 0
printf("struct mux {\n"
"unsigned int freq;\n"
"unsigned int symrate;\n"
"char fec;\n"
"char constellation;\n"
"char bw;\n"
"char fechp;\n"
"char feclp;\n"
"char tmode;\n"
"char guard;\n"
"char hierarchy;\n"
"char polarisation;\n"
"}\n");
#endif
type = argv[2];
for(i = 2; i < argc; i++)
scan_file(argv[i]);
for(i = 3; i < argc; i++) {
convert_file(argv[i]);
for(c = regions; c != NULL; c = c->next) {
dump_networks(c);
}
dump_regions();
return 0;
}

View file

@ -24,56 +24,19 @@
#include "tvhead.h"
#include "notify.h"
#include "ajaxui/ajaxui_mailbox.h"
#include "webui/webui.h"
void
notify_tdmi_state_change(th_dvb_mux_instance_t *tdmi)
notify_by_msg(const char *class, htsmsg_t *m)
{
ajax_mailbox_tdmi_state_change(tdmi);
}
void
notify_tdmi_name_change(th_dvb_mux_instance_t *tdmi)
{
ajax_mailbox_tdmi_name_change(tdmi);
}
void
notify_tdmi_status_change(th_dvb_mux_instance_t *tdmi)
{
ajax_mailbox_tdmi_status_change(tdmi);
}
void
notify_tdmi_qual_change(th_dvb_mux_instance_t *tdmi)
{
ajax_mailbox_tdmi_qual_change(tdmi);
}
void
notify_tdmi_services_change(th_dvb_mux_instance_t *tdmi)
{
ajax_mailbox_tdmi_services_change(tdmi);
}
void
notify_tda_change(th_dvb_adapter_t *tda)
{
ajax_mailbox_tda_change(tda);
}
void
notify_xmltv_grabber_status_change(struct xmltv_grabber *xg)
{
ajax_mailbox_xmltv_grabber_status_change(xg);
htsmsg_add_str(m, "notificationClass", class);
comet_mailbox_add_message(m);
htsmsg_destroy(m);
}
/**
*
*/
void
notify_transprot_status_change(struct th_transport *t)
{
@ -83,12 +46,5 @@ notify_transprot_status_change(struct th_transport *t)
if(s->ths_status_callback != NULL)
s->ths_status_callback(s, t->tht_last_status, s->ths_opaque);
ajax_mailbox_transport_status_change(t);
}
void
notify_cwc_status_change(struct cwc *cwc)
{
ajax_mailbox_cwc_status_change(cwc);
// ajax_mailbox_transport_status_change(t);
}

View file

@ -19,28 +19,10 @@
#ifndef NOTIFY_H_
#define NOTIFY_H_
struct xmltv_grabber;
struct th_dvb_mux_instance;
struct th_dvb_adapterr;
#include <libhts/htsmsg.h>
void notify_tdmi_state_change(struct th_dvb_mux_instance *tdmi);
void notify_by_msg(const char *class, htsmsg_t *m);
void notify_tdmi_name_change(struct th_dvb_mux_instance *tdmi);
void notify_tdmi_status_change(struct th_dvb_mux_instance *tdmi);
void notify_tdmi_qual_change(struct th_dvb_mux_instance *tdmi);
void notify_tdmi_services_change(struct th_dvb_mux_instance *tdmi);
void notify_tda_change(struct th_dvb_adapter *tda);
void notify_xmltv_grabber_status_change(struct xmltv_grabber *xg);
struct th_transport;
void notify_transprot_status_change(struct th_transport *t);
struct cwc;
void notify_cwc_status_change(struct cwc *cwc);
#endif /* NOTIFY_H_ */

29
psi.c
View file

@ -543,38 +543,39 @@ htstvstreamtype2txt(tv_streamtype_t s)
/**
* Save transport info
* Store transport settings into message
*/
void
psi_save_transport(FILE *fp, th_transport_t *t)
psi_get_transport_settings(htsmsg_t *m, th_transport_t *t)
{
th_stream_t *st;
htsmsg_t *sub;
fprintf(fp, "\tpcr = %d\n", t->tht_pcr_pid);
htsmsg_add_u32(m, "pcr", t->tht_pcr_pid);
if(t->tht_disabled)
fprintf(fp, "\tdisabled = 1\n");
htsmsg_add_u32(m, "disabled", !!t->tht_disabled);
LIST_FOREACH(st, &t->tht_streams, st_link) {
fprintf(fp, "\tstream {\n");
fprintf(fp, "\t\tpid = %d\n", st->st_pid);
fprintf(fp, "\t\ttype = %s\n", val2str(st->st_type, streamtypetab) ?: "?");
sub = htsmsg_create();
htsmsg_add_u32(sub, "pid", st->st_pid);
htsmsg_add_str(sub, "type", val2str(st->st_type, streamtypetab) ?: "?");
if(st->st_lang[0])
fprintf(fp, "\t\tlanguage = %s\n", st->st_lang);
htsmsg_add_str(sub, "language", st->st_lang);
if(st->st_type == HTSTV_CA)
fprintf(fp, "\t\tcaid = %s\n", psi_caid2name(st->st_caid));
htsmsg_add_str(sub, "caid", psi_caid2name(st->st_caid));
if(st->st_frame_duration)
fprintf(fp, "\t\tframeduration = %d\n", st->st_frame_duration);
htsmsg_add_u32(sub, "frameduration", st->st_frame_duration);
fprintf(fp, "\t}\n");
htsmsg_add_msg(m, "stream", sub);
}
}
#if 0
/**
* Load transport info
*/
@ -621,4 +622,4 @@ psi_load_transport(struct config_head *cl, th_transport_t *t)
}
}
#endif

5
psi.h
View file

@ -19,6 +19,8 @@
#ifndef PSI_H_
#define PSI_H_
#include <libhts/htsmsg.h>
#define PSI_SECTION_SIZE 4096
typedef struct psi_section {
@ -43,7 +45,6 @@ int psi_build_pmt(th_muxer_t *tm, uint8_t *buf0, int maxlen, int pcrpid);
const char *psi_caid2name(uint16_t caid);
void psi_save_transport(FILE *fp, th_transport_t *t);
void psi_load_transport(struct config_head *cl, th_transport_t *t);
void psi_get_transport_settings(htsmsg_t *m, th_transport_t *t);
#endif /* PSI_H_ */

18
pvr.c
View file

@ -513,12 +513,12 @@ pvr_generate_filename(pvr_rec_t *pvrr)
while(1) {
if(stat(fullname, &st) == -1) {
syslog(LOG_DEBUG, "pvr: File \"%s\" -- %s -- Using for recording",
tvhlog(LOG_DEBUG, "pvr", "File \"%s\" -- %s -- Using for recording",
fullname, strerror(errno));
break;
}
syslog(LOG_DEBUG, "pvr: Overwrite protection, file \"%s\" exists",
tvhlog(LOG_DEBUG, "pvr", "Overwrite protection, file \"%s\" exists",
fullname);
tally++;
@ -581,7 +581,7 @@ pvr_fsm(pvr_rec_t *pvrr)
delta = pvrr->pvrr_stop - now;
if(delta <= 0) {
syslog(LOG_NOTICE, "pvr: \"%s\" - Recording skipped, "
tvhlog(LOG_NOTICE, "pvr", "\"%s\" - Recording skipped, "
"program has already come to pass", pvrr->pvrr_printname);
pvrr->pvrr_status = HTSTV_PVR_STATUS_DONE;
pvr_inform_status_change(pvrr);
@ -677,8 +677,8 @@ pvrr_transport_available(pvr_rec_t *pvrr, th_transport_t *t)
fmt = guess_format(pvrr->pvrr_fmt_lavfname, NULL, NULL);
if(fmt == NULL) {
syslog(LOG_ERR,
"pvr: \"%s\" - Unable to open file format \"%s\" for output",
tvhlog(LOG_ERR, "pvr",
"\"%s\" - Unable to open file format \"%s\" for output",
pvrr->pvrr_printname, pvrr->pvrr_fmt_lavfname);
pvrr->pvrr_error = HTSTV_PVR_STATUS_FILE_ERROR;
pvr_fsm(pvrr);
@ -706,8 +706,8 @@ pvrr_transport_available(pvr_rec_t *pvrr, th_transport_t *t)
snprintf(urlname, sizeof(urlname), "file:%s", pvrr->pvrr_filename);
if((err = url_fopen(&fctx->pb, urlname, URL_WRONLY)) < 0) {
syslog(LOG_ERR,
"pvr: \"%s\" - Unable to create output file \"%s\" -- %s\n",
tvhlog(LOG_ERR, "pvr",
"\"%s\" - Unable to create output file \"%s\" -- %s\n",
pvrr->pvrr_printname, pvrr->pvrr_filename,
strerror(AVUNERROR(err)));
av_free(fctx);
@ -800,7 +800,7 @@ pvr_recorder_thread(void *aux)
if(t != NULL)
*t = 0;
syslog(LOG_INFO, "pvr: \"%s\" - Recording started, ends at %s",
tvhlog(LOG_INFO, "pvr", "\"%s\" - Recording started, ends at %s",
pvrr->pvrr_printname, txt2);
@ -845,7 +845,7 @@ pvr_recorder_thread(void *aux)
}
pthread_mutex_unlock(&pvrr->pvrr_pktq_mutex);
syslog(LOG_INFO, "pvr: \"%s\" - Recording completed",
tvhlog(LOG_INFO, "pvr", "\"%s\" - Recording completed",
pvrr->pvrr_printname);
return NULL;

9
rtsp.c
View file

@ -255,7 +255,8 @@ rtsp_reply_error(http_connection_t *hc, int error, const char *errstr)
if(errstr == NULL)
errstr = rtsp_err2str(error);
syslog(LOG_INFO, "rtsp: %s: %s", tcp_logname(&hc->hc_tcp_session), errstr);
tvhlog(LOG_INFO, "rtsp",
"%s: %s", tcp_logname(&hc->hc_tcp_session), errstr);
http_printf(hc, "RTSP/1.0 %d %s\r\n", error, errstr);
if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL)
@ -330,7 +331,8 @@ rtsp_cmd_play(http_connection_t *hc)
ts_muxer_play(rs->rs_muxer, start);
if(rs->rs_s->ths_channel != NULL)
syslog(LOG_INFO, "rtsp: %s: Starting playback of %s",
tvhlog(LOG_INFO, "rtsp",
"%s: Starting playback of %s",
tcp_logname(&hc->hc_tcp_session), rs->rs_s->ths_channel->ch_name);
http_printf(hc,
@ -367,7 +369,8 @@ rtsp_cmd_pause(http_connection_t *hc)
ts_muxer_pause(rs->rs_muxer);
if(rs->rs_s->ths_channel != NULL)
syslog(LOG_INFO, "rtsp: %s: Pausing playback of %s",
tvhlog(LOG_INFO, "rtsp",
"%s: Pausing playback of %s",
tcp_logname(&hc->hc_tcp_session), rs->rs_s->ths_channel->ch_name);
http_printf(hc,

View file

@ -90,7 +90,7 @@ sp_packet_input(void *opaque, th_muxstream_t *tms, th_pkt_t *pkt)
th_transport_t *t = sp->sp_t;
channel_t *ch;
syslog(LOG_INFO, "Probed \"%s\" -- Ok\n", t->tht_svcname);
tvhlog(LOG_INFO, "serviceprobe", "Probed \"%s\" -- Ok\n", t->tht_svcname);
if(t->tht_ch == NULL && t->tht_svcname != NULL) {
ch = channel_find(t->tht_svcname, 1, NULL);
@ -130,7 +130,8 @@ sp_status_callback(struct th_subscription *s, int status, void *opaque)
break;
}
syslog(LOG_INFO, "Probed \"%s\" -- %s\n", t->tht_svcname, errtxt);
tvhlog(LOG_INFO, "serviceprobe",
"Probed \"%s\" -- %s\n", t->tht_svcname, errtxt);
dtimer_arm(&sp->sp_timer, sp_done_callback, sp, 0);
}

View file

@ -24,6 +24,7 @@
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <syslog.h>
#include "tvhead.h"
#include "dispatch.h"
@ -99,7 +100,9 @@ reaper(void *opaque, int64_t now)
"unknown status\n");
}
syslog(LOG_INFO, "spawn: \"%s\" %s", s ? s->name : "<unknwon spawn>", txt);
tvhlog(LOG_INFO, "spawn",
"spawn: \"%s\" %s", s ? s->name : "<unknwon spawn>", txt);
if(s != NULL) {
LIST_REMOVE(s, link);
free((void *)s->name);

View file

@ -150,7 +150,8 @@ subscription_create(channel_t *ch, unsigned int weight, const char *name,
subscription_reschedule();
if(s->ths_transport == NULL)
syslog(LOG_NOTICE, "No transponder available for subscription \"%s\" "
tvhlog(LOG_NOTICE, "subscription",
"No transponder available for subscription \"%s\" "
"to channel \"%s\"",
s->ths_title, s->ths_channel->ch_name);

12
tcp.c
View file

@ -248,7 +248,7 @@ tcp_disconnect(tcp_session_t *ses, int err)
ses->tcp_callback(TCP_DISCONNECT, ses);
syslog(LOG_INFO, "%s: %s: disconnected -- %s",
tvhlog(LOG_INFO, "tcp", "%s: %s: disconnected -- %s",
ses->tcp_name, ses->tcp_peer_txt, strerror(err));
close(dispatch_delfd(ses->tcp_dispatch_handle));
@ -319,7 +319,7 @@ tcp_start_session(tcp_session_t *ses)
snprintf(ses->tcp_peer_txt, sizeof(ses->tcp_peer_txt), "%s:%d",
inet_ntoa(si->sin_addr), ntohs(si->sin_port));
syslog(LOG_INFO, "%s: %s%sConnected to %s", ses->tcp_name,
tvhlog(LOG_INFO, "tcp", "%s: %s%sConnected to %s", ses->tcp_name,
ses->tcp_hostname ?: "", ses->tcp_hostname ? ": " : "",
ses->tcp_peer_txt);
@ -351,7 +351,7 @@ tcp_client_connect_fail(tcp_session_t *c, int error)
struct sockaddr_in *si = (struct sockaddr_in *)&c->tcp_peer_addr;
syslog(LOG_ERR, "%s: Unable to connect to \"%s\" (%s) : %d -- %s",
tvhlog(LOG_ERR, "tcp", "%s: Unable to connect to \"%s\" (%s) : %d -- %s",
c->tcp_name, c->tcp_hostname, inet_ntoa(si->sin_addr),
ntohs(si->sin_port), strerror(error));
@ -410,7 +410,7 @@ tcp_session_peer_resolved(void *aux, struct sockaddr *so, const char *error)
c->tcp_resolver = NULL;
if(error != NULL) {
syslog(LOG_ERR, "%s: Unable to resolve \"%s\" -- %s",
tvhlog(LOG_ERR, "tcp", "%s: Unable to resolve \"%s\" -- %s",
c->tcp_name, c->tcp_hostname, error);
/* Try again in 30 seconds */
dtimer_arm(&c->tcp_timer, tcp_client_reconnect_timeout, c, 30);
@ -550,11 +550,11 @@ tcp_create_server(int port, size_t session_size, const char *name,
fcntl(s->tcp_fd, F_SETFL, fcntl(s->tcp_fd, F_GETFL) | O_NONBLOCK);
syslog(LOG_INFO, "%s: Listening for TCP connections on %s:%d",
tvhlog(LOG_INFO, "tcp", "%s: Listening for TCP connections on %s:%d",
name, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
if(bind(s->tcp_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
syslog(LOG_ERR,
tvhlog(LOG_ERR, "tcp",
"%s: Unable to bind socket for incomming TCP connections, "
"%s:%d -- %s",
name,

View file

@ -364,7 +364,7 @@ teletext_rundown(th_transport_t *t, channel_t *ch, tt_page_t *ttp)
if(prev != t->tht_tt_commercial_advice) {
syslog(LOG_DEBUG,
tvhlog(LOG_DEBUG, "teletext",
"teletext-rundown: \"%s\" on \"%s\": "
"%s commercial break (chunk %ds)",
ch->ch_name, t->tht_name,

View file

@ -642,7 +642,7 @@ transport_signal_status(th_transport_t *t, int newstatus)
snprintf(buf, sizeof(buf), "\"%s\" on %s",
t->tht_chname ?: t->tht_svcname, t->tht_sourcename(t));
syslog(LOG_INFO, "%s -- Changed status from \"%s\" to \"%s\"",
tvhlog(LOG_INFO, "transport", "%s -- Changed status from \"%s\" to \"%s\"",
buf, transport_status_to_text(t->tht_last_status),
transport_status_to_text(newstatus));

View file

@ -20,7 +20,6 @@
#define TV_HEAD_H
#include <pthread.h>
#include <syslog.h>
#include <netinet/in.h>
#include <libhts/htsq.h>
#include <libhts/htstv.h>
@ -957,4 +956,15 @@ static inline unsigned int tvh_strhash(const char *s, unsigned int mod)
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
void tvhlog(int severity, const char *subsys, const char *fmt, ...);
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
#endif /* TV_HEAD_H */

7
v4l.c
View file

@ -157,7 +157,8 @@ v4l_setfreq(th_v4l_adapter_t *tva, int frequency)
vf.frequency = (frequency * 16) / 1000000;
result = ioctl(tva->tva_fd, VIDIOC_S_FREQUENCY, &vf);
if(result < 0) {
syslog(LOG_ERR, "%s: Unable to tune to %dHz\n", tva->tva_path, frequency);
tvhlog(LOG_ERR, "v4l",
"%s: Unable to tune to %dHz\n", tva->tva_path, frequency);
return 1;
}
@ -165,11 +166,11 @@ v4l_setfreq(th_v4l_adapter_t *tva, int frequency)
result = ioctl(tva->tva_fd, VIDIOC_G_TUNER, &vt);
if(result < 0) {
syslog(LOG_ERR, "%s: Unable read tuner status\n", tva->tva_path);
tvhlog(LOG_ERR, "v4l", "%s: Unable read tuner status\n", tva->tva_path);
return 1;
}
syslog(LOG_DEBUG, "%s: Tuned to %.3f MHz%s",
tvhlog(LOG_DEBUG, "v4l", "%s: Tuned to %.3f MHz%s",
tva->tva_path, (float)frequency/1000000.0,
vt.signal ? " (Signal Detected)" : "");

190
webui/acleditor.js Normal file
View file

@ -0,0 +1,190 @@
tvheadend.acleditor = function() {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header: "Enabled",
dataIndex: 'enabled',
width: 60
});
var streamingColumn = new Ext.grid.CheckColumn({
header: "Streaming",
dataIndex: 'streaming',
width: 100
});
var pvrColumn = new Ext.grid.CheckColumn({
header: "Video Recorder",
dataIndex: 'pvr',
width: 100
});
var webuiColumn = new Ext.grid.CheckColumn({
header: "Web Interface",
dataIndex: 'webui',
width: 100
});
var adminColumn = new Ext.grid.CheckColumn({
header: "Admin",
dataIndex: 'admin',
width: 100
});
var cm = new Ext.grid.ColumnModel([
enabledColumn,
{
id:'username',
header: "Username",
dataIndex: 'username',
editor: new fm.TextField({
allowBlank: false
})
},{
id:'password',
header: "Password",
dataIndex: 'password',
editor: new fm.TextField({
allowBlank: false
})
},{
header: "Prefix",
dataIndex: 'prefix',
editor: new fm.TextField({
allowBlank: false,
})
},
streamingColumn,
pvrColumn,
webuiColumn,
adminColumn,
{
id:'comment',
header: "Comment",
dataIndex: 'comment',
width: 400,
editor: new fm.TextField({
})
}
]);
cm.defaultSortable = true;
var UserRecord = Ext.data.Record.create([
{name: 'enabled'},
{name: 'streaming'},
{name: 'pvr'},
{name: 'admin'},
{name: 'webui'},
{name: 'username'},
{name: 'prefix'},
{name: 'password'},
{name: 'comment'}]);
var store = new Ext.data.JsonStore({root: 'entries',
fields: UserRecord,
url: "tablemgr",
autoLoad: true,
id: 'id',
storeid: 'id',
baseParams: {table: "accesscontrol", op: "get"}
});
function addRecord() {
Ext.Ajax.request({
url: "tablemgr", params: {op:"create", table:"accesscontrol"},
failure:function(response,options){
Ext.MessageBox.alert('Server Error','Unable to generate new record');
},
success:function(response,options){
var responseData = Ext.util.JSON.decode(response.responseText);
var p = new UserRecord(responseData, responseData.id);
grid.stopEditing();
store.insert(0, p);
grid.startEditing(0, 0);
}
})
};
function delSelected() {
var selectedKeys = grid.selModel.selections.keys;
if(selectedKeys.length > 0)
{
Ext.MessageBox.confirm('Message',
'Do you really want to delete selection?',
deleteRecord);
} else {
Ext.MessageBox.alert('Message',
'Please select at least one item to delete');
}
};
function deleteRecord(btn) {
if(btn=='yes') {
var selectedKeys = grid.selModel.selections.keys;
Ext.Ajax.request({
url: "tablemgr", params: {op:"delete", table:"accesscontrol",
entries:Ext.encode(selectedKeys)},
failure:function(response,options){
Ext.MessageBox.alert('Server Error','Unable to delete');
},
success:function(response,options){
store.reload();
}
})
}
}
function saveChanges() {
var mr = store.getModifiedRecords();
var out = new Array();
for (var x = 0; x < mr.length; x++) {
v = mr[x].getChanges();
out[x] = v;
out[x].id = mr[x].id;
}
Ext.Ajax.request({url: "tablemgr",
params: {op:"update", table:"accesscontrol",
entries:Ext.encode(out)},
success:function(response,options){
store.commitChanges();
},
failure:function(response,options){
Ext.MessageBox.alert('Message',response.statusText);
}
});
}
var grid = new Ext.grid.EditorGridPanel({
title: 'Access Configuration',
plugins:[enabledColumn,streamingColumn,pvrColumn,adminColumn, webuiColumn],
store: store,
clicksToEdit: 2,
cm: cm,
selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
tbar: [{
text: 'Add entry',
handler: addRecord
}, '-', {
text: 'Delete selected',
handler: delSelected
}, '-', {
text: "Save changes",
handler: saveChanges
}
]
});
return grid;
}

301
webui/comet.c Normal file
View file

@ -0,0 +1,301 @@
/*
* tvheadend, COMET
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libavutil/md5.h>
#include <libhts/htsmsg.h>
#include <libhts/htsmsg_json.h>
#include "tvhead.h"
#include "dispatch.h"
#include "http.h"
#include "webui/webui.h"
#include "access.h"
#define MAILBOX_UNUSED_TIMEOUT 20
#define MAILBOX_EMPTY_REPLY_TIMEOUT 10
//#define mbdebug(fmt...) printf(fmt);
#define mbdebug(fmt...)
static LIST_HEAD(, comet_mailbox) mailboxes;
int mailbox_tally;
typedef struct comet_mailbox {
char *cmb_boxid; /* an md5hash */
dtimer_t cmb_timer;
http_reply_t *cmb_hr; /* Pending request */
htsmsg_t *cmb_messages; /* A vector */
LIST_ENTRY(comet_mailbox) cmb_link;
} comet_mailbox_t;
/**
*
*/
static void
cmb_destroy(comet_mailbox_t *cmb)
{
mbdebug("mailbox[%s]: destroyed\n", cmb->cmb_boxid);
if(cmb->cmb_messages != NULL)
htsmsg_destroy(cmb->cmb_messages);
LIST_REMOVE(cmb, cmb_link);
dtimer_disarm(&cmb->cmb_timer);
free(cmb->cmb_boxid);
free(cmb);
}
/**
*
*/
static void
comet_mailbox_unused(void *opaque, int64_t now)
{
comet_mailbox_t *cmb = opaque;
assert(cmb->cmb_hr == NULL);
cmb_destroy(cmb);
}
/**
*
*/
static void
comet_mailbox_connection_lost(http_reply_t *hr, void *opaque)
{
comet_mailbox_t *cmb = opaque;
assert(hr == cmb->cmb_hr);
cmb_destroy(cmb);
}
/**
*
*/
static comet_mailbox_t *
comet_mailbox_create(void)
{
comet_mailbox_t *cmb = calloc(1, sizeof(comet_mailbox_t));
struct timeval tv;
uint8_t sum[16];
char id[33];
int i;
struct AVMD5 *ctx;
ctx = alloca(av_md5_size);
gettimeofday(&tv, NULL);
av_md5_init(ctx);
av_md5_update(ctx, (void *)&tv, sizeof(tv));
av_md5_update(ctx, (void *)&mailbox_tally, sizeof(uint32_t));
av_md5_final(ctx, sum);
for(i = 0; i < 16; i++) {
id[i * 2 + 0] = "0123456789abcdef"[sum[i] >> 4];
id[i * 2 + 1] = "0123456789abcdef"[sum[i] & 15];
}
id[32] = 0;
cmb->cmb_boxid = strdup(id);
mailbox_tally++;
LIST_INSERT_HEAD(&mailboxes, cmb, cmb_link);
dtimer_arm(&cmb->cmb_timer, comet_mailbox_unused, cmb,
MAILBOX_UNUSED_TIMEOUT);
return cmb;
}
/**
*
*/
static void
comet_mailbox_reply(comet_mailbox_t *cmb, http_reply_t *hr)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "boxid", cmb->cmb_boxid);
htsmsg_add_msg(m, "messages", cmb->cmb_messages ?: htsmsg_create_array());
cmb->cmb_messages = NULL;
htsmsg_json_serialize(m, &hr->hr_q, 0);
htsmsg_destroy(m);
http_output(hr->hr_connection, hr, "text/x-json; charset=UTF-8", NULL, 0);
cmb->cmb_hr = NULL;
/* Arm a timer in case the browser won't call back */
dtimer_arm(&cmb->cmb_timer, comet_mailbox_unused, cmb,
MAILBOX_UNUSED_TIMEOUT);
}
/**
*
*/
static void
comet_mailbox_empty_reply(void *opaque, int64_t now)
{
comet_mailbox_t *cmb = opaque;
http_reply_t *hr = cmb->cmb_hr;
comet_mailbox_reply(cmb, hr);
http_continue(hr->hr_connection);
}
/**
* Poll callback
*
* Prepare the mailbox for reply
*/
static int
comet_mailbox_poll(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
comet_mailbox_t *cmb = NULL;
const char *cometid = http_arg_get(&hc->hc_req_args, "boxid");
if(cometid != NULL)
LIST_FOREACH(cmb, &mailboxes, cmb_link)
if(!strcmp(cmb->cmb_boxid, cometid))
break;
if(cmb == NULL)
cmb = comet_mailbox_create();
if(cmb->cmb_hr != NULL) {
mbdebug("mailbox already processing\n");
return 409;
}
if(cmb->cmb_messages != NULL) {
/* Pending letters, direct reply */
mbdebug("direct reply\n");
comet_mailbox_reply(cmb, hr);
return 0;
}
mbdebug("nothing in queue, waiting\n");
cmb->cmb_hr = hr;
hr->hr_opaque = cmb;
hr->hr_destroy = comet_mailbox_connection_lost;
dtimer_arm(&cmb->cmb_timer, comet_mailbox_empty_reply, cmb,
MAILBOX_EMPTY_REPLY_TIMEOUT);
return 0;
}
/**
*
*/
#if 0
static dtimer_t comet_clock;
static void
comet_ticktack(void *opaque, int64_t now)
{
char buf[64];
htsmsg_t *x;
snprintf(buf, sizeof(buf), "The clock is now %lluµs past 1970",
now);
x = htsmsg_create();
htsmsg_add_str(x, "type", "logmessage");
htsmsg_add_str(x, "logtxt", buf);
comet_mailbox_add_message(x);
htsmsg_destroy(x);
dtimer_arm(&comet_clock, comet_ticktack, NULL, 1);
}
#endif
/**
*
*/
void
comet_init(void)
{
http_path_add("/comet", NULL, comet_mailbox_poll, ACCESS_WEB_INTERFACE);
// dtimer_arm(&comet_clock, comet_ticktack, NULL, 1);
}
/**
* Delayed delivery of mailbox replies
*/
static void
comet_mailbox_deliver(void *opaque, int64_t now)
{
comet_mailbox_t *cmb = opaque;
http_connection_t *hc;
hc = cmb->cmb_hr->hr_connection;
comet_mailbox_reply(cmb, cmb->cmb_hr);
http_continue(hc);
}
/**
*
*/
void
comet_mailbox_add_message(htsmsg_t *m)
{
comet_mailbox_t *cmb;
LIST_FOREACH(cmb, &mailboxes, cmb_link) {
if(cmb->cmb_messages == NULL)
cmb->cmb_messages = htsmsg_create_array();
htsmsg_add_msg(cmb->cmb_messages, NULL, htsmsg_copy(m));
if(cmb->cmb_hr != NULL)
dtimer_arm_hires(&cmb->cmb_timer, comet_mailbox_deliver, cmb,
getclock_hires() + 100000);
}
}

176
webui/cwceditor.js Normal file
View file

@ -0,0 +1,176 @@
tvheadend.cwceditor = function() {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header: "Enabled",
dataIndex: 'enabled',
width: 60
});
var cm = new Ext.grid.ColumnModel([
enabledColumn,
{
id:'hostname',
header: "Hostname",
dataIndex: 'hostname',
width: 200,
editor: new fm.TextField({
allowBlank: false
})
},{
id:'port',
header: "Port",
dataIndex: 'port',
editor: new fm.TextField({
allowBlank: false
})
},{
id:'username',
header: "Username",
dataIndex: 'username',
editor: new fm.TextField({
allowBlank: false
})
},{
id:'password',
header: "Password",
dataIndex: 'password',
editor: new fm.TextField({
allowBlank: false
})
},{
header: "DES Key",
dataIndex: 'deskey',
width: 300,
editor: new fm.TextField({
allowBlank: false,
})
},
{
id:'comment',
header: "Comment",
dataIndex: 'comment',
width: 400,
editor: new fm.TextField({
})
}
]);
cm.defaultSortable = true;
var UserRecord = Ext.data.Record.create([
{name: 'enabled'},
{name: 'hostname'},
{name: 'port'},
{name: 'username'},
{name: 'password'},
{name: 'deskey'},
{name: 'comment'}]);
var store = new Ext.data.JsonStore({root: 'entries',
fields: UserRecord,
url: "tablemgr",
autoLoad: true,
id: 'id',
storeid: 'id',
baseParams: {table: "cwc", op: "get"}
});
function addRecord() {
Ext.Ajax.request({
url: "tablemgr", params: {op:"create", table:"cwc"},
failure:function(response,options){
Ext.MessageBox.alert('Server Error','Unable to generate new record');
},
success:function(response,options){
var responseData = Ext.util.JSON.decode(response.responseText);
var p = new UserRecord(responseData, responseData.id);
grid.stopEditing();
store.insert(0, p);
grid.startEditing(0, 0);
}
})
};
function delSelected() {
var selectedKeys = grid.selModel.selections.keys;
if(selectedKeys.length > 0)
{
Ext.MessageBox.confirm('Message',
'Do you really want to delete selection?',
deleteRecord);
} else {
Ext.MessageBox.alert('Message',
'Please select at least one item to delete');
}
};
function deleteRecord(btn) {
if(btn=='yes') {
var selectedKeys = grid.selModel.selections.keys;
Ext.Ajax.request({
url: "tablemgr", params: {op:"delete", table:"cwc",
entries:Ext.encode(selectedKeys)},
failure:function(response,options){
Ext.MessageBox.alert('Server Error','Unable to delete');
},
success:function(response,options){
store.reload();
}
})
}
}
function saveChanges() {
var mr = store.getModifiedRecords();
var out = new Array();
for (var x = 0; x < mr.length; x++) {
v = mr[x].getChanges();
out[x] = v;
out[x].id = mr[x].id;
}
Ext.Ajax.request({url: "tablemgr",
params: {op:"update", table:"cwc",
entries:Ext.encode(out)},
success:function(response,options){
store.commitChanges();
},
failure:function(response,options){
Ext.MessageBox.alert('Message',response.statusText);
}
});
}
var grid = new Ext.grid.EditorGridPanel({
title: 'Code Word Client',
plugins:[enabledColumn],
store: store,
clicksToEdit: 2,
cm: cm,
selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
tbar: [{
text: 'Add entry',
handler: addRecord
}, '-', {
text: 'Delete selected',
handler: delSelected
}, '-', {
text: "Save changes",
handler: saveChanges
}
]
});
return grid;
}

270
webui/dvb.js Normal file
View file

@ -0,0 +1,270 @@
/**
* DVB adapter details
*/
tvheadend.dvb_adapterdetails = function(adapterId, adapterName, treenode) {
var confreader = new Ext.data.JsonReader({
root: 'dvbadapters',
}, ['name', 'automux']);
var confpanel = new Ext.FormPanel({
frame:true,
disabled:true,
region:'south',
title:'Configuraton',
split:true,
height: 150,
collapsible: true,
margins:'0 0 0 0',
labelAlign: 'right',
labelWidth: 150,
waitMsgTarget: true,
reader: confreader,
defaultType: 'textfield',
items: [{
fieldLabel: 'Adapter name',
name: 'name',
width: 400,
},
new Ext.form.Checkbox({
fieldLabel: 'Autodetect muxes',
name: 'automux',
})
],
});
confpanel.getForm().load({url:'/dvbadapter',
params:{'adapterId': adapterId, 'op':'load'},
success:function(form, action) {
confpanel.enable();
}});
var submit = confpanel.addButton({
text: 'Save changes',
handler: function() {
confpanel.getForm().submit({url:'/dvbadapter',
params:{'adapterId': adapterId, 'op':'save'},
waitMsg:'Saving Data...',
success:function(form, action) {
}
})
}
});
var status = {
region:'center',
html: 'Status...',
margins:'0 0 0 0'
};
function addmux() {
var locationbutton = new Ext.Button({
text: 'Add',
disabled: true,
handler: function() {
var n = locationlist.getSelectionModel().getSelectedNode();
Ext.Ajax.request({url: '/dvbadapter',
params: {network: n.attributes.id,
adapterId: adapterId, op: 'addnetwork'}});
win.close();
}
});
var locationlist = new Ext.tree.TreePanel({
title:'By location',
autoScroll:true,
rootVisible:false,
loader: new Ext.tree.TreeLoader({
baseParams: {adapter: adapterId},
dataUrl:'/dvbnetworks',
}),
root: new Ext.tree.AsyncTreeNode({
id:'root',
}),
buttons: [locationbutton],
buttonAlign: 'center'
});
locationlist.on('click', function(n) {
if(n.attributes.leaf) {
locationbutton.enable();
} else {
locationbutton.disable();
}
});
/*
var locationpanel = new Ext.FormPanel({
frame:true,
title:'By location',
margins:'0 0 0 0',
reader: confreader,
defaultType: 'textfield',
items: [locationlist]
});
var submit = locationpanel.addButton({
text: 'Submit',
handler: function() {
alert('hej');
}
});
*/
win = new Ext.Window({
title: 'Add mux(es) on ' + adapterName,
layout: 'fit',
width: 500,
height: 500,
modal: true,
plain: true,
items: new Ext.TabPanel({
autoTabs: true,
activeTab: 0,
deferredRender: false,
border: false,
items: [locationlist, {
html: 'def',
title: 'Manual configuration'
}]
})
});
win.show();
}
var panel = new Ext.Panel({
title: adapterName,
layout:'border',
border: false,
items:[status, confpanel],
tbar:[{
text: 'Add mux(es)',
handler: addmux
}]
});
return panel;
}
/**
*
*/
tvheadend.dvb = function() {
function refresh() {
var sm = tree.getSelectionModel();
console.log(sm.getSelectedNode());
sm.getSelectedNode().reload();
}
function deleteNode() {
var sm = tree.getSelectionModel();
console.log(sm.getSelectedNode());
}
var tree = new Ext.tree.ColumnTree({
region:'west',
autoScroll:true,
rootVisible:false,
columns:[{
header:'Name',
width:300,
dataIndex:'name'
},{
header:'Type',
width:100,
dataIndex:'type'
},{
header:'Status',
width:100,
dataIndex:'status'
},{
header:'Quality',
width:100,
dataIndex:'quality'
}],
loader: new Ext.tree.TreeLoader({
clearOnLoad: true,
dataUrl:'/dvbtree',
uiProviders:{
'col': Ext.tree.ColumnNodeUI
}
}),
root: new Ext.tree.AsyncTreeNode({
id:'root',
}),
tbar: [{
text: 'Delete selected',
handler: deleteNode
}, '-', {
text: 'Refresh all',
handler: refresh
}],
});
/**
*
*/
var details = new Ext.Panel({
region:'center', layout:'fit',
items:[{border: false}]
});
/**
*
*/
var panel = new Ext.Panel({
border: false,
title:'DVB Adapters',
layout:'border',
items: [tree, details]
});
/**
*
*/
tree.on('click', function(n) {
details.remove(details.getComponent(0));
details.doLayout();
switch(n.attributes.itype) {
case 'adapter':
var newPanel =
new tvheadend.dvb_adapterdetails(n.attributes.id,
n.attributes.name, n);
break;
case 'mux':
case 'transport':
default:
var newPanel = {title: n.attributes.name, html: ''};
break;
}
details.add(newPanel);
details.doLayout();
});
tvheadend.dvbtree = tree;
return panel;
}

65
webui/ext.css Normal file
View file

@ -0,0 +1,65 @@
/*
* Ext JS Library 2.1
* Copyright(c) 2006-2008, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
.x-column-tree .x-tree-node {
zoom:1;
}
.x-column-tree .x-tree-node-el {
/*border-bottom:1px solid #eee; borders? */
zoom:1;
}
.x-column-tree .x-tree-selected {
background: #d9e8fb;
}
.x-column-tree .x-tree-node a {
line-height:18px;
vertical-align:middle;
}
.x-column-tree .x-tree-node a span{
}
.x-column-tree .x-tree-node .x-tree-selected a span{
background:transparent;
color:#000;
}
.x-tree-col {
float:left;
overflow:hidden;
padding:0 1px;
zoom:1;
}
.x-tree-col-text, .x-tree-hd-text {
overflow:hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
padding:3px 3px 3px 5px;
white-space: nowrap;
font:normal 11px arial, tahoma, helvetica, sans-serif;
}
.x-tree-headers {
background: #f9f9f9 url(http://192.168.1.50/lab/extjs/resources/images/default/grid/grid3-hrow.gif) repeat-x 0 bottom;
cursor:default;
zoom:1;
}
.x-tree-hd {
float:left;
overflow:hidden;
border-left:1px solid #eee;
border-right:1px solid #d0d0d0;
}
/*
.task {
background-image:url(../shared/icons/fam/cog.png) !important;
}
.task-folder {
background-image:url(../shared/icons/fam/folder_go.png) !important;
}*/

145
webui/extensions.js Normal file
View file

@ -0,0 +1,145 @@
/*
* Ext JS Library 2.1
* Copyright(c) 2006-2008, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
/**
* CheckedColumn
*/
Ext.grid.CheckColumn = function(config){
Ext.apply(this, config);
if(!this.id){
this.id = Ext.id();
}
this.renderer = this.renderer.createDelegate(this);
};
Ext.grid.CheckColumn.prototype ={
init : function(grid){
this.grid = grid;
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},
onMouseDown : function(e, t){
if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
e.stopEvent();
var index = this.grid.getView().findRowIndex(t);
var record = this.grid.store.getAt(index);
record.set(this.dataIndex, !record.data[this.dataIndex]);
}
},
renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
}
};
/**
* ColumnTree
*/
Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
lines:false,
borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
cls:'x-column-tree',
onRender : function(){
Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
this.headers = this.body.createChild(
{cls:'x-tree-headers'},this.innerCt.dom);
var cols = this.columns, c;
var totalWidth = 0;
for(var i = 0, len = cols.length; i < len; i++){
c = cols[i];
totalWidth += c.width;
this.headers.createChild({
cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
cn: {
cls:'x-tree-hd-text',
html: c.header
},
style:'width:'+(c.width-this.borderWidth)+'px;'
});
}
this.headers.createChild({cls:'x-clear'});
// prevent floats from wrapping when clipped
this.headers.setWidth(totalWidth);
this.innerCt.setWidth(totalWidth);
}
});
Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
focus: Ext.emptyFn, // prevent odd scrolling behavior
setColText : function(colidx, text) {
this.colNode[colidx].innerHTML = text;
},
renderElements : function(n, a, targetNode, bulkRender){
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
this.colNode = [];
var t = n.getOwnerTree();
var cols = t.columns;
var bw = t.borderWidth;
var c = cols[0];
var buf = [
'<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',
'<div class="x-tree-col" style="width:',c.width-bw,'px;">',
'<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">',
'<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',
a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
'<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
"</div>"];
for(var i = 1, len = cols.length; i < len; i++){
c = cols[i];
buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
'<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
"</div>");
}
buf.push(
'<div class="x-clear"></div></div>',
'<ul class="x-tree-node-ct" style="display:none;"></ul>',
"</li>");
if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
n.nextSibling.ui.getEl(), buf.join(""));
}else{
this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
}
this.elNode = this.wrap.childNodes[0];
this.ctNode = this.wrap.childNodes[1];
var cs = this.elNode.firstChild.childNodes;
this.indentNode = cs[0];
this.ecNode = cs[1];
this.iconNode = cs[2];
this.anchor = cs[3];
this.textNode = cs[3].firstChild;
for(var i = 1, len = cols.length; i < len; i++) {
this.colNode[i] = this.elNode.childNodes[i].firstChild;
}
}
});

416
webui/extjs.c Normal file
View file

@ -0,0 +1,416 @@
/*
* tvheadend, EXTJS based interface
* Copyright (C) 2008 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <libhts/htsmsg.h>
#include <libhts/htsmsg_json.h>
#include "tvhead.h"
#include "http.h"
#include "webui.h"
#include "access.h"
#include "dtable.h"
#include "dvb.h"
#include "dvb_support.h"
#include "dvb_muxconfig.h"
#include "transports.h"
extern const char *htsversion;
#include "obj/tvheadend.jsh"
#include "obj/extensions.jsh"
#include "obj/acleditor.jsh"
#include "obj/cwceditor.jsh"
#include "obj/dvb.jsh"
#include "obj/ext.cssh"
static void
extjs_load(htsbuf_queue_t *hq, const char *script)
{
htsbuf_qprintf(hq,
"<script type=\"text/javascript\" "
"src=\"%s\">"
"</script>\n", script);
}
/**
*
*/
static void
extjs_exec(htsbuf_queue_t *hq, const char *fmt, ...)
{
va_list ap;
htsbuf_qprintf(hq, "<script type=\"text/javascript\">\r\n");
va_start(ap, fmt);
htsbuf_vqprintf(hq, fmt, ap);
va_end(ap);
htsbuf_qprintf(hq, "\r\n</script>\r\n");
}
/**
* PVR info, deliver info about the given PVR entry
*/
static int
extjs_root(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
#define EXTJSPATH "http://www.lonelycoder.com/ext-2.2"
htsbuf_qprintf(hq, "<html><body>\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\""EXTJSPATH"/resources/css/ext-all.css\">\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"app/extensions.css\">\n"
"<script type=\"text/javascript\" src=\""EXTJSPATH"/adapter/ext/ext-base.js\"></script>\n"
"<script type=\"text/javascript\" src=\""EXTJSPATH"/ext-all-debug.js\"></script>\n");
extjs_exec(hq, "Ext.BLANK_IMAGE_URL = "
"'"EXTJSPATH"/resources/images/default/s.gif';");
/**
* Load extjs extensions
*/
extjs_load(hq, "app/extensions.js");
extjs_load(hq, "app/tabclosemenu.js");
/**
* Create a namespace for our app
*/
extjs_exec(hq, "Ext.namespace('tvheadend');");
/**
* Load all components
*/
extjs_load(hq, "app/acleditor.js");
extjs_load(hq, "app/cwceditor.js");
extjs_load(hq, "app/dvb.js");
/**
* Finally, the app itself
*/
extjs_load(hq, "app/tvheadend.js");
extjs_exec(hq, "Ext.onReady(tvheadend.app.init, tvheadend.app);");
htsbuf_qprintf(hq,
"<style type=\"text/css\">\n"
"html, body {\n"
"\tfont:normal 12px verdana;\n"
"\tmargin:0;\n"
"\tpadding:0;\n"
"\tborder:0 none;\n"
"\toverflow:hidden;\n"
"\theight:100%;\n"
"}\n"
"#systemlog {\n"
"\tfont:normal 12px courier; font-weight: bold;\n"
"}\n"
"p {\n"
"\tmargin:5px;\n"
"}\n"
"</style>\n"
"<title>HTS Tvheadend %s</title>\n"
"</head>\n"
"<body>\n"
"<div id=\"systemlog\"></div>\n"
"</body></html>\n",
htsversion);
http_output_html(hc, hr);
return 0;
}
/**
*
*/
static int
extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
dtable_t *dt;
htsmsg_t *out = NULL, *in, *array;
const char *tablename = http_arg_get(&hc->hc_req_args, "table");
const char *op = http_arg_get(&hc->hc_req_args, "op");
const char *entries = http_arg_get(&hc->hc_req_args, "entries");
if(tablename == NULL || (dt = dtable_find(tablename)) == NULL)
return 404;
in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
if(!strcmp(op, "create")) {
out = dtable_record_create(dt);
} else if(!strcmp(op, "get")) {
array = dtable_record_get_all(dt);
out = htsmsg_create();
htsmsg_add_msg(out, "entries", array);
} else if(!strcmp(op, "update")) {
if(in == NULL)
return HTTP_STATUS_BAD_REQUEST;
dtable_record_update_by_array(dt, in);
} else if(!strcmp(op, "delete")) {
if(in == NULL)
return HTTP_STATUS_BAD_REQUEST;
dtable_record_delete_by_array(dt, in);
} else {
return HTTP_STATUS_BAD_REQUEST;
}
if(in != NULL)
htsmsg_destroy(in);
if(out != NULL) {
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
}
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
return 0;
}
/**
*
*/
static void
extjs_dvbtree_node(htsmsg_t *array, int leaf, const char *id, const char *name,
const char *type, const char *status, int quality,
const char *itype)
{
htsmsg_t *e = htsmsg_create();
htsmsg_add_str(e, "uiProvider", "col");
htsmsg_add_str(e, "id", id);
htsmsg_add_u32(e, "leaf", leaf);
htsmsg_add_str(e, "itype", itype);
htsmsg_add_str(e, "name", name);
htsmsg_add_str(e, "type", type);
htsmsg_add_str(e, "status", status);
htsmsg_add_u32(e, "quality", quality);
htsmsg_add_msg(array, NULL, e);
}
/**
*
*/
static int
extjs_dvbtree(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
const char *s = http_arg_get(&hc->hc_req_args, "node");
htsmsg_t *out = NULL;
char buf[200];
th_dvb_adapter_t *tda;
th_dvb_mux_instance_t *tdmi;
th_transport_t *t;
if(s == NULL)
return HTTP_STATUS_BAD_REQUEST;
out = htsmsg_create_array();
if(!strcmp(s, "root")) {
/**
* List of all adapters
*/
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
snprintf(buf, sizeof(buf), "%s adapter",
dvb_adaptertype_to_str(tda->tda_type));
extjs_dvbtree_node(out, 0,
tda->tda_identifier, tda->tda_displayname,
buf, tda->tda_rootpath != NULL ? "OK" : "No H/W",
100, "adapter");
}
} else if((tda = dvb_adapter_find_by_identifier(s)) != NULL) {
RB_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
extjs_dvbtree_node(out, 0,
tdmi->tdmi_identifier, buf, "DVB Mux",
dvb_mux_status(tdmi, 0),
100 + tdmi->tdmi_quality * 2, "mux");
}
} else if((tdmi = dvb_mux_find_by_identifier(s)) != NULL) {
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
if(transport_servicetype_txt(t) == NULL)
continue;
extjs_dvbtree_node(out, 1,
t->tht_identifier, t->tht_svcname,
transport_servicetype_txt(t),
"OK", 100, "transport");
}
}
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
return 0;
}
/**
*
*/
static int
extjs_dvbnetworks(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
const char *s = http_arg_get(&hc->hc_req_args, "node");
const char *a = http_arg_get(&hc->hc_req_args, "adapter");
th_dvb_adapter_t *tda;
htsmsg_t *out = NULL;
if(s == NULL || a == NULL)
return HTTP_STATUS_BAD_REQUEST;
if((tda = dvb_adapter_find_by_identifier(a)) == NULL)
return HTTP_STATUS_BAD_REQUEST;
out = dvb_mux_preconf_get_node(tda->tda_type, s);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
return 0;
}
/**
*
*/
static htsmsg_t *
json_single_record(htsmsg_t *rec, const char *root)
{
htsmsg_t *out, *array;
out = htsmsg_create();
array = htsmsg_create_array();
htsmsg_add_msg(array, NULL, rec);
htsmsg_add_msg(out, "dvbadapters", array);
return out;
}
/**
*
*/
static int
extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hr->hr_q;
const char *s = http_arg_get(&hc->hc_req_args, "adapterId");
const char *op = http_arg_get(&hc->hc_req_args, "op");
th_dvb_adapter_t *tda = s ? dvb_adapter_find_by_identifier(s) : NULL;
htsmsg_t *r, *out;
if(tda == NULL)
return HTTP_STATUS_BAD_REQUEST;
if(!strcmp(op, "load")) {
r = htsmsg_create();
htsmsg_add_str(r, "id", tda->tda_identifier);
htsmsg_add_str(r, "device", tda->tda_rootpath ?: "No hardware attached");
htsmsg_add_str(r, "name", tda->tda_displayname);
htsmsg_add_u32(r, "automux", 1);
out = json_single_record(r, "dvbadapters");
} else if(!strcmp(op, "save")) {
if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL)
dvb_tda_set_displayname(tda, s);
out = htsmsg_create();
htsmsg_add_u32(out, "success", 1);
} else if(!strcmp(op, "addnetwork")) {
if((s = http_arg_get(&hc->hc_req_args, "network")) != NULL)
dvb_mux_preconf_add_network(tda, s);
out = htsmsg_create();
htsmsg_add_u32(out, "success", 1);
} else {
return HTTP_STATUS_BAD_REQUEST;
}
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
return 0;
}
/**
* WEB user interface
*/
void
extjs_start(void)
{
http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE);
http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE);
http_path_add("/dvbtree", NULL, extjs_dvbtree, ACCESS_WEB_INTERFACE);
http_path_add("/dvbadapter", NULL, extjs_dvbadapter, ACCESS_WEB_INTERFACE);
http_path_add("/dvbnetworks", NULL, extjs_dvbnetworks, ACCESS_WEB_INTERFACE);
#define ADD_JS_RESOURCE(path, name) \
http_resource_add(path, name, sizeof(name), "text/javascript", "gzip")
ADD_JS_RESOURCE("/app/extensions.js", embedded_extensions);
ADD_JS_RESOURCE("/app/extensions.css", embedded_ext);
ADD_JS_RESOURCE("/app/tvheadend.js", embedded_tvheadend);
ADD_JS_RESOURCE("/app/acleditor.js", embedded_acleditor);
ADD_JS_RESOURCE("/app/cwceditor.js", embedded_cwceditor);
ADD_JS_RESOURCE("/app/dvb.js", embedded_dvb);
}

135
webui/tvheadend.js Normal file
View file

@ -0,0 +1,135 @@
/**
* Comet interfaces
*/
tvheadend.comet_poller = function() {
function parse_comet_response(responsetxt) {
var response = Ext.util.JSON.decode(responsetxt);
for (var x = 0; x < response.messages.length; x++) {
var m = response.messages[x];
switch(m.notificationClass) {
case 'logmessage':
var sl = Ext.get('systemlog');
var e = Ext.DomHelper.append(sl,
'<div>' + m.logtxt + '</div>');
e.scrollIntoView(sl);
break;
case 'dvbadapter':
case 'dvbmux':
case 'dvbtransport':
var n = tvheadend.dvbtree.getNodeById(m.id);
if(n != null) {
if(m.reload != null && n.isLoaded()) {
n.reload();
}
if(m.name != null) {
n.setText(m.name);
n.attributes.name = m.name;
}
if(m.quality != null) {
n.getUI().setColText(3, m.quality);
n.attributes.quality = m.quality;
}
if(m.status != null) {
n.getUI().setColText(2, m.status);
n.attributes.status = m.status;
}
}
break;
}
}
Ext.Ajax.request({
url: '/comet',
params : { boxid: response.boxid },
success: function(result, request) {
parse_comet_response(result.responseText);
}});
};
Ext.Ajax.request({
url: '/comet',
success: function(result, request) {
parse_comet_response(result.responseText);
}});
}
/**
*
*/
// create application
tvheadend.app = function() {
// public space
return {
// public methods
init: function() {
var confpanel = new Ext.TabPanel({
activeTab:0,
autoScroll:true,
title: 'Configuration',
items: [new tvheadend.dvb,
new tvheadend.acleditor,
new tvheadend.cwceditor]
});
var pvrpanel = new Ext.TabPanel({
autoScroll:true,
title: 'Video Recorder'
});
var chpanel = new Ext.TabPanel({
autoScroll:true,
title: 'Channels'
});
var viewport = new Ext.Viewport({
layout:'border',
items:[
{
region:'south',
contentEl: 'systemlog',
split:true,
autoScroll:true,
height: 150,
minSize: 100,
maxSize: 400,
collapsible: true,
title:'System log',
margins:'0 0 0 0'
},
new Ext.TabPanel({region:'center',
activeTab:0,
items:[confpanel,
pvrpanel,
chpanel]})
]
});
new tvheadend.comet_poller;
}
};
}(); // end of app

View file

@ -31,7 +31,7 @@
#include "pvr.h"
#define ACCESS_SIMPLE \
(ACCESS_WEB_INTERFACE | ACCESS_RECORDER_VIEW | ACCESS_RECORDER_CHANGE)
(ACCESS_WEB_INTERFACE | ACCESS_RECORDER)
static struct strtab recstatustxt[] = {
{ "Recording scheduled", HTSTV_PVR_STATUS_SCHEDULED },
@ -108,7 +108,7 @@ page_root(http_connection_t *hc, http_reply_t *hr,
if(is_client_simple(hc)) {
http_redirect(hc, hr, "/simple.html");
} else {
http_redirect(hc, hr, "/ajax/index.html");
http_redirect(hc, hr, "/extjs.html");
}
return 0;
}
@ -418,7 +418,6 @@ page_pvrinfo(http_connection_t *hc, http_reply_t *hr,
return 0;
}
/**
* WEB user interface
*/
@ -430,5 +429,8 @@ webui_start(void)
http_path_add("/simple.html", NULL, page_simple, ACCESS_SIMPLE);
http_path_add("/eventinfo", NULL, page_einfo, ACCESS_SIMPLE);
http_path_add("/pvrinfo", NULL, page_pvrinfo, ACCESS_SIMPLE);
extjs_start();
comet_init();
}

View file

@ -19,6 +19,15 @@
#ifndef WEBUI_H_
#define WEBUI_H_
#include <libhts/htsmsg.h>
void webui_start(void);
void extjs_start(void);
void comet_init(void);
void comet_mailbox_add_message(htsmsg_t *m);
#endif /* WEBUI_H_ */

View file

@ -422,7 +422,7 @@ xbmsp_send_err(xbmsp_t *xbmsp, uint32_t msgid, uint8_t errcode,
vsnprintf(errbuf, sizeof(errbuf), errfmt, ap);
va_end(ap);
syslog(LOG_INFO, "%s: %s", xbmsp->xbmsp_logname, errbuf);
tvhlog(LOG_INFO, "xbmsp", "%s: %s", xbmsp->xbmsp_logname, errbuf);
slen = strlen(errbuf);