From 80209a4aff0b1d799b026fdd19d6d915617ad0be Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 29 Jun 2014 22:26:52 +0200 Subject: [PATCH] cron, internal epggrab: add multi-line cron support for internal epggrab --- src/cron.c | 66 ++++++++++++++++++++++++++++- src/cron.h | 26 ++++++++++++ src/epggrab.c | 66 +++++++++++++++++++++++------ src/epggrab.h | 8 +--- src/webui/extjs.c | 6 +-- src/webui/static/app/epggrab.js | 75 +++++---------------------------- 6 files changed, 160 insertions(+), 87 deletions(-) diff --git a/src/cron.c b/src/cron.c index 6e9de261..4290664e 100644 --- a/src/cron.c +++ b/src/cron.c @@ -21,7 +21,9 @@ #include #include +#include #include +#include /* * Parse value @@ -148,6 +150,43 @@ cron_set ( cron_t *c, const char *str ) return 0; } +/* + * Set value + */ +cron_multi_t * +cron_multi_set ( const char *str ) +{ + char *s = str ? alloca(strlen(str) + 1) : NULL; + char *line, *sptr; + cron_t cron; + cron_multi_t *cm = NULL, *cm2; + int count = 0; + + if (s == NULL) + return NULL; + strcpy(s, str); + line = strtok_r(s, "\n", &sptr); + while (line) { + while (*line && *line <= ' ') + line++; + if (line[0] != '#') + if (!cron_set(&cron, line)) { + count++; + cm2 = realloc(cm, sizeof(cm) + sizeof(cron) * count); + if (cm2 == NULL) { + free(cm); + return NULL; + } + cm = cm2; + cm->cm_crons[count - 1] = cron; + } + line = strtok_r(NULL, "\n", &sptr); + } + if (count) + cm->cm_count = count; + return cm; +} + /* * Check for leap year */ @@ -182,7 +221,7 @@ int cron_next ( cron_t *c, const time_t now, time_t *ret ) { struct tm nxt, tmp; - int endyear; + int endyear, loops = 1000; localtime_r(&now, &nxt); endyear = nxt.tm_year + 10; @@ -239,6 +278,10 @@ cron_next ( cron_t *c, const time_t now, time_t *ret ) !(c->c_wday & (0x1LL << (nxt.tm_wday))) || !(c->c_mon & (0x1LL << (nxt.tm_mon))) ) { + /* Endless loop protection */ + if (loops-- == 0) + return -1; + /* Stop possible infinite loop on invalid request */ if (nxt.tm_year >= endyear) return -1; @@ -277,6 +320,27 @@ cron_next ( cron_t *c, const time_t now, time_t *ret ) return 0; } +/* + * Find the next time (starting from now) that the cron should fire + */ +int +cron_multi_next ( cron_multi_t *cm, const time_t now, time_t *ret ) +{ + uint32_t i; + time_t r = (time_t)-1, t; + + if (cm == NULL) + return -1; + for (i = 0; i < cm->cm_count; i++) + if (!cron_next(&cm->cm_crons[i], now, &t)) + if (r == (time_t)-1 || t < r) + r = t; + if (r == (time_t)-1) + return -1; + *ret = r; + return 0; +} + /* * Testing */ diff --git a/src/cron.h b/src/cron.h index 155afa17..81a8cd05 100644 --- a/src/cron.h +++ b/src/cron.h @@ -38,6 +38,12 @@ typedef struct cron uint8_t c_wday; ///< Day of the Week mask } cron_t; +typedef struct cron_multi +{ + uint32_t cm_count; ///< Count of multiple crons + cron_t cm_crons[0]; ///< Allocated cron structures +} cron_multi_t; + /** * Initialise from a string * @@ -59,6 +65,26 @@ int cron_set ( cron_t *c, const char *str ); */ int cron_next ( cron_t *c, const time_t cur, time_t *nxt ); +/** + * Initialise from a string + * + * @param str String representation of the mutiple cron entries ('\n' delimiter) + * + * @return cron_multi_t pointer if OK, NULL if failed to parse + */ +cron_multi_t *cron_multi_set ( const char *str ); + +/** + * Determine the next time a cron will run (from cur) + * + * @param c The cron to check + * @param now The current time + * @param nxt The next time to execute + * + * @return 0 if next time was found + */ +int cron_multi_next ( cron_multi_t *cm, const time_t cur, time_t *nxt ); + #endif /* __TVH_CRON_H__ */ /****************************************************************************** diff --git a/src/epggrab.c b/src/epggrab.c index 60b1d7f6..62c77bc4 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -36,6 +36,7 @@ #include "htsmsg_xml.h" #include "file.h" #include "service.h" +#include "cron.h" /* Thread protection */ static int epggrab_confver; @@ -44,7 +45,7 @@ static pthread_cond_t epggrab_cond; int epggrab_running; /* Config */ -uint32_t epggrab_interval; +char *epggrab_cron; epggrab_module_int_t* epggrab_module; epggrab_module_list_t epggrab_modules; uint32_t epggrab_channel_rename; @@ -54,6 +55,8 @@ uint32_t epggrab_epgdb_periodicsave; gtimer_t epggrab_save_timer; +static cron_multi_t *epggrab_cron_multi; + /* ************************************************************************** * Internal Grab Thread * *************************************************************************/ @@ -88,6 +91,7 @@ static void* _epggrab_internal_thread ( void* p ) int err, confver = -1; // force first run struct timespec ts; epggrab_module_int_t *mod; + time_t t; /* Setup timeout */ ts.tv_nsec = 0; @@ -107,7 +111,10 @@ static void* _epggrab_internal_thread ( void* p ) } confver = epggrab_confver; mod = epggrab_module; - ts.tv_sec += epggrab_interval; + if (!cron_multi_next(epggrab_cron_multi, time(NULL), &t)) + ts.tv_sec = t; + else + ts.tv_sec += 60; pthread_mutex_unlock(&epggrab_mutex); if ( !epggrab_running) @@ -129,8 +136,9 @@ static void _epggrab_load ( void ) epggrab_module_t *mod; htsmsg_field_t *f; htsmsg_t *m, *a; - uint32_t enabled = 1; + uint32_t enabled = 1, interval; const char *str; + char buf[32]; int old = 0; /* Load settings */ @@ -149,10 +157,33 @@ static void _epggrab_load ( void ) if (epggrab_epgdb_periodicsave) gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL, epggrab_epgdb_periodicsave); - if (!htsmsg_get_u32(m, old ? "grab-interval" : "interval", - &epggrab_interval)) { - if (old) epggrab_interval *= 3600; + if ((str = htsmsg_get_str(m, "cron")) == NULL) { + if (!htsmsg_get_u32(m, old ? "grab-interval" : "interval", + &interval)) { + if (old) interval *= 3600; + str = buf; + if (interval <= 600) + strcpy(buf, "*/10 * * * *"); + else if (interval <= 900) + strcpy(buf, "*/15 * * * *"); + else if (interval <= 1200) + strcpy(buf, "*/30 * * * *"); + else if (interval <= 3600) + strcpy(buf, "4 * * * *"); + else if (interval <= 7200) + strcpy(buf, "4 */2 * * *"); + else if (interval <= 14400) + strcpy(buf, "4 */4 * * *"); + else if (interval <= 28800) + strcpy(buf, "4 */8 * * *"); + else if (interval <= 43200) + strcpy(buf, "4 */12 * * *"); + else + strcpy(buf, "4 0 * * *"); + } else + strcpy(buf, "4 */12 * * *"); } + epggrab_set_cron(str); htsmsg_get_u32(m, "grab-enabled", &enabled); if (enabled) { if ( (str = htsmsg_get_str(m, old ? "current-grabber" : "module")) ) { @@ -215,7 +246,8 @@ static void _epggrab_load ( void ) /* Defaults */ } else { - epggrab_interval = 12 * 3600; // hours + free(epggrab_cron); + epggrab_cron = strdup("4 */12 * * *"); // each 12 hours (noon) epggrab_module = NULL; // disabled LIST_FOREACH(mod, &epggrab_modules, link) // enable all OTA by default if (mod->type == EPGGRAB_OTA) @@ -244,9 +276,8 @@ void epggrab_save ( void ) m = htsmsg_create_map(); htsmsg_add_u32(m, "channel_rename", epggrab_channel_rename); htsmsg_add_u32(m, "channel_renumber", epggrab_channel_renumber); - htsmsg_add_u32(m, "channel_reicon", epggrab_channel_reicon); htsmsg_add_u32(m, "epgdb_periodicsave", epggrab_epgdb_periodicsave); - htsmsg_add_u32(m, "interval", epggrab_interval); + htsmsg_add_str(m, "cron", epggrab_cron); if ( epggrab_module ) htsmsg_add_str(m, "module", epggrab_module->id); a = NULL; @@ -261,12 +292,15 @@ void epggrab_save ( void ) htsmsg_destroy(m); } -int epggrab_set_interval ( uint32_t interval ) +int epggrab_set_cron ( const char *cron ) { int save = 0; - if ( epggrab_interval != interval ) { + if ( epggrab_cron == NULL || strcmp(epggrab_cron, cron) ) { save = 1; - epggrab_interval = interval; + free(epggrab_cron); + epggrab_cron = strdup(cron); + free(epggrab_cron_multi); + epggrab_cron_multi = cron_multi_set(cron); } return save; } @@ -377,13 +411,15 @@ pthread_t epggrab_tid; void epggrab_init ( void ) { /* Defaults */ - epggrab_interval = 0; + epggrab_cron = NULL; epggrab_module = NULL; epggrab_channel_rename = 0; epggrab_channel_renumber = 0; epggrab_channel_reicon = 0; epggrab_epgdb_periodicsave = 0; + epggrab_cron_multi = NULL; + pthread_mutex_init(&epggrab_mutex, NULL); pthread_cond_init(&epggrab_cond, NULL); @@ -432,4 +468,8 @@ void epggrab_done ( void ) epggrab_ota_shutdown(); eit_done(); opentv_done(); + free(epggrab_cron); + epggrab_cron = NULL; + free(epggrab_cron_multi); + epggrab_cron_multi = NULL; } diff --git a/src/epggrab.h b/src/epggrab.h index 64776668..091c0008 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -199,8 +199,6 @@ struct epggrab_ota_mux int om_complete; ///< Has completed a scan int om_active; - int om_timeout; ///< User configurable - int om_interval; time_t om_when; ///< Next event time LIST_ENTRY(epggrab_ota_mux) om_q_link; @@ -214,8 +212,6 @@ struct epggrab_ota_map { LIST_ENTRY(epggrab_ota_map) om_link; epggrab_module_ota_t *om_module; - int om_timeout; - int om_interval; int om_complete; int om_first; uint64_t om_tune_count; @@ -254,7 +250,7 @@ htsmsg_t* epggrab_module_list ( void ); extern epggrab_module_list_t epggrab_modules; extern pthread_mutex_t epggrab_mutex; extern int epggrab_running; -extern uint32_t epggrab_interval; +extern char *epggrab_cron; extern epggrab_module_int_t* epggrab_module; extern uint32_t epggrab_channel_rename; extern uint32_t epggrab_channel_renumber; @@ -264,7 +260,7 @@ extern uint32_t epggrab_epgdb_periodicsave; /* * Set configuration */ -int epggrab_set_interval ( uint32_t interval ); +int epggrab_set_cron ( const char *cron ); int epggrab_set_module ( epggrab_module_t *mod ); int epggrab_set_module_by_id ( const char *id ); int epggrab_set_channel_rename ( uint32_t e ); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 4aba5a2e..4055b6b9 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -432,7 +432,7 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) r = htsmsg_create_map(); if (epggrab_module) htsmsg_add_str(r, "module", epggrab_module->id); - htsmsg_add_u32(r, "interval", epggrab_interval); + htsmsg_add_str(r, "cron", epggrab_cron); htsmsg_add_u32(r, "channel_rename", epggrab_channel_rename); htsmsg_add_u32(r, "channel_renumber", epggrab_channel_renumber); htsmsg_add_u32(r, "channel_reicon", epggrab_channel_reicon); @@ -461,8 +461,8 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) save |= epggrab_set_channel_reicon(str ? 1 : 0); if ( (str = http_arg_get(&hc->hc_req_args, "epgdb_periodicsave")) ) save |= epggrab_set_periodicsave(atoi(str) * 3600); - if ( (str = http_arg_get(&hc->hc_req_args, "interval")) ) - save |= epggrab_set_interval(atoi(str)); + if ( (str = http_arg_get(&hc->hc_req_args, "cron")) ) + save |= epggrab_set_cron(str); if ( (str = http_arg_get(&hc->hc_req_args, "module")) ) save |= epggrab_set_module_by_id(str); if ( (str = http_arg_get(&hc->hc_req_args, "external")) ) { diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index 196e5b2d..a6e232f7 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -82,13 +82,22 @@ tvheadend.epggrab = function() { var confreader = new Ext.data.JsonReader({ root: 'epggrabSettings' - }, ['module', 'interval', 'channel_rename', 'channel_renumber', + }, ['module', 'cron', 'channel_rename', 'channel_renumber', 'channel_reicon', 'epgdb_periodicsave']); /* **************************************************************** * Basic Fields * ***************************************************************/ + /* + * Cron setup + */ + var internalCron = new Ext.form.TextArea({ + fieldLabel: 'Cron multi-line', + name: 'cron', + width: 300, + }); + /* * Module selector */ @@ -105,68 +114,6 @@ tvheadend.epggrab = function() { store: internalModuleStore }); - /* - * Interval selector - */ - var intervalUnits = [[86400, 'Days'], [3600, 'Hours'], - [60, 'Minutes'], [1, 'Seconds']]; - var intervalValue = new Ext.form.NumberField({ - width: 300, - allowNegative: false, - allowDecimals: false, - minValue: 1, - maxValue: 7, - value: 1, - fieldLabel: 'Grab interval', - name: 'intervalValue', - listeners: { - 'valid': function(e) { - v = e.getValue() * intervalUnit.getValue(); - interval.setValue(v); - } - } - }); - var intervalUnit = new Ext.form.ComboBox({ - name: 'intervalUnit', - width: 300, - valueField: 'key', - displayField: 'value', - value: 86400, - forceSelection: true, - editable: false, - triggerAction: 'all', - mode: 'local', - store: new Ext.data.SimpleStore({ - fields: ['key', 'value'], - data: intervalUnits - }), - listeners: { - 'change': function(e, n, o) { - intervalValue.maxValue = (7 * 86400) / n; - intervalValue.validate(); - } - } - }); - var interval = new Ext.form.Hidden({ - name: 'interval', - value: 86400, - listeners: { - 'enable': function(e) { - v = e.getValue(); - for (i = 0; i < intervalUnits.length; i++) { - u = intervalUnits[i][0]; - if ((v % u) === 0) { - intervalUnit.setValue(u); - intervalValue.maxValue = (7 * 86400) / u; - intervalValue.setValue(v / u); - intervalValue.validate(); - break; - } - } - } - } - }); - /* * Channel handling */ @@ -215,7 +162,7 @@ tvheadend.epggrab = function() { width: 700, autoHeight: true, collapsible: true, - items: [interval, internalModule, intervalValue, intervalUnit] + items: [internalCron, internalModule] }); /* ****************************************************************