diff options
author | babkin <babkin@FreeBSD.org> | 2001-01-20 21:28:16 +0000 |
---|---|---|
committer | babkin <babkin@FreeBSD.org> | 2001-01-20 21:28:16 +0000 |
commit | 6511a9a1a9a24226685ebf775cbf06fadba4d1fa (patch) | |
tree | f44d0a6e642b7c6065fc45611d4d32ba20ba8a44 /usr.sbin | |
parent | fca3b93cb33c66afb73b14b76d9e4ba823846eff (diff) | |
download | FreeBSD-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')
-rw-r--r-- | usr.sbin/cron/cron/cron.8 | 27 | ||||
-rw-r--r-- | usr.sbin/cron/cron/cron.c | 133 | ||||
-rw-r--r-- | usr.sbin/cron/cron/cron.h | 3 |
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 |