diff options
-rw-r--r-- | usr.sbin/cron/cron/cron.c | 148 | ||||
-rw-r--r-- | usr.sbin/cron/cron/cron.h | 6 | ||||
-rw-r--r-- | usr.sbin/cron/crontab/crontab.5 | 2 | ||||
-rw-r--r-- | usr.sbin/cron/lib/entry.c | 23 |
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); |