The new version of the daylight time saving support. This time it works

for any change of the time zone offset from GMT. To enable use the
option -s.
This commit is contained in:
babkin 2001-03-09 03:14:09 +00:00
parent 4664d7c8cc
commit ec83952d16
3 changed files with 182 additions and 5 deletions

View File

@ -26,6 +26,12 @@
.Sh SYNOPSIS
.Nm
.Oo
.Fl s
.Oc
.Oo
.Fl o
.Oc
.Oo
.Fl x
.Ar debugflag Ns Op , Ns Ar ...
.Oc
@ -71,6 +77,51 @@ 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
Available options:
.Bl -tag -width indent
.It Fl s
Enable special handling of situations when the GMT offset of the local
timezone changes, such as the switches between the standard time and
daylight saving time.
.Pp
The jobs run during the GMT offset changes time as
intuitively expected. If a job falls into a time interval that disappears
(for example, during the switch from
standard time) to daylight saving time or is
duplicated (for example, during the reverse switch), then it's handled
in one of two ways:
.Pp
The first case is for the jobs that run every at hour of a time interval
overlapping with the disappearing or duplicated interval.
In other words, if the job had run within one hour before the GMT offset change
(and cron was not restarted nor the
.Xr crontab 5
changed after that)
or would run after the change at the next hour.
They work as always, skip the skipped time or run in the added
time as usual.
.Pp
The second case is for the jobs that run less frequently.
They 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 interval disappears
due to the GMT offset change, such jobs are
executed at the same absolute point of time as they would be in the
old time zone. For example, if exactly one hour disappears, this
point would be during the next hour at the first minute that is
specified for them in
.Xr crontab 5 .
.It Fl o
Disable the special handling of situations when the GMT offset of the local
timezone changes, to be compatible with the old (default) behavior.
If both options
.Fl o
and
.Fl s
are specified, the option specified last wins.
.El
.Sh SEE ALSO
.Xr crontab 1 ,
.Xr crontab 5

View File

@ -36,19 +36,22 @@ 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 int dst_enabled = 0;
static void
usage() {
char **dflags;
fprintf(stderr, "usage: cron [-x debugflag[,...]]\n");
fprintf(stderr, "usage: cron [-s] [-o] [-x debugflag[,...]]\n");
fprintf(stderr, "\ndebugflags: ");
for(dflags = DebugFlagNames; *dflags; dflags++) {
@ -117,7 +120,7 @@ main(argc, argv)
# if DEBUGGING
/* if (!(DebugFlags & DTEST)) */
# endif /*DEBUGGING*/
cron_sleep();
cron_sleep(&database);
load_database(&database);
@ -154,6 +157,11 @@ static void
cron_tick(db)
cron_db *db;
{
static struct tm lasttm;
static time_t diff = 0, /* time difference in seconds from the last offset change */
difflimit = 0; /* end point for the time zone correction */
struct tm otztm; /* time in the old time zone */
int otzminute, otzhour, otzdom, otzmonth, otzdow;
register struct tm *tm = localtime(&TargetTime);
register int minute, hour, dom, month, dow;
register user *u;
@ -170,6 +178,65 @@ cron_tick(db)
Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
getpid(), minute, hour, dom, month, dow))
if (dst_enabled && last_time != 0
&& TargetTime > last_time /* exclude stepping back */
&& tm->tm_gmtoff != lasttm.tm_gmtoff ) {
diff = tm->tm_gmtoff - lasttm.tm_gmtoff;
if ( diff > 0 ) { /* ST->DST */
/* mark jobs for an earlier run */
difflimit = TargetTime + diff;
for (u = db->head; u != NULL; u = u->next) {
for (e = u->crontab; e != NULL; e = e->next) {
e->flags &= ~NOT_UNTIL;
if ( e->lastrun >= TargetTime )
e->lastrun = 0;
/* not include the ends of hourly ranges */
if ( e->lastrun < TargetTime - 3600 )
e->flags |= RUN_AT;
else
e->flags &= ~RUN_AT;
}
}
} else { /* diff < 0 : DST->ST */
/* mark jobs for skipping */
difflimit = TargetTime - diff;
for (u = db->head; u != NULL; u = u->next) {
for (e = u->crontab; e != NULL; e = e->next) {
e->flags |= NOT_UNTIL;
e->flags &= ~RUN_AT;
}
}
}
}
if (diff != 0) {
/* if the time was reset of the end of special zone is reached */
if (last_time == 0 || TargetTime >= difflimit) {
/* disable the TZ switch checks */
diff = 0;
difflimit = 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);
}
}
} else {
/* get the time in the old time zone */
time_t difftime = TargetTime + tm->tm_gmtoff - diff;
gmtime_r(&difftime, &otztm);
/* make 0-based values out of these so we can use them as indicies
*/
otzminute = otztm.tm_min -FIRST_MINUTE;
otzhour = otztm.tm_hour -FIRST_HOUR;
otzdom = otztm.tm_mday -FIRST_DOM;
otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
otzdow = otztm.tm_wday -FIRST_DOW;
}
}
/* 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
@ -181,6 +248,27 @@ 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 ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
if (bit_test(e->minute, otzminute)
&& bit_test(e->hour, otzhour)
&& bit_test(e->month, otzmonth)
&& ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom))
: (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom))
)
) {
if ( e->flags & RUN_AT ) {
e->flags &= ~RUN_AT;
e->lastrun = TargetTime;
job_add(e, u);
continue;
} else
e->flags &= ~NOT_UNTIL;
} else if ( e->flags & NOT_UNTIL )
continue;
}
if (bit_test(e->minute, minute)
&& bit_test(e->hour, hour)
&& bit_test(e->month, month)
@ -189,10 +277,15 @@ cron_tick(db)
: (bit_test(e->dow,dow) || bit_test(e->dom,dom))
)
) {
e->flags &= ~RUN_AT;
e->lastrun = TargetTime;
job_add(e, u);
}
}
}
last_time = TargetTime;
lasttm = *tm;
}
@ -216,7 +309,9 @@ cron_sync() {
static void
cron_sleep() {
cron_sleep(db)
cron_db *db;
{
int seconds_to_wait = 0;
/*
@ -231,6 +326,7 @@ cron_sleep() {
*/
if (seconds_to_wait < -600 || seconds_to_wait > 600) {
cron_clean(db);
cron_sync();
continue;
}
@ -255,6 +351,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) {
@ -299,8 +415,14 @@ parse_args(argc, argv)
{
int argch;
while ((argch = getopt(argc, argv, "x:")) != -1) {
while ((argch = getopt(argc, argv, "osx:")) != -1) {
switch (argch) {
case 'o':
dst_enabled = 0;
break;
case 's':
dst_enabled = 1;
break;
case 'x':
if (!set_debug_flags(optarg))
usage();
@ -310,3 +432,4 @@ parse_args(argc, argv)
}
}
}

View File

@ -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 lastrun;
} entry;
/* the crontab database will be a list of the