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:
parent
4664d7c8cc
commit
ec83952d16
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user