diff --git a/Makefile b/Makefile index 8e339c9f..ede52dd2 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/ajaxui/ajaxui.h b/ajaxui/ajaxui.h index ee416e8d..e695d751 100644 --- a/ajaxui/ajaxui.h +++ b/ajaxui/ajaxui.h @@ -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, diff --git a/ajaxui/ajaxui_config.c b/ajaxui/ajaxui_config.c index e6075a9d..a051ab11 100644 --- a/ajaxui/ajaxui_config.c +++ b/ajaxui/ajaxui_config.c @@ -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(); } diff --git a/ajaxui/ajaxui_config_dvb.c b/ajaxui/ajaxui_config_dvb.c index c7950832..b650819f 100644 --- a/ajaxui/ajaxui_config_dvb.c +++ b/ajaxui/ajaxui_config_dvb.c @@ -676,7 +676,6 @@ ajax_dvbmuxeditor(http_connection_t *hc, http_reply_t *hr, return 0; } - /** * */ diff --git a/ajaxui/ajaxui_config_xmltv.c b/ajaxui/ajaxui_config_xmltv.c new file mode 100644 index 00000000..c69b0872 --- /dev/null +++ b/ajaxui/ajaxui_config_xmltv.c @@ -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 . + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#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, "
"); + + switch(xmltv_status) { + default: + tcp_qprintf(tq, "

" + "XMLTV subsystem is not yet fully initialized, please retry " + "in a few seconds

"); + http_output_html(hc, hr); + return 0; + + case XMLTVSTATUS_FIND_GRABBERS_NOT_FOUND: + tcp_qprintf(tq, "

" + "XMLTV subsystem can not initialize

" + "

" + "Make sure that the 'tv_find_grabbers' executable is in " + "the system path

"); + http_output_html(hc, hr); + return 0; + + case XMLTVSTATUS_RUNNING: + break; + } + + tcp_qprintf(tq, "
"); + + 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, "
", + MAX(displines, ngrabbers) * 14); + + LIST_FOREACH(xg, &xmltv_grabbers, xg_link) { + + snprintf(buf, sizeof(buf), + "%s", 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, "
"); + + ajax_box_end(tq, AJAX_BOX_SIDEBOX); + + tcp_qprintf(tq, "
" + "
" + "
"); + + tcp_qprintf(tq, ""); + 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,"
", xg->xg_identifier); + + switch(xg->xg_status) { + case XMLTV_GRABBER_DISABLED: + tcp_qprintf(tq, + "

This grabber is currently not enabled, click " + "here " + "to enable it

"); + break; + + + case XMLTV_GRABBER_ENQUEUED: + case XMLTV_GRABBER_GRABBING: + case XMLTV_GRABBER_UNCONFIGURED: + case XMLTV_GRABBER_DYSFUNCTIONAL: + case XMLTV_GRABBER_IDLE: + tcp_qprintf(tq, "

%s

", xmltv_grabber_status_long(xg, xg->xg_status)); + break; + + } + + + tcp_qprintf(tq,"
"); + + 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); + +} diff --git a/ajaxui/ajaxui_mailbox.c b/ajaxui/ajaxui_mailbox.c index 8f2cc588..4c7862db 100644 --- a/ajaxui/ajaxui_mailbox.c +++ b/ajaxui/ajaxui_mailbox.c @@ -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)); +} + + diff --git a/ajaxui/ajaxui_mailbox.h b/ajaxui/ajaxui_mailbox.h index 2fc84b43..611ec749 100644 --- a/ajaxui/ajaxui_mailbox.h +++ b/ajaxui/ajaxui_mailbox.h @@ -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_ */ diff --git a/epg_xmltv.c b/epg_xmltv.c index d376adf6..98036d12 100644 --- a/epg_xmltv.c +++ b/epg_xmltv.c @@ -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"; + } } diff --git a/epg_xmltv.h b/epg_xmltv.h index d4d9cb8f..7673dd30 100644 --- a/epg_xmltv.h +++ b/epg_xmltv.h @@ -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__ */ diff --git a/notify.c b/notify.c index 30d52e84..7d106c90 100644 --- a/notify.c +++ b/notify.c @@ -23,6 +23,7 @@ #include #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); +} diff --git a/notify.h b/notify.h index 3ba5415b..83a949ac 100644 --- a/notify.h +++ b/notify.h @@ -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_ */ diff --git a/spawn.c b/spawn.c index 659e7290..43c8fbce 100644 --- a/spawn.c +++ b/spawn.c @@ -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); diff --git a/spawn.h b/spawn.h index 02136715..3f77508d 100644 --- a/spawn.h +++ b/spawn.h @@ -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);