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:
parent
a07836fc38
commit
ae0c2f5334
3 changed files with 398 additions and 0 deletions
1
Makefile
1
Makefile
|
@ -114,6 +114,7 @@ SRCS = src/version.c \
|
||||||
src/input.c \
|
src/input.c \
|
||||||
src/http/http_client.c \
|
src/http/http_client.c \
|
||||||
src/fsmonitor.c \
|
src/fsmonitor.c \
|
||||||
|
src/cron.c \
|
||||||
|
|
||||||
SRCS += \
|
SRCS += \
|
||||||
src/api.c \
|
src/api.c \
|
||||||
|
|
329
src/cron.c
Normal file
329
src/cron.c
Normal 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
68
src/cron.h
Normal 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
|
||||||
|
*****************************************************************************/
|
Loading…
Add table
Reference in a new issue