summaryrefslogtreecommitdiffstats
path: root/usr.sbin/cron
diff options
context:
space:
mode:
authorbabkin <babkin@FreeBSD.org>2001-03-09 03:14:09 +0000
committerbabkin <babkin@FreeBSD.org>2001-03-09 03:14:09 +0000
commitec83952d168152469a2f0920e5f894a4ebdb92e3 (patch)
treefb3882c3481fe894901935a8359fc022dbd04862 /usr.sbin/cron
parent4664d7c8ccd74042df38536c46dd83824aefe766 (diff)
downloadFreeBSD-src-ec83952d168152469a2f0920e5f894a4ebdb92e3.zip
FreeBSD-src-ec83952d168152469a2f0920e5f894a4ebdb92e3.tar.gz
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.
Diffstat (limited to 'usr.sbin/cron')
-rw-r--r--usr.sbin/cron/cron/cron.851
-rw-r--r--usr.sbin/cron/cron/cron.c133
-rw-r--r--usr.sbin/cron/cron/cron.h3
3 files changed, 182 insertions, 5 deletions
diff --git a/usr.sbin/cron/cron/cron.8 b/usr.sbin/cron/cron/cron.8
index ec66af4..02d3aaf 100644
--- a/usr.sbin/cron/cron/cron.8
+++ b/usr.sbin/cron/cron/cron.8
@@ -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
diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c
index 8889b72..5131645 100644
--- a/usr.sbin/cron/cron/cron.c
+++ b/usr.sbin/cron/cron/cron.c
@@ -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)
}
}
}
+
diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h
index 9d7b8d3..33bdf92 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 lastrun;
} entry;
/* the crontab database will be a list of the
OpenPOWER on IntegriCloud