]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
util cron: re-added code I wrote in early EPG days for handling cron tasks
authorAdam Sutton <dev@adamsutton.me.uk>
Sun, 20 Apr 2014 13:12:53 +0000 (14:12 +0100)
committerAdam Sutton <dev@adamsutton.me.uk>
Sun, 20 Apr 2014 13:12:53 +0000 (14:12 +0100)
This will be used as a means of managing mux subscription scheduling.

Makefile
src/cron.c [new file with mode: 0644]
src/cron.h [new file with mode: 0644]

index 20e8ff5864c676d0db14f59f2d7c44ddccbf720f..2f1d995e210219b7d8557e0325bdae11651bf071 100644 (file)
--- a/Makefile
+++ b/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 \
diff --git a/src/cron.c b/src/cron.c
new file mode 100644 (file)
index 0000000..0dbcbbf
--- /dev/null
@@ -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
+ *****************************************************************************/
diff --git a/src/cron.h b/src/cron.h
new file mode 100644 (file)
index 0000000..155afa1
--- /dev/null
@@ -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
+ *****************************************************************************/