--- /dev/null
+#include "cron.h"
+#include <time.h>
+
+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;
+}
--- /dev/null
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#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();
+}
--- /dev/null
+#ifndef __EPGGRAB_H__
+#define __EPGGRAB_H__
+
+#include <pthread.h>
+#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__ */