Initial work for web-ui based xmltv configuration

This commit is contained in:
Andreas Öman 2008-04-19 10:51:45 +00:00
parent 1fb2447c31
commit 8161549c2f
13 changed files with 666 additions and 119 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
notify.c intercom.c
SRCS += http.c
@ -36,7 +36,7 @@ SRCS += FFdecsa.c
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_transport.c ajaxui_config_xmltv.c
JSSRCS += tvheadend.js

View file

@ -61,6 +61,10 @@ void ajax_config_channels_init(void);
int ajax_config_dvb_tab(http_connection_t *hc, http_reply_t *hr);
void ajax_config_dvb_init(void);
int ajax_config_xmltv_tab(http_connection_t *hc, http_reply_t *hr);
void ajax_config_xmltv_init(void);
void ajax_config_transport_init(void);
int ajax_transport_build_list(http_connection_t *hc, tcp_queue_t *tq,

View file

@ -31,11 +31,13 @@
#define AJAX_CONFIG_TAB_CHANNELS 0
#define AJAX_CONFIG_TAB_DVB 1
#define AJAX_CONFIG_TABS 2
#define AJAX_CONFIG_TAB_XMLTV 2
#define AJAX_CONFIG_TABS 3
const char *ajax_config_tabnames[] = {
[AJAX_CONFIG_TAB_CHANNELS] = "Channels & Groups",
[AJAX_CONFIG_TAB_DVB] = "DVB adapters",
[AJAX_CONFIG_TAB_XMLTV] = "XML-TV",
};
@ -79,6 +81,8 @@ ajax_config_dispatch(http_connection_t *hc, http_reply_t *hr,
return ajax_config_channels_tab(hc, hr);
case AJAX_CONFIG_TAB_DVB:
return ajax_config_dvb_tab(hc, hr);
case AJAX_CONFIG_TAB_XMLTV:
return ajax_config_xmltv_tab(hc, hr);
default:
return HTTP_STATUS_NOT_FOUND;
@ -126,4 +130,5 @@ ajax_config_init(void)
ajax_config_channels_init();
ajax_config_dvb_init();
ajax_config_xmltv_init();
}

View file

@ -676,7 +676,6 @@ ajax_dvbmuxeditor(http_connection_t *hc, http_reply_t *hr,
return 0;
}
/**
*
*/

View file

@ -0,0 +1,209 @@
/*
* tvheadend, AJAX / HTML user 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/>.
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "tvhead.h"
#include "http.h"
#include "ajaxui.h"
#include "channels.h"
#include "epg_xmltv.h"
#include "psi.h"
#include "transports.h"
#include "ajaxui_mailbox.h"
/**
* XMLTV configuration
*/
int
ajax_config_xmltv_tab(http_connection_t *hc, http_reply_t *hr)
{
tcp_queue_t *tq = &hr->hr_tq;
xmltv_grabber_t *xg;
int ngrabbers = 0;
int displines = 21;
int csize[10];
const char *cells[10];
int o = 1;
char buf[200];
tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
switch(xmltv_status) {
default:
tcp_qprintf(tq, "<p style=\"text-align: center; font-weight: bold\">"
"XMLTV subsystem is not yet fully initialized, please retry "
"in a few seconds</p></div>");
http_output_html(hc, hr);
return 0;
case XMLTVSTATUS_FIND_GRABBERS_NOT_FOUND:
tcp_qprintf(tq, "<p style=\"text-align: center; font-weight: bold\">"
"XMLTV subsystem can not initialize</p>"
"<p style=\"text-align: center\">"
"Make sure that the 'tv_find_grabbers' executable is in "
"the system path</p></div>");
http_output_html(hc, hr);
return 0;
case XMLTVSTATUS_RUNNING:
break;
}
tcp_qprintf(tq, "<div style=\"float: left; width:45%\">");
ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, "XMLTV grabbers");
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
ngrabbers++;
ajax_table_header(hc, tq,
(const char *[]){"Grabber", "Status", NULL},
(int[]){4,2}, ngrabbers > displines, csize);
tcp_qprintf(tq, "<hr><div "
"style=\"height: %dpx; overflow: auto\" class=\"normallist\">",
MAX(displines, ngrabbers) * 14);
LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
snprintf(buf, sizeof(buf),
"<a href=\"javascript:void(0);\" "
"onClick=\"new Ajax.Updater('grabberpane', "
"'/ajax/xmltvgrabber/%s', {method: 'get', evalScripts: true})\""
">%s</a>", xg->xg_identifier, xg->xg_title);
cells[0] = buf;
cells[1] = xmltv_grabber_status(xg);
cells[2] = NULL;
ajax_table_row(tq, cells, csize, &o,
(const char *[]){NULL, "status"},
xg->xg_identifier);
}
ajax_mailbox_start(tq, "xmltvgrabbers");
tcp_qprintf(tq, "</div>");
ajax_box_end(tq, AJAX_BOX_SIDEBOX);
tcp_qprintf(tq, "</div>"
"<div id=\"grabberpane\" style=\"float: left; width:55%\">"
"</div>");
tcp_qprintf(tq, "</div>");
http_output_html(hc, hr);
return 0;
}
/**
* Display detailes about a grabber
*/
static int
ajax_xmltvgrabber(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
xmltv_grabber_t *xg;
tcp_queue_t *tq = &hr->hr_tq;
if(remain == NULL || (xg = xmltv_grabber_find(remain)) == NULL)
return HTTP_STATUS_NOT_FOUND;
ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, xg->xg_title);
tcp_qprintf(tq,"<div id=\"details_%s\">", xg->xg_identifier);
switch(xg->xg_status) {
case XMLTV_GRABBER_DISABLED:
tcp_qprintf(tq,
"<p>This grabber is currently not enabled, click "
"<a href=\"javascript:void(0);\" "
"onClick=\"new Ajax.Request('/ajax/xmltvgrabbermode/%s', "
"{parameters: {'mode': 'enable'}})\">here</a> "
"to enable it</p>");
break;
case XMLTV_GRABBER_ENQUEUED:
case XMLTV_GRABBER_GRABBING:
case XMLTV_GRABBER_UNCONFIGURED:
case XMLTV_GRABBER_DYSFUNCTIONAL:
case XMLTV_GRABBER_IDLE:
tcp_qprintf(tq, "<p>%s</p>", xmltv_grabber_status_long(xg, xg->xg_status));
break;
}
tcp_qprintf(tq,"</div>");
ajax_box_end(tq, AJAX_BOX_SIDEBOX);
ajax_mailbox_start(tq, xg->xg_identifier);
http_output_html(hc, hr);
return 0;
}
/**
* Enable / Disable a grabber
*/
static int
ajax_xmltvgrabbermode(http_connection_t *hc, http_reply_t *hr,
const char *remain, void *opaque)
{
xmltv_grabber_t *xg;
tcp_queue_t *tq = &hr->hr_tq;
if(remain == NULL || (xg = xmltv_grabber_find(remain)) == NULL)
return HTTP_STATUS_NOT_FOUND;
xmltv_grabber_enable(xg);
tcp_qprintf(tq,"$('details_%s').innerHTML='Ok, please wait...';",
xg->xg_identifier);
http_output(hc, hr, "text/javascript; charset=UTF8", NULL, 0);
return 0;
}
/**
*
*/
void
ajax_config_xmltv_init(void)
{
http_path_add("/ajax/xmltvgrabber" , NULL, ajax_xmltvgrabber);
http_path_add("/ajax/xmltvgrabbermode" , NULL, ajax_xmltvgrabbermode);
}

View file

@ -28,6 +28,7 @@
#include "http.h"
#include "ajaxui.h"
#include "transports.h"
#include "epg_xmltv.h"
#define MAILBOX_UNUSED_TIMEOUT 15
#define MAILBOX_EMPTY_REPLY_TIMEOUT 10
@ -407,7 +408,6 @@ ajax_mailbox_tdmi_services_change(th_dvb_mux_instance_t *tdmi)
}
void
ajax_mailbox_tda_change(th_dvb_adapter_t *tda)
{
@ -420,3 +420,18 @@ ajax_mailbox_tda_change(th_dvb_adapter_t *tda)
"dvbmuxlist", tda->tda_identifier,
buf);
}
void
ajax_mailbox_xmltv_grabber_status_change(xmltv_grabber_t *xg, int status)
{
ajax_mailbox_update_div("xmltvgrabbers",
"status", xg->xg_identifier,
xmltv_grabber_status(xg));
ajax_mailbox_update_div(xg->xg_identifier,
"details", xg->xg_identifier,
xmltv_grabber_status_long(xg, status));
}

View file

@ -35,4 +35,9 @@ void ajax_mailbox_tda_change(th_dvb_adapter_t *tda);
void ajax_mailbox_start(tcp_queue_t *tq, const char *identifier);
struct xmltv_grabber;
void ajax_mailbox_xmltv_grabber_status_change(struct xmltv_grabber *xg,
int status);
#endif /* AJAXUI_MAILBOX_H_ */

View file

@ -37,48 +37,29 @@
#include "epg_xmltv.h"
#include "refstr.h"
#include "spawn.h"
#include "intercom.h"
#include "notify.h"
extern int xmltvreload;
LIST_HEAD(, xmltv_channel) xmltv_channel_list;
typedef struct xmltv_map {
LIST_ENTRY(xmltv_map) xm_link;
th_channel_t *xm_channel; /* Set if we have resolved the channel */
int xm_isupdated;
} xmltv_map_t;
typedef struct xmltv_channel {
LIST_ENTRY(xmltv_channel) xc_link;
const char *xc_name;
const char *xc_displayname;
refstr_t *xc_icon;
LIST_HEAD(, xmltv_map) xc_maps;
struct event_queue xc_events;
} xmltv_channel_t;
int xmltv_status;
struct xmltv_grabber_list xmltv_grabbers;
struct xmltv_grabber_queue xmltv_grabber_workq;
static pthread_mutex_t xmltv_work_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t xmltv_work_cond = PTHREAD_COND_INITIALIZER;
static xmltv_channel_t *
xc_find(const char *name)
xc_find(xmltv_grabber_t *xg, const char *name)
{
xmltv_channel_t *xc;
LIST_FOREACH(xc, &xmltv_channel_list, xc_link)
LIST_FOREACH(xc, &xg->xg_channels, xc_link)
if(!strcmp(xc->xc_name, name))
return xc;
xc = calloc(1, sizeof(xmltv_channel_t));
xc->xc_name = strdup(name);
TAILQ_INIT(&xc->xc_events);
LIST_INSERT_HEAD(&xmltv_channel_list, xc, xc_link);
LIST_INSERT_HEAD(&xg->xg_channels, xc, xc_link);
return xc;
}
@ -88,12 +69,12 @@ xc_find(const char *name)
#define XML_FOREACH(n) for(; (n) != NULL; (n) = (n)->next)
static void
xmltv_parse_channel(xmlNode *n, char *chid)
xmltv_parse_channel(xmltv_grabber_t *xg, xmlNode *n, char *chid)
{
char *t, *c;
xmltv_channel_t *xc;
xc = xc_find(chid);
xc = xc_find(xg, chid);
XML_FOREACH(n) {
@ -107,8 +88,8 @@ xmltv_parse_channel(xmlNode *n, char *chid)
if(!strcmp((char *)n->name, "icon")) {
t = (char *)xmlGetProp(n, (unsigned char *)"src");
refstr_free(xc->xc_icon);
xc->xc_icon = refstr_alloc(t);
free(xc->xc_icon_url);
xc->xc_icon_url = strdup(t);
xmlFree(t);
}
xmlFree(c);
@ -146,7 +127,8 @@ str2time(char *str)
static void
xmltv_parse_programme(xmlNode *n, char *chid, char *startstr, char *stopstr)
xmltv_parse_programme(xmltv_grabber_t *xg, xmlNode *n,
char *chid, char *startstr, char *stopstr)
{
char *c;
event_t *e;
@ -154,7 +136,7 @@ xmltv_parse_programme(xmlNode *n, char *chid, char *startstr, char *stopstr)
int duration;
xmltv_channel_t *xc;
xc = xc_find(chid);
xc = xc_find(xg, chid);
start = str2time(startstr);
stop = str2time(stopstr);
@ -179,7 +161,7 @@ xmltv_parse_programme(xmlNode *n, char *chid, char *startstr, char *stopstr)
static void
xmltv_parse_tv(xmlNode *n)
xmltv_parse_tv(xmltv_grabber_t *xg, xmlNode *n)
{
char *chid;
char *start;
@ -193,7 +175,7 @@ xmltv_parse_tv(xmlNode *n)
if(!strcmp((char *)n->name, "channel")) {
chid = (char *)xmlGetProp(n, (unsigned char *)"id");
if(chid != NULL) {
xmltv_parse_channel(n->children, chid);
xmltv_parse_channel(xg, n->children, chid);
xmlFree(chid);
}
} else if(!strcmp((char *)n->name, "programme")) {
@ -201,7 +183,7 @@ xmltv_parse_tv(xmlNode *n)
start = (char *)xmlGetProp(n, (unsigned char *)"start");
stop = (char *)xmlGetProp(n, (unsigned char *)"stop");
xmltv_parse_programme(n->children, chid, start, stop);
xmltv_parse_programme(xg, n->children, chid, start, stop);
xmlFree(chid);
xmlFree(start);
@ -213,11 +195,11 @@ xmltv_parse_tv(xmlNode *n)
static void
xmltv_parse_root(xmlNode *n)
xmltv_parse_root(xmltv_grabber_t *xg, xmlNode *n)
{
XML_FOREACH(n) {
if(n->type == XML_ELEMENT_NODE && !strcmp((char *)n->name, "tv"))
xmltv_parse_tv(n->children);
xmltv_parse_tv(xg, n->children);
}
}
@ -225,66 +207,22 @@ xmltv_parse_root(xmlNode *n)
/*
*
*/
void
xmltv_flush(void)
static void
xmltv_flush_events(xmltv_grabber_t *xg)
{
xmltv_channel_t *xc;
xmltv_map_t *xm;
event_t *e;
LIST_FOREACH(xc, &xmltv_channel_list, xc_link) {
LIST_FOREACH(xc, &xg->xg_channels, xc_link) {
while((e = TAILQ_FIRST(&xc->xc_events)) != NULL) {
TAILQ_REMOVE(&xc->xc_events, e, e_link);
epg_event_free(e);
}
while((xm = LIST_FIRST(&xc->xc_maps)) != NULL) {
LIST_REMOVE(xm, xm_link);
free(xm);
}
}
}
/*
*
*/
void
xmltv_load(void)
{
xmlDoc *doc = NULL;
xmlNode *root_element = NULL;
const char *prog;
char *outbuf;
int outlen;
prog = config_get_str("xmltvgrabber", NULL);
if(prog == NULL)
return;
syslog(LOG_INFO, "xmltv: Starting grabber \"%s\"", prog);
outlen = spawn_and_store_stdout(prog, &outbuf);
if(outlen < 1) {
syslog(LOG_ERR, "xmltv: Cannot parse output from \"%s\"", prog);
return;
}
doc = xmlParseMemory(outbuf, outlen);
if(doc == NULL) {
syslog(LOG_ERR, "xmltv: Error while parsing output from \"%s\"", prog);
free(outbuf);
return;
}
xmltv_flush();
root_element = xmlDocGetRootElement(doc);
xmltv_parse_root(root_element);
xmlFreeDoc(doc);
xmlCleanupParser();
syslog(LOG_INFO, "xmltv: EPG sucessfully loaded and parsed");
free(outbuf);
}
#if 0
/*
*
*/
@ -381,7 +319,158 @@ xmltv_transfer(void)
}
}
}
#endif
/*
* Execute grabber and parse result,
*
* Return nextstate
*/
static int
xmltv_grab(xmltv_grabber_t *xg)
{
xmlDoc *doc = NULL;
xmlNode *root_element;
const char *prog;
char *outbuf;
int outlen;
prog = xg->xg_path;
syslog(LOG_INFO, "xmltv: Starting grabber \"%s\"", prog);
outlen = spawn_and_store_stdout(prog, NULL, &outbuf);
if(outlen < 1) {
syslog(LOG_ERR, "xmltv: No output from \"%s\"", prog);
return XMLTV_GRABBER_UNCONFIGURED;
}
doc = xmlParseMemory(outbuf, outlen);
if(doc == NULL) {
syslog(LOG_ERR, "xmltv: Error while parsing output from \"%s\"", prog);
free(outbuf);
return XMLTV_GRABBER_DYSFUNCTIONAL;
}
xmltv_flush_events(xg);
root_element = xmlDocGetRootElement(doc);
xmltv_parse_root(xg, root_element);
xmlFreeDoc(doc);
xmlCleanupParser();
syslog(LOG_INFO, "xmltv: EPG sucessfully loaded and parsed");
free(outbuf);
return XMLTV_GRABBER_IDLE;
}
static struct strtab grabberstatustab[] = {
{ "Disabled", XMLTV_GRABBER_DISABLED },
{ "Missing configuration", XMLTV_GRABBER_UNCONFIGURED },
{ "Dysfunctional", XMLTV_GRABBER_DYSFUNCTIONAL },
{ "Waiting for execution", XMLTV_GRABBER_ENQUEUED },
{ "Grabbing", XMLTV_GRABBER_GRABBING },
{ "Idle", XMLTV_GRABBER_IDLE },
};
const char *
xmltv_grabber_status(xmltv_grabber_t *xg)
{
return val2str(xg->xg_status, grabberstatustab) ?: "Invalid";
}
/**
*
*/
static int
grabbercmp(xmltv_grabber_t *a, xmltv_grabber_t *b)
{
return strcasecmp(a->xg_title, b->xg_title);
}
/**
* Enlist all active grabbers (for user to choose among)
*/
static int
xmltv_find_grabbers(const char *prog)
{
char *outbuf;
int outlen;
char *s, *a, *b;
xmltv_grabber_t *xg;
int n = 0;
char buf[40];
outlen = spawn_and_store_stdout(prog, NULL, &outbuf);
if(outlen < 1) {
return -1;
}
s = outbuf;
while(1) {
a = s;
while(*s && *s != '|')
s++;
if(!*s)
break;
*s++ = 0;
b = s;
while(*s > 31)
s++;
if(!*s)
break;
*s++ = 0;
while(*s < 31 && *s > 0)
s++;
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
if(!strcmp(b, xg->xg_title))
break;
if(xg != NULL)
continue;
xg = calloc(1, sizeof(xmltv_grabber_t));
xg->xg_path = a;
xg->xg_title = b;
snprintf(buf, sizeof(buf), "xmlgrabber_%d", n++);
xg->xg_identifier = strdup(buf);
LIST_INSERT_SORTED(&xmltv_grabbers, xg, xg_link, grabbercmp);
}
return 0;
}
/**
*
*/
static void
xmltv_thread_set_status(icom_t *ic, xmltv_grabber_t *xg, int s)
{
htsmsg_t *m = htsmsg_create();
xg->xg_status = s;
htsmsg_add_u32(m, "status", s);
htsmsg_add_str(m, "identifier", xg->xg_identifier);
icom_send_msg_from_thread(ic, m);
htsmsg_destroy(m);
}
/*
*
@ -389,31 +478,172 @@ xmltv_transfer(void)
static void *
xmltv_thread(void *aux)
{
int sleeptime;
icom_t *ic = aux;
struct timespec ts;
xmltv_grabber_t *xg;
int r;
time_t n;
/* Default to 12 hours */
sleeptime = atoi(config_get_str("xmltvinterval", "43200"));
/* Start by finding all available grabbers, we try with a few
different locations */
if(xmltv_find_grabbers("/bin/tv_find_grabbers") &&
xmltv_find_grabbers("/usr/bin/tv_find_grabbers") &&
xmltv_find_grabbers("/usr/local/bin/tv_find_grabbers")) {
xmltv_status = XMLTVSTATUS_FIND_GRABBERS_NOT_FOUND;
return NULL;
}
xmltv_status = XMLTVSTATUS_RUNNING;
pthread_mutex_lock(&xmltv_work_lock);
while(1) {
xmltv_load();
xmltv_transfer();
sleep(sleeptime);
xg = TAILQ_FIRST(&xmltv_grabber_workq);
if(xg != NULL) {
xmltv_thread_set_status(ic, xg, XMLTV_GRABBER_GRABBING);
TAILQ_REMOVE(&xmltv_grabber_workq, xg, xg_work_link);
pthread_mutex_unlock(&xmltv_work_lock);
r = xmltv_grab(xg);
pthread_mutex_lock(&xmltv_work_lock);
xmltv_thread_set_status(ic, xg, r);
time(&n);
if(r == XMLTV_GRABBER_IDLE) {
/* Completed load */
xg->xg_nextgrab = n + 3600;
} else {
xg->xg_nextgrab = n + 60;
}
continue;
}
n = INT32_MAX;
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
n = xg->xg_nextgrab ? MIN(n, xg->xg_nextgrab) : n;
ts.tv_sec = n;
ts.tv_nsec = 0;
pthread_cond_timedwait(&xmltv_work_cond, &xmltv_work_lock, &ts);
time(&n);
LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
switch(xg->xg_status) {
case XMLTV_GRABBER_GRABBING:
abort();
case XMLTV_GRABBER_DISABLED:
case XMLTV_GRABBER_ENQUEUED:
break;
case XMLTV_GRABBER_UNCONFIGURED:
case XMLTV_GRABBER_DYSFUNCTIONAL:
case XMLTV_GRABBER_IDLE:
if(xg->xg_nextgrab <= n + 1)
TAILQ_INSERT_TAIL(&xmltv_grabber_workq, xg, xg_work_link);
break;
}
}
}
sleep(100000);
return NULL;
}
void
xmltv_grabber_enable(xmltv_grabber_t *xg)
{
pthread_mutex_lock(&xmltv_work_lock);
if(xg->xg_status == XMLTV_GRABBER_DISABLED) {
xg->xg_status = XMLTV_GRABBER_ENQUEUED;
TAILQ_INSERT_TAIL(&xmltv_grabber_workq, xg, xg_work_link);
pthread_cond_signal(&xmltv_work_cond);
}
pthread_mutex_unlock(&xmltv_work_lock);
}
/**
*
*/
static void
icom_cb(void *opaque, htsmsg_t *m)
{
const char *s;
uint32_t status;
xmltv_grabber_t *xg;
if(!htsmsg_get_u32(m, "status", &status)) {
s = htsmsg_get_str(m, "identifier");
if(s != NULL && (xg = xmltv_grabber_find(s)) != NULL) {
notify_xmltv_grabber_status_change(xg, status);
}
}
htsmsg_destroy(m);
}
/**
* Locate a grabber by its id
*/
xmltv_grabber_t *
xmltv_grabber_find(const char *id)
{
xmltv_grabber_t *xg;
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
if(!strcmp(id, xg->xg_identifier))
return xg;
return NULL;
}
/*
/**
*
*/
void
xmltv_init(void)
{
pthread_t ptid;
icom_t *ic = icom_create(icom_cb, NULL);
if(config_get_str("xmltvgrabber", NULL) == NULL) {
syslog(LOG_INFO, "xmltv: No grabber configured, xmltv subsystem disabled");
return;
}
TAILQ_INIT(&xmltv_grabber_workq);
pthread_create(&ptid, NULL, xmltv_thread, NULL);
pthread_create(&ptid, NULL, xmltv_thread, ic);
}
/**
*
*/
const char *
xmltv_grabber_status_long(xmltv_grabber_t *xg, int status)
{
static char buf[1000];
switch(status) {
case XMLTV_GRABBER_UNCONFIGURED:
snprintf(buf, sizeof(buf),
"This grabber is not configured, you need to configure it "
"manually by running '%s --configure' in a shell",
xg->xg_path);
return buf;
case XMLTV_GRABBER_DYSFUNCTIONAL:
snprintf(buf, sizeof(buf),
"This grabber does not produce valid XML, please check "
"manually by running '%s' in a shell",
xg->xg_path);
return buf;
case XMLTV_GRABBER_ENQUEUED:
case XMLTV_GRABBER_GRABBING:
return "Grabbing, please wait";
default:
return "Unknown status";
}
}

View file

@ -19,6 +19,67 @@
#ifndef XMLTV_H
#define XMLTV_H
LIST_HEAD(xmltv_grabber_list, xmltv_grabber);
TAILQ_HEAD(xmltv_grabber_queue, xmltv_grabber);
LIST_HEAD(xmltv_channel_list, xmltv_channel);
typedef struct xmltv_grabber {
LIST_ENTRY(xmltv_grabber) xg_link;
char *xg_path;
char *xg_title;
char *xg_identifier;
enum {
XMLTV_GRABBER_DISABLED,
XMLTV_GRABBER_UNCONFIGURED,
XMLTV_GRABBER_DYSFUNCTIONAL,
XMLTV_GRABBER_ENQUEUED,
XMLTV_GRABBER_GRABBING,
XMLTV_GRABBER_IDLE,
} xg_status;
TAILQ_ENTRY(xmltv_grabber) xg_work_link;
time_t xg_nextgrab;
struct xmltv_channel_list xg_channels;
} xmltv_grabber_t;
/**
* A channel in the XML-TV world
*/
typedef struct xmltv_channel {
LIST_ENTRY(xmltv_channel) xc_link;
char *xc_name;
char *xc_displayname;
char *xc_channel;
char *xc_icon_url;
struct event_queue xc_events;
} xmltv_channel_t;
#define XMLTVSTATUS_STARTING 0
#define XMLTVSTATUS_FIND_GRABBERS_NOT_FOUND 1
#define XMLTVSTATUS_RUNNING 2
extern int xmltv_status;
extern struct xmltv_grabber_list xmltv_grabbers;
void xmltv_init(void);
const char *xmltv_grabber_status(xmltv_grabber_t *xg);
void xmltv_grabber_enable(xmltv_grabber_t *xg);
xmltv_grabber_t *xmltv_grabber_find(const char *id);
const char *xmltv_grabber_status_long(xmltv_grabber_t *xg, int status);
#endif /* __XMLTV_H__ */

View file

@ -23,6 +23,7 @@
#include <string.h>
#include "tvhead.h"
#include "notify.h"
#include "ajaxui/ajaxui_mailbox.h"
void
@ -59,3 +60,9 @@ notify_tda_change(th_dvb_adapter_t *tda)
{
ajax_mailbox_tda_change(tda);
}
void
notify_xmltv_grabber_status_change(struct xmltv_grabber *xg, int status)
{
ajax_mailbox_xmltv_grabber_status_change(xg, status);
}

View file

@ -19,14 +19,21 @@
#ifndef NOTIFY_H_
#define NOTIFY_H_
void notify_tdmi_state_change(th_dvb_mux_instance_t *tdmi);
struct xmltv_grabber;
struct th_dvb_mux_instance;
struct th_dvb_adapterr;
void notify_tdmi_name_change(th_dvb_mux_instance_t *tdmi);
void notify_tdmi_state_change(struct th_dvb_mux_instance *tdmi);
void notify_tdmi_status_change(th_dvb_mux_instance_t *tdmi);
void notify_tdmi_name_change(struct th_dvb_mux_instance *tdmi);
void notify_tdmi_services_change(th_dvb_mux_instance_t *tdmi);
void notify_tdmi_status_change(struct th_dvb_mux_instance *tdmi);
void notify_tda_change(th_dvb_adapter_t *tda);
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,
int status);
#endif /* NOTIFY_H_ */

21
spawn.c
View file

@ -29,6 +29,8 @@
#include "dispatch.h"
#include "spawn.h"
extern char **environ;
static dtimer_t reaper_timer;
pthread_mutex_t spawn_mutex = PTHREAD_MUTEX_INITIALIZER;
@ -139,15 +141,22 @@ spawn_enq(const char *name, int pid)
* *outp will point to the allocated buffer
* The function will return the size of the buffer
*/
int
spawn_and_store_stdout(const char *prog, char **outp)
spawn_and_store_stdout(const char *prog, char *const argv[], char **outp)
{
pid_t p;
int fd[2], r, totalsize = 0;
char *outbuf;
struct spawn_output_buf_queue bufs;
spawn_output_buf_t *b = NULL;
const char *local_argv[2];
if(argv == NULL) {
local_argv[0] = prog;
local_argv[1] = NULL;
argv = (void *)local_argv;
}
if(pipe(fd) == -1)
return -1;
@ -167,7 +176,7 @@ spawn_and_store_stdout(const char *prog, char **outp)
dup2(fd[1], 1);
close(fd[1]);
syslog(LOG_INFO, "spawn: Executing \"%s\"", prog);
execl(prog, prog, NULL);
execve(prog, argv, environ);
syslog(LOG_ERR, "spawn: pid %d cannot execute %s -- %s",
getpid(), prog, strerror(errno));
close(1);
@ -226,10 +235,6 @@ spawnv(const char *prog, char *const argv[])
{
pid_t p;
char *envp[1];
envp[0] = NULL;
p = fork();
if(p == -1) {
@ -242,7 +247,7 @@ spawnv(const char *prog, char *const argv[])
close(0);
close(2);
syslog(LOG_INFO, "spawn: Executing \"%s\"", prog);
execve(prog, argv, envp);
execve(prog, argv, environ);
syslog(LOG_ERR, "spawn: pid %d cannot execute %s -- %s",
getpid(), prog, strerror(errno));
close(1);

View file

@ -19,7 +19,7 @@
#ifndef SPAWN_H
#define SPAWN_H
int spawn_and_store_stdout(const char *prog, char **outp);
int spawn_and_store_stdout(const char *prog, char *const argv[], char **outp);
void spawn_init(void);