util cron: re-added code I wrote in early EPG days for handling cron tasks

This will be used as a means of managing mux subscription scheduling.
This commit is contained in:
Adam Sutton 2014-04-20 14:12:53 +01:00
parent a07836fc38
commit ae0c2f5334
3 changed files with 398 additions and 0 deletions

View file

@ -114,6 +114,7 @@ SRCS = src/version.c \
src/input.c \
src/http/http_client.c \
src/fsmonitor.c \
src/cron.c \
SRCS += \
src/api.c \

329
src/cron.c Normal file
View file

@ -0,0 +1,329 @@
/*
* Tvheadend - cron routines
*
* Copyright (C) 2014 Adam Sutton
*
* 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/>.
*/
#include "cron.h"
#include <time.h>
#include <stdio.h>
#include <string.h>
/*
* Parse value
*/
static int
cron_parse_val ( const char *str, const char **key, int *v )
{
int i = 0;
if (key) {
while (key[i]) {
if (!strncasecmp(str, key[i], strlen(key[i]))) {
*v = i;
return 0;
}
i++;
}
}
return sscanf(str, "%d", v) == 1 ? 0 : 1;
}
/*
* Parse individual field in cron spec
*/
static int
cron_parse_field
( const char **istr, uint64_t *field, uint64_t mask, int bits, int off,
const char **key )
{
int sn = -1, en = -1, mn = -1;
const char *str = *istr;
const char *beg = str;
uint64_t val = 0;
while ( 1 ) {
if ( *str == '*' ) {
sn = 0;
en = bits - 1;
beg = NULL;
} else if ( *str == ',' || *str == ' ' || *str == '\0' ) {
if (beg)
if (cron_parse_val(beg, key, en == -1 ? (sn == -1 ? &sn : &en) : &mn))
return 1;
if ((sn - off) >= bits || (en - off) >= bits || mn > bits)
return 1;
if (en < 0) en = sn;
if (mn < 0) mn = 1;
while (sn <= en) {
if ( (sn % mn) == 0 )
val |= (0x1LL << (sn - off));
sn++;
}
if (*str != ',') break;
sn = en = mn = -1;
beg = (str + 1);
} else if ( *str == '/' ) {
if (beg)
if (en == -1 || cron_parse_val(beg, key, sn == -1 ? &sn : &en))
return 1;
beg = (str + 1);
} else if ( *str == '-' ) {
if (sn != -1 || cron_parse_val(beg, key, &sn))
return 1;
beg = (str + 1);
}
str++;
}
if (*str == ' ') str++;
*istr = str;
*field = (val | ((val >> bits) & 0x1)) & mask;
return 0;
}
/*
* Set value
*/
int
cron_set ( cron_t *c, const char *str )
{
uint64_t ho, mi, mo, dm, dw;
static const char *days[] = {
"sun", "mon", "tue", "wed", "thu", "fri", "sat"
};
static const char *months[] = {
"ignore",
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec"
};
/* Daily (01:01) */
if ( !strcmp(str, "@daily") ) {
c->c_min = 1;
c->c_hour = 1;
c->c_mday = CRON_MDAY_MASK;
c->c_mon = CRON_MON_MASK;
c->c_wday = CRON_WDAY_MASK;
/* Hourly (XX:02) */
} else if ( !strcmp(str, "@hourly") ) {
c->c_min = 2;
c->c_hour = CRON_HOUR_MASK;
c->c_mday = CRON_MDAY_MASK;
c->c_mon = CRON_MON_MASK;
c->c_wday = CRON_WDAY_MASK;
/* Standard */
} else {
if (cron_parse_field(&str, &mi, CRON_MIN_MASK, 60, 0, NULL) || !mi)
return 1;
if (cron_parse_field(&str, &ho, CRON_HOUR_MASK, 24, 0, NULL) || !ho)
return 1;
if (cron_parse_field(&str, &dm, CRON_MDAY_MASK, 31, 1, NULL) || !dm)
return 1;
if (cron_parse_field(&str, &mo, CRON_MON_MASK, 12, 1, months) || !mo)
return 1;
if (cron_parse_field(&str, &dw, CRON_WDAY_MASK, 7, 0, days) || !dw)
return 1;
c->c_min = mi;
c->c_hour = ho;
c->c_mday = dm;
c->c_mon = mo;
c->c_wday = dw;
}
return 0;
}
/*
* Check for leap year
*/
static int
is_leep_year ( int year )
{
if (!(year % 400))
return 1;
if (!(year % 100))
return 0;
return (year % 4) ? 0 : 1;
}
/*
* Check for days in month
*/
static int
days_in_month ( int year, int mon )
{
int d;
if (mon == 2)
d = 28 + is_leep_year(year);
else
d = 30 + ((0x15AA >> mon) & 0x1);
return d;
}
/*
* Find the next time (starting from now) that the cron should fire
*/
int
cron_next ( cron_t *c, const time_t now, time_t *ret )
{
struct tm nxt;
int endyear;
localtime_r(&now, &nxt);
endyear = nxt.tm_year + 10;
/* Invalid day */
if (!(c->c_mday & (0x1LL << (nxt.tm_mday-1))) ||
!(c->c_wday & (0x1LL << (nxt.tm_wday))) ||
!(c->c_mon & (0x1LL << (nxt.tm_mon))) ) {
nxt.tm_min = 0;
nxt.tm_hour = 0;
/* Invalid hour */
} else if (!(c->c_hour & (0x1LL << nxt.tm_hour))) {
nxt.tm_min = 0;
/* Increment */
} else {
++nxt.tm_min;
}
/* Minute */
while (!(c->c_min & (0x1LL << nxt.tm_min))) {
if (nxt.tm_min == 60) {
++nxt.tm_hour;
nxt.tm_min = 0;
} else
nxt.tm_min++;
}
/* Hour */
while (!(c->c_hour & (0x1LL << nxt.tm_hour))) {
if (nxt.tm_hour == 24) {
++nxt.tm_mday;
++nxt.tm_wday;
nxt.tm_hour = 0;
} else
++nxt.tm_hour;
}
/* Date */
if (nxt.tm_wday == 7)
nxt.tm_wday = 0;
if (nxt.tm_mday == days_in_month(nxt.tm_year+1900, nxt.tm_mon+1)) {
nxt.tm_mday = 1;
nxt.tm_mon++;
if (nxt.tm_mon == 12) {
nxt.tm_mon = 0;
++nxt.tm_year;
}
}
while (!(c->c_mday & (0x1LL << (nxt.tm_mday-1))) ||
!(c->c_wday & (0x1LL << (nxt.tm_wday))) ||
!(c->c_mon & (0x1LL << (nxt.tm_mon))) ) {
/* Stop possible infinite loop on invalid request */
if (nxt.tm_year >= endyear)
return -1;
/* Increment day of week */
if (++nxt.tm_wday == 7)
nxt.tm_wday = 0;
/* Increment day */
if (++nxt.tm_mday > days_in_month(nxt.tm_year+1900, nxt.tm_mon+1)) {
nxt.tm_mday = 1;
if (++nxt.tm_mon == 12) {
nxt.tm_mon = 0;
++nxt.tm_year;
}
}
/* Shortcut the month */
while (!(c->c_mon & (0x1LL << nxt.tm_mon))) {
nxt.tm_wday
+= 1 + (days_in_month(nxt.tm_year+1900, nxt.tm_mon+1) - nxt.tm_mday);
nxt.tm_mday = 1;
if (++nxt.tm_mon >= 12) {
nxt.tm_mon = 0;
++nxt.tm_year;
}
}
nxt.tm_wday %= 7;
}
/* Create time */
// TODO: not sure this will provide the correct time with respect to DST!
nxt.tm_isdst = 0;
*ret = mktime(&nxt);
return 0;
}
/*
* Testing
*/
#if 0
static
void print_bits ( uint64_t b, int n )
{
while (n) {
printf("%d", (int)(b & 0x1));
b >>= 1;
n--;
}
}
int
main ( int argc, char **argv )
{
cron_t c;
time_t n;
struct tm tm;
char buf[128];
time(&n);
if (cron_set(&c, argv[1]))
printf("INVALID CRON: %s\n", argv[1]);
else {
printf("min = "); print_bits(c.c_min, 60); printf("\n");
printf("hour = "); print_bits(c.c_hour, 24); printf("\n");
printf("mday = "); print_bits(c.c_mday, 31); printf("\n");
printf("mon = "); print_bits(c.c_mon, 12); printf("\n");
printf("wday = "); print_bits(c.c_wday, 7); printf("\n");
localtime_r(&n, &tm);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M", &tm);
printf("NOW: %s\n", buf);
if (cron_next(&c, n, &n)) {
printf("FAILED to find NEXT\n");
return 1;
}
localtime_r(&n, &tm);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M", &tm);
printf("NXT: %s\n", buf);
}
return 0;
}
#endif
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/

68
src/cron.h Normal file
View file

@ -0,0 +1,68 @@
/*
* Tvheadend - cron routines
*
* Copyright (C) 2014 Adam Sutton
*
* 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/>.
*/
#ifndef __TVH_CRON_H__
#define __TVH_CRON_H__
#include <stdint.h>
#include <sys/types.h>
#define CRON_MIN_MASK (0x0FFFFFFFFFFFFFFFLL) // 60 bits
#define CRON_HOUR_MASK (0x00FFFFFF) // 24 bits
#define CRON_MDAY_MASK (0x7FFFFFFF) // 31 bits
#define CRON_MON_MASK (0x0FFF) // 12 bits
#define CRON_WDAY_MASK (0x7F) // 7 bits
typedef struct cron
{
uint64_t c_min; ///< Minute mask
uint32_t c_hour; ///< Hour mask
uint32_t c_mday; ///< Day of the Month mask
uint16_t c_mon; ///< Month mask
uint8_t c_wday; ///< Day of the Week mask
} cron_t;
/**
* Initialise from a string
*
* @param c The cron instance to update
* @param str String representation of the cron
*
* @return 0 if OK, 1 if failed to parse
*/
int cron_set ( cron_t *c, 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_next ( cron_t *c, const time_t cur, time_t *nxt );
#endif /* __TVH_CRON_H__ */
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/