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
This commit is contained in:
parent
3c0cec43e7
commit
08b2398e6d
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=71301
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user