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/http/http_client.c \
|
||||
src/fsmonitor.c \
|
||||
src/cron.c \
|
||||
|
||||
SRCS += \
|
||||
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