summaryrefslogtreecommitdiffstats
path: root/usr.sbin/cron
diff options
context:
space:
mode:
authorbabkin <babkin@FreeBSD.org>2001-01-20 21:28:16 +0000
committerbabkin <babkin@FreeBSD.org>2001-01-20 21:28:16 +0000
commit6511a9a1a9a24226685ebf775cbf06fadba4d1fa (patch)
treef44d0a6e642b7c6065fc45611d4d32ba20ba8a44 /usr.sbin/cron
parentfca3b93cb33c66afb73b14b76d9e4ba823846eff (diff)
downloadFreeBSD-src-6511a9a1a9a24226685ebf775cbf06fadba4d1fa.zip
FreeBSD-src-6511a9a1a9a24226685ebf775cbf06fadba4d1fa.tar.gz
Added sensible handling of switch to and from daylight saving time
for the jobs that fall into the disappearing or duplicated time interval. PR: bin/24494
Diffstat (limited to 'usr.sbin/cron')
-rw-r--r--usr.sbin/cron/cron/cron.827
-rw-r--r--usr.sbin/cron/cron/cron.c133
-rw-r--r--usr.sbin/cron/cron/cron.h3
3 files changed, 158 insertions, 5 deletions
diff --git a/usr.sbin/cron/cron/cron.8 b/usr.sbin/cron/cron/cron.8
index 12f1a96..45f2aa0 100644
--- a/usr.sbin/cron/cron/cron.8
+++ b/usr.sbin/cron/cron/cron.8
@@ -68,6 +68,33 @@ need not be restarted whenever a crontab file is modified. Note that the
.Xr crontab 1
command updates the modtime of the spool directory whenever it changes a
crontab.
+.Pp
+If the time zone has daylight saving time which differs by one hour from
+the standard time and the switch to and from daylight saving time occurs
+at :00 minutes (as in most of Asia, Europe and North America) then these
+time switches are handled specially by
+.Nm cron .
+The time zones with other variations of daylight saving time (such as with
+30 minutes difference or switched at :30 minutes etc.) do not have such
+support.
+.Pp
+In the supported time zones the jobs run during the switches to and
+from daylinght saving time as
+intuitively expected. If a job falls
+into a time interval that disappears during the switch from
+standard time to daylight saving time or is
+duplicated during the reverse switch, then it's handled
+in one of two ways. The jobs that run every hour work
+as always, they skip the skipped hour or run in the added
+hour as usual. But the jobs that run less frequently
+are executed exactly once, they are not skipped nor
+executed twice (unless cron is restarted or the user's
+.Xr crontab 5
+is changed during such a time interval). If an hour disappears
+during the switch to daylight saving time, such jobs are
+executed during the next hour at the first minute that is specified
+for them in
+.Xr crontab 5 .
.Sh SEE ALSO
.Xr crontab 1 ,
.Xr crontab 5
diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c
index e8f8557..8c4d737 100644
--- a/usr.sbin/cron/cron/cron.c
+++ b/usr.sbin/cron/cron/cron.c
@@ -36,13 +36,15 @@ static void usage __P((void)),
run_reboot_jobs __P((cron_db *)),
cron_tick __P((cron_db *)),
cron_sync __P((void)),
- cron_sleep __P((void)),
+ cron_sleep __P((cron_db *)),
+ cron_clean __P((cron_db *)),
#ifdef USE_SIGCHLD
sigchld_handler __P((int)),
#endif
sighup_handler __P((int)),
parse_args __P((int c, char *v[]));
+static time_t last_time = 0;
static void
usage() {
@@ -66,7 +68,6 @@ main(argc, argv)
char *argv[];
{
cron_db database;
-
ProgramName = argv[0];
#if defined(BSD)
@@ -127,7 +128,7 @@ main(argc, argv)
# if DEBUGGING
/* if (!(DebugFlags & DTEST)) */
# endif /*DEBUGGING*/
- cron_sleep();
+ cron_sleep(&database);
load_database(&database);
@@ -164,6 +165,7 @@ static void
cron_tick(db)
cron_db *db;
{
+ static struct tm lasttm;
register struct tm *tm = localtime(&TargetTime);
register int minute, hour, dom, month, dow;
register user *u;
@@ -180,6 +182,93 @@ cron_tick(db)
Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
getpid(), minute, hour, dom, month, dow))
+ /* check for the daylight saving time change
+ * we support only change by +-1 hour happening at :00 minutes,
+ * those living in more strange timezones are out of luck
+ */
+ if (last_time != 0 && tm->tm_isdst != lasttm.tm_isdst
+ && TargetTime > last_time /* exclude stepping back */) {
+ int prevhr, nexthr, runtime;
+ int lastmin, lasthour;
+ int trandom, tranmonth, trandow;
+ time_t diff; /* time difference in seconds */
+
+ lastmin = lasttm.tm_min -FIRST_MINUTE;
+ lasthour = lasttm.tm_hour -FIRST_HOUR;
+
+ prevhr = (hour + (HOUR_COUNT-1)) % HOUR_COUNT;
+ nexthr = (lasthour + 1) % HOUR_COUNT;
+
+ if ( lasttm.tm_isdst != 1 && tm->tm_isdst == 1 /* ST->DST */
+ && prevhr == nexthr ) {
+ diff = ( (hour*MINUTE_COUNT + minute)
+ - (lasthour*MINUTE_COUNT + lastmin)
+ + HOUR_COUNT*MINUTE_COUNT
+ ) % (HOUR_COUNT*MINUTE_COUNT);
+ diff -= (TargetTime - last_time) / 60/*seconds*/;
+ if (diff != MINUTE_COUNT)
+ goto dstdone;
+
+ if (hour == 0) {
+ trandom = lasttm.tm_mday -FIRST_DOM;
+ tranmonth = lasttm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
+ trandow = lasttm.tm_wday -FIRST_DOW;
+ } else {
+ trandom = dom;
+ tranmonth = month;
+ trandow = dow;
+ }
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ /* adjust only jobs less frequent than 1 hr */
+ if ( !bit_test(e->hour, prevhr)
+ || bit_test(e->hour, lasthour)
+ || bit_test(e->hour, hour) )
+ continue;
+
+ if ( bit_test(e->month, tranmonth)
+ && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
+ ? (bit_test(e->dow,trandow) && bit_test(e->dom,trandom))
+ : (bit_test(e->dow,trandow) || bit_test(e->dom,trandom)) )
+ ) {
+ bit_ffs(e->minute, MINUTE_COUNT, &runtime);
+ if(runtime >= 0) {
+ e->tmval = TargetTime + (runtime-minute)*60;
+ e->flags |= RUN_AT;
+ e->flags &= ~NOT_UNTIL;
+ }
+ }
+ }
+ }
+ } else if ( lasttm.tm_isdst == 1 && tm->tm_isdst != 1 /* DST->ST */
+ && lasthour == hour ) {
+ diff = ( (lasthour*MINUTE_COUNT + lastmin)
+ - (hour*MINUTE_COUNT + minute)
+ + HOUR_COUNT*MINUTE_COUNT
+ ) % (HOUR_COUNT*MINUTE_COUNT);
+ diff += (TargetTime - last_time) / 60/*seconds*/;
+ if (diff != MINUTE_COUNT)
+ goto dstdone;
+
+ runtime = TargetTime + (MINUTE_COUNT - minute)*60;
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ /* adjust only jobs less frequent than 1 hr */
+ if ( !bit_test(e->hour, hour)
+ || bit_test(e->hour, prevhr)
+ || bit_test(e->hour, nexthr) )
+ continue;
+
+ e->tmval = runtime;
+ e->flags |= NOT_UNTIL;
+ e->flags &= ~RUN_AT;
+ }
+ }
+ }
+ }
+dstdone:
+
/* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
* first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
* on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
@@ -191,7 +280,14 @@ cron_tick(db)
Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
env_get("LOGNAME", e->envp),
e->uid, e->gid, e->cmd))
- if (bit_test(e->minute, minute)
+ if (e->flags & NOT_UNTIL) {
+ if (TargetTime >= e->tmval)
+ e->flags &= ~NOT_UNTIL;
+ else
+ continue;
+ }
+ if ( (e->flags & RUN_AT) && TargetTime == e->tmval
+ || bit_test(e->minute, minute)
&& bit_test(e->hour, hour)
&& bit_test(e->month, month)
&& ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
@@ -199,10 +295,14 @@ cron_tick(db)
: (bit_test(e->dow,dow) || bit_test(e->dom,dom))
)
) {
+ e->flags &= ~RUN_AT;
job_add(e, u);
}
}
}
+
+ last_time = TargetTime;
+ lasttm = *tm;
}
@@ -226,7 +326,9 @@ cron_sync() {
static void
-cron_sleep() {
+cron_sleep(db)
+ cron_db *db;
+{
int seconds_to_wait = 0;
/*
@@ -241,6 +343,7 @@ cron_sleep() {
*/
if (seconds_to_wait < -600 || seconds_to_wait > 600) {
+ cron_clean(db);
cron_sync();
continue;
}
@@ -265,6 +368,26 @@ cron_sleep() {
}
+/* if the time was changed abruptly, clear the flags related
+ * to the daylight time switch handling to avoid strange effects
+ */
+
+static void
+cron_clean(db)
+ cron_db *db;
+{
+ user *u;
+ entry *e;
+
+ last_time = 0;
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags &= ~(RUN_AT|NOT_UNTIL);
+ }
+ }
+}
+
#ifdef USE_SIGCHLD
static void
sigchld_handler(x) {
diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h
index 9d7b8d3..a5abf53 100644
--- a/usr.sbin/cron/cron/cron.h
+++ b/usr.sbin/cron/cron/cron.h
@@ -172,6 +172,9 @@ typedef struct _entry {
#define DOM_STAR 0x01
#define DOW_STAR 0x02
#define WHEN_REBOOT 0x04
+#define RUN_AT 0x08
+#define NOT_UNTIL 0x10
+ time_t tmval;
} entry;
/* the crontab database will be a list of the
OpenPOWER on IntegriCloud