cron(8): schedule interval jobs that get loaded during execution

Jobs using the @<second> syntax currently only get executed if they exist
when cron is started. The simplest reproducer of this is:

echo '@20 root echo "Hello!"' >> /etc/cron.d/myjob

myjob will get loaded at the next second==0, but this echo job will not
run until cron restarts. These jobs are normally handled in
run_reboot_jobs(), which sets e->lastexit of INTERVAL jobs to the startup
time so they run 'n' seconds later.

Fix this by special-casing TargetTime > 0 in the database load. Preexisting
jobs will be handled at startup during run_reboot_jobs as normal, but if
we've reloaded a database during runtime we'll hit this case and set
e->lastexit to the current time when we process it. They will then run every
'n' seconds from that point, and a full restart of cron is no longer
required to make these jobs work.

Reported by:	Juraj Lutter (otis_sk.freebsd.org)
Reviewed by:	allanjude, bapt, bjk (earlier version), Juraj Lutter
MFC after:	3 days
Differential Revision:	https://reviews.freebsd.org/D19924
This commit is contained in:
Kyle Evans 2019-04-20 02:54:20 +00:00
parent 93096fecb6
commit bd6174f74c
2 changed files with 24 additions and 5 deletions

View File

@ -259,6 +259,8 @@ process_crontab(uname, fname, tabname, statbuf, new_db, old_db)
struct passwd *pw = NULL;
int crontab_fd = OK - 1;
user *u;
entry *e;
time_t now;
if (strcmp(fname, SYS_NAME) && !(pw = getpwnam(uname))) {
/* file doesn't have a user in passwd file.
@ -307,6 +309,21 @@ process_crontab(uname, fname, tabname, statbuf, new_db, old_db)
u = load_user(crontab_fd, pw, fname);
if (u != NULL) {
u->mtime = statbuf->st_mtime;
/*
* TargetTime == 0 when we're initially populating the database,
* and TargetTime > 0 any time after that (i.e. we're reloading
* cron.d/ files because they've been created/modified). In the
* latter case, we should check for any interval jobs and run
* them 'n' seconds from the time the job was loaded/reloaded.
* Otherwise, they will not be run until cron is restarted.
*/
if (TargetTime != 0) {
now = time(NULL);
for (e = u->crontab; e != NULL; e = e->next) {
if ((e->flags & INTERVAL) != 0)
e->lastexit = now;
}
}
link_user(new_db, u);
}

View File

@ -17,7 +17,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 15, 2019
.Dd April 19, 2019
.Dt CRONTAB 5
.Os
.Sh NAME
@ -245,12 +245,14 @@ string meaning
The
.Sq @
symbol followed by a numeric value has a special notion of running
a job that much seconds after completion of previous invocation of
a job that many seconds after completion of the previous invocation of
the job.
Unlike regular syntax, it guarantees not to overlap two or more
invocations of the same job.
The first run is scheduled specified amount of seconds after cron
has started.
invocations of the same job during normal cron execution.
Note, however, that overlap may occur if the job is running when the file
containing the job is modified and subsequently reloaded.
The first run is scheduled for the specified number of seconds after cron
is started or the crontab entry is reloaded.
.Sh EXAMPLE CRON FILE
.Bd -literal