Author: kevans
Date: Mon Apr 22 03:04:39 2019
New Revision: 346513
URL: https://svnweb.freebsd.org/changeset/base/346513

Log:
  MFC r334817, r334910
  
  r334817:
  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.
  
  r334910:
  Remove old, dead compat code.
  
  We no longer need to od these things conditionally, and the fallbacks
  are to 4.2BSD era defaults, which nobody uses anymore. Vixie cron has
  diverged from upstream anyway in our tree, and it's not clear there's
  actually a viable upstream anymore. Plus, we don't follow the
  vendor-supplied code pattern here.
  
  I'm doing this to reduce false positives from grep.

Modified:
  stable/11/usr.sbin/cron/cron/cron.c
  stable/11/usr.sbin/cron/cron/cron.h
  stable/11/usr.sbin/cron/cron/do_command.c
  stable/11/usr.sbin/cron/cron/pathnames.h
  stable/11/usr.sbin/cron/crontab/crontab.5
  stable/11/usr.sbin/cron/lib/entry.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/usr.sbin/cron/cron/cron.c
==============================================================================
--- stable/11/usr.sbin/cron/cron/cron.c Mon Apr 22 02:37:46 2019        
(r346512)
+++ stable/11/usr.sbin/cron/cron/cron.c Mon Apr 22 03:04:39 2019        
(r346513)
@@ -46,7 +46,9 @@ static        void    usage(void),
                parse_args(int c, char *v[]);
 
 static int     run_at_secres(cron_db *);
+static void    find_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(&database);
        secres1 = secres2 = run_at_secres(&database);
-       run_reboot_jobs(&database);
        cron_sync(secres1);
+       run_reboot_jobs(&database);
        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: stable/11/usr.sbin/cron/cron/cron.h
==============================================================================
--- stable/11/usr.sbin/cron/cron/cron.h Mon Apr 22 02:37:46 2019        
(r346512)
+++ stable/11/usr.sbin/cron/cron/cron.h Mon Apr 22 03:04:39 2019        
(r346513)
@@ -168,19 +168,29 @@ 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);
-       bitstr_t        bit_decl(month,  MONTH_COUNT);
-       bitstr_t        bit_decl(dow,    DOW_COUNT);
+       union {
+               struct {
+                       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);
+                       bitstr_t        bit_decl(month,  MONTH_COUNT);
+                       bitstr_t        bit_decl(dow,    DOW_COUNT);
+               };
+               struct {
+                       time_t  lastexit;
+                       time_t  interval;
+                       pid_t   child;
+               };
+       };
        int             flags;
 #define        DOM_STAR        0x01
 #define        DOW_STAR        0x02
 #define        WHEN_REBOOT     0x04
-#define        RUN_AT  0x08
+#define        RUN_AT          0x08
 #define        NOT_UNTIL       0x10
 #define        SEC_RES         0x20
+#define        INTERVAL        0x40
        time_t  lastrun;
 } entry;
 

Modified: stable/11/usr.sbin/cron/cron/do_command.c
==============================================================================
--- stable/11/usr.sbin/cron/cron/do_command.c   Mon Apr 22 02:37:46 2019        
(r346512)
+++ stable/11/usr.sbin/cron/cron/do_command.c   Mon Apr 22 03:04:39 2019        
(r346513)
@@ -47,6 +47,8 @@ do_command(e, u)
        entry   *e;
        user    *u;
 {
+       pid_t pid;
+
        Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
                getpid(), e->cmd, u->name, e->uid, e->gid))
 
@@ -57,9 +59,11 @@ do_command(e, u)
         * vfork() is unsuitable, since we have much to do, and the parent
         * needs to be able to run off and fork other processes.
         */
-       switch (fork()) {
+       switch ((pid = fork())) {
        case -1:
                log_it("CRON",getpid(),"error","can't fork");
+               if (e->flags & INTERVAL)
+                       e->lastexit = time(NULL);
                break;
        case 0:
                /* child process */
@@ -70,6 +74,12 @@ do_command(e, u)
                break;
        default:
                /* parent process */
+               Debug(DPROC, ("[%d] main process forked child #%d, "
+                   "returning to work\n", getpid(), pid))
+               if (e->flags & INTERVAL) {
+                       e->lastexit = 0;
+                       e->child = pid;
+               }
                break;
        }
        Debug(DPROC, ("[%d] main process returning to work\n", getpid()))

Modified: stable/11/usr.sbin/cron/cron/pathnames.h
==============================================================================
--- stable/11/usr.sbin/cron/cron/pathnames.h    Mon Apr 22 02:37:46 2019        
(r346512)
+++ stable/11/usr.sbin/cron/cron/pathnames.h    Mon Apr 22 03:04:39 2019        
(r346513)
@@ -53,11 +53,7 @@
 
                        /* where should the daemon stick its PID?
                         */
-#ifdef _PATH_VARRUN
-# define PIDDIR        _PATH_VARRUN
-#else
-# define PIDDIR "/etc/"
-#endif
+#define PIDDIR _PATH_VARRUN
 #define PIDFILE                "%scron.pid"
 
                        /* 4.3BSD-style crontab */
@@ -68,16 +64,4 @@
                        /* what editor to use if no EDITOR or VISUAL
                         * environment variable specified.
                         */
-#if defined(_PATH_VI)
-# define EDITOR _PATH_VI
-#else
-# define EDITOR "/usr/ucb/vi"
-#endif
-
-#ifndef _PATH_BSHELL
-# define _PATH_BSHELL "/bin/sh"
-#endif
-
-#ifndef _PATH_DEFPATH
-# define _PATH_DEFPATH "/usr/bin:/bin"
-#endif
+#define EDITOR _PATH_VI

Modified: stable/11/usr.sbin/cron/crontab/crontab.5
==============================================================================
--- stable/11/usr.sbin/cron/crontab/crontab.5   Mon Apr 22 02:37:46 2019        
(r346512)
+++ stable/11/usr.sbin/cron/crontab/crontab.5   Mon Apr 22 03:04:39 2019        
(r346513)
@@ -17,7 +17,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd January 5, 2016
+.Dd June 6, 2018
 .Dt CRONTAB 5
 .Os
 .Sh NAME
@@ -220,7 +220,10 @@ would cause a command to be run at 4:30 am on the 1st 
 month, plus every Friday.
 .Pp
 Instead of the first five fields,
-one of eight special strings may appear:
+a line may start with
+.Sq @
+symbol followed either by one of eight special strings or by a numeric value.
+The recognized special strings are:
 .Bd -literal -offset indent
 string         meaning
 ------         -------
@@ -235,6 +238,16 @@ string             meaning
 @every_minute  Run once a minute, "*/1 * * * *".
 @every_second  Run once a second.
 .Ed
+.Pp
+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
+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.
 .Sh EXAMPLE CRON FILE
 .Bd -literal
 
@@ -251,6 +264,8 @@ MAILTO=paul
 0 22 * * 1-5   mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
 23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
 5 4 * * sun     echo "run at 5 after 4 every sunday"
+# run at 5 minutes intervals, no matter how long it takes
+@300           svnlite up /usr/src
 .Ed
 .Sh SEE ALSO
 .Xr crontab 1 ,
@@ -292,7 +307,7 @@ either).
 .Pp
 All of the
 .Sq @
-commands that can appear in place of the first five fields
+directives that can appear in place of the first five fields
 are extensions.
 .Sh AUTHORS
 .An Paul Vixie Aq Mt [email protected]

Modified: stable/11/usr.sbin/cron/lib/entry.c
==============================================================================
--- stable/11/usr.sbin/cron/lib/entry.c Mon Apr 22 02:37:46 2019        
(r346512)
+++ stable/11/usr.sbin/cron/lib/entry.c Mon Apr 22 03:04:39 2019        
(r346513)
@@ -132,6 +132,9 @@ load_entry(file, error_func, pw, envp)
        }
 
        if (ch == '@') {
+               long interval;
+               char *endptr;
+
                /* all of these should be flagged and load-limited; i.e.,
                 * instead of @hourly meaning "0 * * * *" it should mean
                 * "close to the front of every hour but not 'til the
@@ -209,6 +212,13 @@ load_entry(file, error_func, pw, envp)
                        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 (*cmd != '\0' &&
+                   (interval = strtol(cmd, &endptr, 10)) > 0 &&
+                   *endptr == '\0') {
+                       Debug(DPARS, ("load_entry()... %ld seconds "
+                           "since last run\n", interval))
+                       e->interval = interval;
+                       e->flags = INTERVAL;
                } else {
                        ecode = e_timespec;
                        goto eof;


_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to