diff --git a/src/cron.c b/src/cron.c new file mode 100644 index 00000000..88723853 --- /dev/null +++ b/src/cron.c @@ -0,0 +1,47 @@ +#include "cron.h" +#include + +int cron_is_time ( cron_t *cron ) +{ + int ret = 1; + uint64_t b = 0x1; + + /* Get the current time */ + time_t t = time(NULL); + struct tm now; + localtime_r(&t, &now); + + /* Check */ + if ( ret && !((b << now.tm_min) & cron->min) ) ret = 0; + if ( ret && !((b << now.tm_hour) & cron->hour) ) ret = 0; + if ( ret && !((b << now.tm_mday) & cron->dom) ) ret = 0; + if ( ret && !((b << now.tm_mon) & cron->mon) ) ret = 0; + if ( ret && !((b << now.tm_wday) & cron->dow) ) ret = 0; + + return ret; +} + +void cron_pack ( cron_t *cron, htsmsg_t *msg ) +{ + htsmsg_add_u32(msg, "min0", cron->min & 0xFFFFFFFF); + htsmsg_add_u32(msg, "min1", cron->min >> 32); + htsmsg_add_u32(msg, "hour", cron->hour); + htsmsg_add_u32(msg, "dom", cron->dom); + htsmsg_add_u32(msg, "mon", cron->mon); + htsmsg_add_u32(msg, "dow", cron->dow); +} + +void cron_unpack ( cron_t *cron, htsmsg_t *msg ) +{ + uint32_t u32 = 0; + htsmsg_get_u32(msg, "min0", &u32); + cron->min = u32; + htsmsg_get_u32(msg, "min1", &u32); + cron->min += ((uint64_t)u32 << 32); + htsmsg_get_u32(msg, "hour", &cron->hour); + htsmsg_get_u32(msg, "dom", &cron->dom); + htsmsg_get_u32(msg, "mon", &u32); + cron->mon = (uint16_t)u32; + htsmsg_get_u32(msg, "dow", &u32); + cron->dow = (uint8_t)u32; +} diff --git a/src/cron.h b/src/cron.h new file mode 100644 index 00000000..9724c84d --- /dev/null +++ b/src/cron.h @@ -0,0 +1,22 @@ +#ifndef __CRON_H__ +#define __CRON_H__ + +#include +#include +#include "htsmsg.h" + +typedef struct cron +{ + uint64_t min; ///< Minutes + uint32_t hour; ///< Hours + uint32_t dom; ///< Day of month + uint16_t mon; ///< Month + uint8_t dow; ///< Day of week +} cron_t; + +void cron_set_string ( cron_t *cron, const char *str ); +int cron_is_time ( cron_t *cron ); +void cron_pack ( cron_t *cron, htsmsg_t *msg ); +void cron_unpack ( cron_t *cron, htsmsg_t *msg ); + +#endif /* __CRON_H__ */ diff --git a/src/epggrab.c b/src/epggrab.c new file mode 100644 index 00000000..a3de81fc --- /dev/null +++ b/src/epggrab.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include "htsmsg.h" +#include "settings.h" +#include "tvheadend.h" +#include "epggrab.h" + +/* Thread protection */ +int epggrab_confver; +pthread_mutex_t epggrab_mutex; +pthread_cond_t epggrab_cond; + +/* Config */ +uint32_t epggrab_advanced; +uint32_t epggrab_eit; +uint32_t epggrab_interval; +epggrab_module_t* epggrab_module; +epggrab_sched_list_t epggrab_schedule; + +/* Internal prototypes */ +static void* _epggrab_thread ( void* ); +static time_t _epggrab_thread_simple ( void ); +static time_t _epggrab_thread_advanced ( void ); +static void _epggrab_load ( void ); +static void _epggrab_save ( void ); +static void _epggrab_set_schedule ( int, epggrab_sched_t* ); + +/* + * Initialise + */ +void epggrab_init ( void ) +{ + /* Defaults */ + epggrab_advanced = 0; + epggrab_eit = 1; // on air grab enabled + epggrab_interval = 12; // hours + epggrab_module = NULL; // disabled + + // TODO: HACK : testing +#if 0 + epggrab_advanced = 1; + epggrab_sched_t es; + memset(&es, 0, sizeof(epggrab_sched_t)); + es.opts = strdup("-d 1"); + es.mod = NULL; + es.cron.hour = 0x1001; + _epggrab_set_schedule(1, &es); + _epggrab_save(); +#endif + + /* Start thread */ + pthread_t tid; + pthread_attr_t tattr; + pthread_attr_init(&tattr); + pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + pthread_create(&tid, &tattr, _epggrab_thread, NULL); +} + +/* + * Simple run + */ +time_t _epggrab_thread_simple ( void ) +{ + /* Copy config */ + time_t ret = time(NULL) + epggrab_interval; + epggrab_module_t* mod = epggrab_module; + + /* Unlock */ + pthread_mutex_unlock(&epggrab_mutex); + + /* Run the module */ + if ( mod ) mod->run(NULL); + + /* Re-lock */ + pthread_mutex_lock(&epggrab_mutex); + + return ret; +} + +/* + * Advanced run + * + * TODO: might be nice to put each module run in another thread, in case + * they're scheduled at the same time? + */ +time_t _epggrab_thread_advanced ( void ) +{ + epggrab_sched_t *s; + + /* Determine which to run */ + LIST_FOREACH(s, &epggrab_schedule, es_link) { + if ( s->mod && cron_is_time(&s->cron) ) { + s->mod->run(s->opts); + } + } + + // TODO: make this driven off next time + // get cron to tell us when next call will run + return time(NULL) + 30; +} + +/* + * Thread + */ +void* _epggrab_thread ( void* p ) +{ + int confver = 0; + struct timespec ts; + pthread_mutex_lock(&epggrab_mutex); + + /* Load */ + _epggrab_load(); + + /* Setup timeout */ + ts.tv_nsec = 0; + ts.tv_sec = 0; + + while ( 1 ) { + + /* Check for config change */ + while ( confver == epggrab_confver ) { + if ( pthread_cond_timedwait(&epggrab_cond, &epggrab_mutex, &ts) == ETIMEDOUT ) break; + } + + /* Run grabber */ + ts.tv_sec = epggrab_advanced + ? _epggrab_thread_advanced() + : _epggrab_thread_simple(); + } + + return NULL; +} + +/* ************************************************************************** + * Module access + * *************************************************************************/ + +epggrab_module_t* epggrab_module_find_by_name ( const char *name ) +{ + return NULL; +} + +/* ************************************************************************** + * Configuration handling + * *************************************************************************/ + +void _epggrab_load ( void ) +{ + htsmsg_t *m, *s, *e, *c; + htsmsg_field_t *f; + const char *str; + epggrab_sched_t *es; + + /* No config */ + if ((m = hts_settings_load("epggrab/config")) == NULL) + return; + + /* Load settings */ + htsmsg_get_u32(m, "advanced", &epggrab_advanced); + printf("advanced = %d\n", epggrab_advanced); + htsmsg_get_u32(m, "eit", &epggrab_eit); + printf("eit = %d\n", epggrab_eit); + if ( !epggrab_advanced ) { + htsmsg_get_u32(m, "interval", &epggrab_interval); + printf("interval = %d\n", epggrab_interval); + str = htsmsg_get_str(m, "module"); + if (str) epggrab_module = epggrab_module_find_by_name(str); + printf("module = %p\n", epggrab_module); + } else { + if ((s = htsmsg_get_list(m, "schedule")) != NULL) { + HTSMSG_FOREACH(f, s) { + if ((e = htsmsg_get_map_by_field(f)) != NULL) { + es = calloc(1, sizeof(epggrab_sched_t)); + str = htsmsg_get_str(e, "module"); + if (str) es->mod = epggrab_module_find_by_name(str); + str = htsmsg_get_str(e, "opts"); + if (str) es->opts = strdup(str); + c = htsmsg_get_map(e, "cron"); + if (f) cron_unpack(&es->cron, c); + printf("scheduleN: mod %p opts %s cron 0x%04X\n", + es->mod, es->opts, es->cron.hour); + } + } + } + } +} + +void _epggrab_save ( void ) +{ + htsmsg_t *m, *s, *e, *c; + epggrab_sched_t *es; + + /* Enable EIT */ + + /* Register */ + epggrab_confver++; + pthread_cond_signal(&epggrab_cond); + + /* Save */ + m = htsmsg_create_map(); + htsmsg_add_u32(m, "advanced", epggrab_advanced); + htsmsg_add_u32(m, "eit", epggrab_eit); + if ( !epggrab_advanced ) { + htsmsg_add_u32(m, "interval", epggrab_interval); + if ( epggrab_module ) + htsmsg_add_str(m, "module", epggrab_module->name()); + } else { + s = htsmsg_create_list(); + LIST_FOREACH(es, &epggrab_schedule, es_link) { + e = htsmsg_create_map(); + if ( es->mod ) htsmsg_add_str(e, "module", es->mod->name()); + if ( es->opts ) htsmsg_add_str(e, "opts", es->opts); + c = htsmsg_create_map(); + cron_pack(&es->cron, c); + htsmsg_add_msg(e, "cron", c); + htsmsg_add_msg(s, NULL, e); + } + htsmsg_add_msg(m, "schedule", s); + } + hts_settings_save(m, "epggrab/config"); + htsmsg_destroy(m); +} + +void _epggrab_set_schedule ( int count, epggrab_sched_t *sched ) +{ + int i; + + /* Remove existing */ +#if 0 + for ( i = 0; i < epggrab_schedcnt; i++ ) { + if ( epggrab_schedule[i].opts ) free(epggrab_schedule[i].opts); + } + free(epggrab_schedule); +#endif + + /* Create new */ + for ( i = 0; i < count; i++ ) { + epggrab_sched_t *es = calloc(1, sizeof(epggrab_sched_t)); + es->mod = sched[i].mod; + es->cron = sched[i].cron; + if ( sched[i].opts ) es->opts = strdup(sched[i].opts); + LIST_INSERT_HEAD(&epggrab_schedule, es, es_link); + } +} + +void epggrab_set_simple ( uint32_t interval, epggrab_module_t *mod ) +{ + /* Set config */ + lock_assert(&epggrab_mutex); + epggrab_advanced = 0; + epggrab_interval = interval; + epggrab_module = mod; + + /* Save */ + _epggrab_save(); +} + +void epggrab_set_advanced ( uint32_t count, epggrab_sched_t *sched ) +{ + /* Set config */ + lock_assert(&epggrab_mutex); + epggrab_advanced = 1; + _epggrab_set_schedule(count, sched); + + /* Save */ + _epggrab_save(); +} + +void epggrab_set_eit ( uint32_t eit ) +{ + /* Set config */ + lock_assert(&epggrab_mutex); + epggrab_eit = eit; + + /* Save */ + _epggrab_save(); +} diff --git a/src/epggrab.h b/src/epggrab.h new file mode 100644 index 00000000..ec03f179 --- /dev/null +++ b/src/epggrab.h @@ -0,0 +1,70 @@ +#ifndef __EPGGRAB_H__ +#define __EPGGRAB_H__ + +#include +#include "cron.h" + +/* ************************************************************************** + * Type definitions + * *************************************************************************/ + +/* + * Grabber base class + */ +typedef struct epggrab_module +{ + const char* (*name) ( void ); + void (*enable) ( void ); + void (*disable) ( void ); + void (*run ) ( const char *opts ); +} epggrab_module_t; + +/* + * Schedule specification + */ +typedef struct epggrab_sched +{ + LIST_ENTRY(epggrab_sched) es_link; + cron_t cron; ///< Cron definition + epggrab_module_t *mod; ///< Module + char *opts; ///< Extra (advanced) options +} epggrab_sched_t; + +/* + * Schedule list + */ +LIST_HEAD(epggrab_sched_list, epggrab_sched); +typedef struct epggrab_sched_list epggrab_sched_list_t; + +/* ************************************************************************** + * Variables + * *************************************************************************/ + +/* + * Lock this if accessing configuration + */ +pthread_mutex_t epggrab_mutex; + +/* ************************************************************************** + * Functions + * *************************************************************************/ + +/* + * Setup + */ +void epggrab_init ( void ); + +/* + * Access the list of supported modules + */ +//epggrab_module_t** epggrab_module_list ( void ); +epggrab_module_t* epggrab_module_find_by_name ( const char *name ); + +/* + * Set configuration + */ +void epggrab_set_simple ( uint32_t interval, epggrab_module_t* mod ); +void epggrab_set_advanced ( uint32_t count, epggrab_sched_t* sched ); +void epggrab_set_eit ( uint32_t eit ); + +#endif /* __EPGGRAB_H__ */