Author: glebius
Date: Thu Jun 7 22:38:40 2018
New Revision: 334817
URL: https://svnweb.freebsd.org/changeset/base/334817
Log:
Add new functionality and syntax to cron(1) to allow to run jobs at a
given interval, which is counted in seconds since exit of the previous
invocation of the job. Example user crontab entry:
@25 sleep 10
The example will launch 'sleep 10' every 35 seconds. This is a rather
useless example above, but clearly explains the functionality.
The practical goal here is to avoid overlap of previous job invocation
to a new one, or to avoid too short interval(s) for jobs that last long
and doesn't have any point of immediate launch soon after previous run.
Another useful effect of interval jobs can be noticed when a cluster of
machines periodically communicates with a single node. Running the task
time based creates too much load on the node. Running interval based
spreads invocations across machines in cluster. Note that -j/-J won't
help in this case.
Sponsored by: Netflix
Modified:
head/usr.sbin/cron/cron/cron.c
head/usr.sbin/cron/cron/cron.h
head/usr.sbin/cron/cron/do_command.c
head/usr.sbin/cron/crontab/crontab.5
head/usr.sbin/cron/lib/entry.c
Modified: head/usr.sbin/cron/cron/cron.c
==
--- head/usr.sbin/cron/cron/cron.c Thu Jun 7 21:24:21 2018
(r334816)
+++ head/usr.sbin/cron/cron/cron.c Thu Jun 7 22:38:40 2018
(r334817)
@@ -46,7 +46,9 @@ staticvoidusage(void),
parse_args(int c, char *v[]);
static int run_at_secres(cron_db *);
+static voidfind_interval_entry(pid_t);
+static cron_db database;
static time_t last_time = 0;
static int dst_enabled = 0;
static int dont_daemonize = 0;
@@ -100,7 +102,6 @@ main(argc, argv)
int argc;
char*argv[];
{
- cron_db database;
int runnum;
int secres1, secres2;
struct tm *tm;
@@ -154,8 +155,8 @@ main(argc, argv)
database.mtime = (time_t) 0;
load_database();
secres1 = secres2 = run_at_secres();
- run_reboot_jobs();
cron_sync(secres1);
+ run_reboot_jobs();
runnum = 0;
while (TRUE) {
# if DEBUGGING
@@ -210,6 +211,9 @@ run_reboot_jobs(db)
if (e->flags & WHEN_REBOOT) {
job_add(e, u);
}
+ if (e->flags & INTERVAL) {
+ e->lastexit = TargetTime;
+ }
}
}
(void) job_runqueue();
@@ -313,6 +317,13 @@ cron_tick(cron_db *db, int secres)
env_get("LOGNAME", e->envp),
e->uid, e->gid, e->cmd))
+ if (e->flags & INTERVAL) {
+ if (e->lastexit > 0 &&
+ TargetTime >= e->lastexit + e->interval)
+ job_add(e, u);
+ continue;
+ }
+
if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
if (bit_test(e->second, otzsecond)
&& bit_test(e->minute, otzminute)
@@ -489,6 +500,7 @@ sigchld_handler(int x)
("[%d] sigchld...no dead kids\n", getpid()))
return;
default:
+ find_interval_entry(pid);
Debug(DPROC,
("[%d] sigchld...pid #%d died, stat=%d\n",
getpid(), pid, WEXITSTATUS(waiter)))
@@ -557,9 +569,26 @@ run_at_secres(cron_db *db)
for (u = db->head; u != NULL; u = u->next) {
for (e = u->crontab; e != NULL; e = e->next) {
- if ((e->flags & SEC_RES) != 0)
+ if ((e->flags & (SEC_RES | INTERVAL)) != 0)
return 1;
}
}
return 0;
+}
+
+static void
+find_interval_entry(pid_t pid)
+{
+ user *u;
+ entry *e;
+
+ for (u = database.head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ if ((e->flags & INTERVAL) && e->child == pid) {
+ e->lastexit = time(NULL);
+ e->child = 0;
+ break;
+ }
+ }
+ }
}
Modified: head/usr.sbin/cron/cron/cron.h
==
--- head/usr.sbin/cron/cron/cron.h Thu Jun 7 21:24:21 2018
(r334816)
+++ head/usr.sbin/cron/cron/cron.h Thu Jun 7 22:38:40 2018
(r334817)
@@ -168,19 +168,29 @@ typedef struct _entry