summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsobomax <sobomax@FreeBSD.org>2012-10-25 22:54:29 +0000
committersobomax <sobomax@FreeBSD.org>2012-10-25 22:54:29 +0000
commit83b5dc03eb3b4233e7a15da88f6ed00abcb3575f (patch)
treef7f1144f9076b5009f873da433df591ef9374365
parent1206789da9bb83473f8fd9d0feda62d7eebbeb69 (diff)
downloadFreeBSD-src-83b5dc03eb3b4233e7a15da88f6ed00abcb3575f.zip
FreeBSD-src-83b5dc03eb3b4233e7a15da88f6ed00abcb3575f.tar.gz
Second attempt to add @every_second keyword support. Due to multiple
requests, default to the previous 60-seconds scheduling method unless there is any @every_second entries to conserve CPU cycles and power. This change also improves scheduling in the default mode by running as close to the beginning of the minnute as possible by replacing sleep(3) with nanosleep(2). Previously, the tasks would run anywhere within the first second of the minute and that offset drifted back and forth each time cron(8) was engaged. MFC after: 1 month
-rw-r--r--usr.sbin/cron/cron/cron.c148
-rw-r--r--usr.sbin/cron/cron/cron.h6
-rw-r--r--usr.sbin/cron/crontab/crontab.52
-rw-r--r--usr.sbin/cron/lib/entry.c23
4 files changed, 149 insertions, 30 deletions
diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c
index 8524ff9..d374319 100644
--- a/usr.sbin/cron/cron/cron.c
+++ b/usr.sbin/cron/cron/cron.c
@@ -35,9 +35,9 @@ static const char rcsid[] =
static void usage(void),
run_reboot_jobs(cron_db *),
- cron_tick(cron_db *),
- cron_sync(void),
- cron_sleep(cron_db *),
+ cron_tick(cron_db *, int),
+ cron_sync(int),
+ cron_sleep(cron_db *, int),
cron_clean(cron_db *),
#ifdef USE_SIGCHLD
sigchld_handler(int),
@@ -45,6 +45,8 @@ static void usage(void),
sighup_handler(int),
parse_args(int c, char *v[]);
+static int run_at_secres(cron_db *);
+
static time_t last_time = 0;
static int dst_enabled = 0;
struct pidfh *pfh;
@@ -98,6 +100,9 @@ main(argc, argv)
char *argv[];
{
cron_db database;
+ int runnum;
+ int secres1, secres2;
+ struct tm *tm;
ProgramName = argv[0];
@@ -147,23 +152,47 @@ main(argc, argv)
database.tail = NULL;
database.mtime = (time_t) 0;
load_database(&database);
+ secres1 = secres2 = run_at_secres(&database);
run_reboot_jobs(&database);
- cron_sync();
+ cron_sync(secres1);
+ runnum = 0;
while (TRUE) {
# if DEBUGGING
/* if (!(DebugFlags & DTEST)) */
# endif /*DEBUGGING*/
- cron_sleep(&database);
-
- load_database(&database);
+ cron_sleep(&database, secres1);
+
+ if (secres1 == 0 || runnum % 60 == 0) {
+ load_database(&database);
+ secres2 = run_at_secres(&database);
+ if (secres2 != secres1) {
+ secres1 = secres2;
+ if (secres1 != 0) {
+ runnum = 0;
+ } else {
+ /*
+ * Going from 1 sec to 60 sec res. If we
+ * are already at minute's boundary, so
+ * let it run, otherwise schedule for the
+ * next minute.
+ */
+ tm = localtime(&TargetTime);
+ if (tm->tm_sec > 0) {
+ cron_sync(secres2);
+ continue;
+ }
+ }
+ }
+ }
/* do this iteration
*/
- cron_tick(&database);
+ cron_tick(&database, secres1);
- /* sleep 1 minute
+ /* sleep 1 or 60 seconds
*/
- TargetTime += 60;
+ TargetTime += (secres1 != 0) ? 1 : 60;
+ runnum += 1;
}
}
@@ -187,29 +216,29 @@ run_reboot_jobs(db)
static void
-cron_tick(db)
- cron_db *db;
+cron_tick(cron_db *db, int secres)
{
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;
+ int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow;
register struct tm *tm = localtime(&TargetTime);
- register int minute, hour, dom, month, dow;
+ register int second, minute, hour, dom, month, dow;
register user *u;
register entry *e;
/* make 0-based values out of these so we can use them as indicies
*/
+ second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND;
minute = tm->tm_min -FIRST_MINUTE;
hour = tm->tm_hour -FIRST_HOUR;
dom = tm->tm_mday -FIRST_DOM;
month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
dow = tm->tm_wday -FIRST_DOW;
- Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
- getpid(), minute, hour, dom, month, dow))
+ Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n",
+ getpid(), second, minute, hour, dom, month, dow))
if (dst_enabled && last_time != 0
&& TargetTime > last_time /* exclude stepping back */
@@ -262,6 +291,7 @@ cron_tick(db)
/* make 0-based values out of these so we can use them as indicies
*/
+ otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND;
otzminute = otztm.tm_min -FIRST_MINUTE;
otzhour = otztm.tm_hour -FIRST_HOUR;
otzdom = otztm.tm_mday -FIRST_DOM;
@@ -283,7 +313,8 @@ cron_tick(db)
e->uid, e->gid, e->cmd))
if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
- if (bit_test(e->minute, otzminute)
+ if (bit_test(e->second, otzsecond)
+ && bit_test(e->minute, otzminute)
&& bit_test(e->hour, otzhour)
&& bit_test(e->month, otzmonth)
&& ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
@@ -302,7 +333,8 @@ cron_tick(db)
continue;
}
- if (bit_test(e->minute, minute)
+ if (bit_test(e->second, second)
+ && bit_test(e->minute, minute)
&& bit_test(e->hour, hour)
&& bit_test(e->month, month)
&& ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
@@ -332,38 +364,74 @@ cron_tick(db)
* that's something sysadmin's know to expect what with crashing computers..
*/
static void
-cron_sync() {
- register struct tm *tm;
+cron_sync(int secres) {
+ struct tm *tm;
TargetTime = time((time_t*)0);
- tm = localtime(&TargetTime);
- TargetTime += (60 - tm->tm_sec);
+ if (secres != 0) {
+ TargetTime += 1;
+ } else {
+ tm = localtime(&TargetTime);
+ TargetTime += (60 - tm->tm_sec);
+ }
}
+static int
+timespec_subtract(struct timespec *result, struct timespec *x,
+ struct timespec *y)
+{
+ time_t nsec;
+
+ /* Perform the carry for the later subtraction by updating y. */
+ if (x->tv_nsec < y->tv_nsec) {
+ nsec = (y->tv_nsec - x->tv_nsec) / 10000000 + 1;
+ y->tv_nsec -= 1000000000 * nsec;
+ y->tv_sec += nsec;
+ }
+ if (x->tv_nsec - y->tv_nsec > 1000000000) {
+ nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
+ y->tv_nsec += 1000000000 * nsec;
+ y->tv_sec -= nsec;
+ }
+
+ /* tv_nsec is certainly positive. */
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_nsec = x->tv_nsec - y->tv_nsec;
+
+ /* Return True if result is negative. */
+ return (x->tv_sec < y->tv_sec);
+}
static void
-cron_sleep(db)
- cron_db *db;
+cron_sleep(cron_db *db, int secres)
{
- int seconds_to_wait = 0;
+ int seconds_to_wait;
+ int rval;
+ struct timespec ctime, ttime, stime, remtime;
/*
* Loop until we reach the top of the next minute, sleep when possible.
*/
for (;;) {
- seconds_to_wait = (int) (TargetTime - time((time_t*)0));
+ clock_gettime(CLOCK_REALTIME, &ctime);
+ ttime.tv_sec = TargetTime;
+ ttime.tv_nsec = 0;
+ timespec_subtract(&stime, &ttime, &ctime);
/*
* If the seconds_to_wait value is insane, jump the cron
*/
- if (seconds_to_wait < -600 || seconds_to_wait > 600) {
+ if (stime.tv_sec < -600 || stime.tv_sec > 600) {
cron_clean(db);
- cron_sync();
+ cron_sync(secres);
continue;
}
+ seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 :
+ stime.tv_sec;
+
Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
getpid(), (long)TargetTime, seconds_to_wait))
@@ -372,13 +440,19 @@ cron_sleep(db)
* to run, break
*/
- if (seconds_to_wait <= 0)
+ if (stime.tv_sec < 0)
break;
if (job_runqueue() == 0) {
Debug(DSCH, ("[%d] sleeping for %d seconds\n",
getpid(), seconds_to_wait))
- sleep(seconds_to_wait);
+ for (;;) {
+ rval = nanosleep(&stime, &remtime);
+ if (rval == 0 || errno != EINTR)
+ break;
+ stime.tv_sec = remtime.tv_sec;
+ stime.tv_nsec = remtime.tv_nsec;
+ }
}
}
}
@@ -484,3 +558,17 @@ parse_args(argc, argv)
}
}
+static int
+run_at_secres(cron_db *db)
+{
+ user *u;
+ entry *e;
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ if ((e->flags & SEC_RES) != 0)
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h
index 38eb33b..1a814df 100644
--- a/usr.sbin/cron/cron/cron.h
+++ b/usr.sbin/cron/cron/cron.h
@@ -124,6 +124,10 @@
LineNumber = ln; \
}
+#define FIRST_SECOND 0
+#define LAST_SECOND 59
+#define SECOND_COUNT (LAST_SECOND - FIRST_SECOND + 1)
+
#define FIRST_MINUTE 0
#define LAST_MINUTE 59
#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1)
@@ -165,6 +169,7 @@ typedef struct _entry {
#endif
char **envp;
char *cmd;
+ bitstr_t bit_decl(second, SECOND_COUNT);
bitstr_t bit_decl(minute, MINUTE_COUNT);
bitstr_t bit_decl(hour, HOUR_COUNT);
bitstr_t bit_decl(dom, DOM_COUNT);
@@ -176,6 +181,7 @@ typedef struct _entry {
#define WHEN_REBOOT 0x04
#define RUN_AT 0x08
#define NOT_UNTIL 0x10
+#define SEC_RES 0x20
time_t lastrun;
} entry;
diff --git a/usr.sbin/cron/crontab/crontab.5 b/usr.sbin/cron/crontab/crontab.5
index 458175e..9be37df 100644
--- a/usr.sbin/cron/crontab/crontab.5
+++ b/usr.sbin/cron/crontab/crontab.5
@@ -232,6 +232,8 @@ string meaning
@daily Run once a day, "0 0 * * *".
@midnight (same as @daily)
@hourly Run once an hour, "0 * * * *".
+@every_minute Run once a minute, "*/1 * * * *".
+@every_second Run once a second.
.Ed
.Sh EXAMPLE CRON FILE
.Bd -literal
diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c
index ea015da..57f7255 100644
--- a/usr.sbin/cron/lib/entry.c
+++ b/usr.sbin/cron/lib/entry.c
@@ -151,6 +151,7 @@ load_entry(file, error_func, pw, envp)
e->flags |= WHEN_REBOOT;
} else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
Debug(DPARS, ("load_entry()...yearly shortcut\n"))
+ bit_set(e->second, 0);
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_set(e->dom, 0);
@@ -159,6 +160,7 @@ load_entry(file, error_func, pw, envp)
e->flags |= DOW_STAR;
} else if (!strcmp("monthly", cmd)) {
Debug(DPARS, ("load_entry()...monthly shortcut\n"))
+ bit_set(e->second, 0);
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_set(e->dom, 0);
@@ -167,6 +169,7 @@ load_entry(file, error_func, pw, envp)
e->flags |= DOW_STAR;
} else if (!strcmp("weekly", cmd)) {
Debug(DPARS, ("load_entry()...weekly shortcut\n"))
+ bit_set(e->second, 0);
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
@@ -175,6 +178,7 @@ load_entry(file, error_func, pw, envp)
bit_set(e->dow, 0);
} else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
Debug(DPARS, ("load_entry()...daily shortcut\n"))
+ bit_set(e->second, 0);
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
@@ -182,11 +186,29 @@ load_entry(file, error_func, pw, envp)
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
} else if (!strcmp("hourly", cmd)) {
Debug(DPARS, ("load_entry()...hourly shortcut\n"))
+ bit_set(e->second, 0);
bit_set(e->minute, 0);
bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else if (!strcmp("every_minute", cmd)) {
+ Debug(DPARS, ("load_entry()...every_minute shortcut\n"))
+ bit_set(e->second, 0);
+ bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1));
+ bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else if (!strcmp("every_second", cmd)) {
+ Debug(DPARS, ("load_entry()...every_second shortcut\n"))
+ e->flags |= SEC_RES;
+ bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1));
+ bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1));
+ bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
} else {
ecode = e_timespec;
goto eof;
@@ -201,6 +223,7 @@ load_entry(file, error_func, pw, envp)
}
} else {
Debug(DPARS, ("load_entry()...about to parse numerics\n"))
+ bit_set(e->second, 0);
ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
PPC_NULL, ch, file);
OpenPOWER on IntegriCloud