On Mon, 13 Apr 2020 21:45:21 -0600, Bob Beck wrote:
> Like this one plenty. I think it's ok the values change on reload.
Here's a cleaned up version that includes the man page. The random
interval can now be one of "~", "low~high", "low~", or "~high" where
if low and/or high are omitted, the appropriate value for the field
is used. Multiple random intervals can be used, separated by a
comma.
- todd
Index: usr.sbin/cron/crontab.5
===================================================================
RCS file: /cvs/src/usr.sbin/cron/crontab.5,v
retrieving revision 1.37
diff -u -p -u -r1.37 crontab.5
--- usr.sbin/cron/crontab.5 6 Jan 2020 19:44:09 -0000 1.37
+++ usr.sbin/cron/crontab.5 14 Apr 2020 19:01:33 -0000
@@ -144,7 +144,18 @@ For example,
.Ar hour
entry specifies execution at hours 8, 9, 10 and 11.
.Pp
-Step values can be used in conjunction with ranges.
+A random value (within the legal range) may be obtained by using the
+.Ql ~
+character in a field.
+The interval of the random value may be specified explicitly, for example
+.Dq 0~30
+will result in a random value between 0 and 30 inclusive.
+If either (or both) of the numbers on either side of the
+.Ql ~
+are omitted, the appropriate limit (low or high) for the field will be used.
+.Pp
+Step values can be used in conjunction with ranges (but not random ranges
+which represent a single number).
Following a range with
.No / Ns Ar number
specifies skips of
@@ -318,6 +329,9 @@ MAILTO=paul
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 hourly at a random time within the first 30 minutes of the hour
+0~30 * * * * /usr/libexec/spamd-setup
.Ed
.Sh SEE ALSO
.Xr crontab 1 ,
@@ -337,6 +351,10 @@ field may use 7 to represent Sunday.
.It
Ranges may include
.Dq steps .
+.It
+Random intervals are supported using the
+.Dq ~
+character.
.It
Months or days of the week can be specified by name.
.It
Index: usr.sbin/cron/entry.c
===================================================================
RCS file: /cvs/src/usr.sbin/cron/entry.c,v
retrieving revision 1.49
diff -u -p -u -r1.49 entry.c
--- usr.sbin/cron/entry.c 13 Jun 2018 11:27:30 -0000 1.49
+++ usr.sbin/cron/entry.c 14 Apr 2020 19:03:47 -0000
@@ -450,33 +450,29 @@ static int
get_range(bitstr_t *bits, int low, int high, const char *names[],
int ch, FILE *file)
{
- /* range = number | number "-" number [ "/" number ]
+ /* range = number | number* "~" number* | number "-" number ["/" number]
*/
int i, num1, num2, num3;
+ num1 = low;
+ num2 = high;
+
if (ch == '*') {
- /* '*' means "first-last" but can still be modified by /step
+ /* '*' means [low, high] but can still be modified by /step
*/
- num1 = low;
- num2 = high;
ch = get_char(file);
if (ch == EOF)
return (EOF);
} else {
- ch = get_number(&num1, low, names, ch, file, ",- \t\n");
- if (ch == EOF)
- return (EOF);
-
- if (ch != '-') {
- /* not a range, it's a single number.
- */
- if (EOF == set_element(bits, low, high, num1)) {
- unget_char(ch, file);
+ if (ch != '~') {
+ ch = get_number(&num1, low, names, ch, file, ",-~
\t\n");
+ if (ch == EOF)
return (EOF);
- }
- return (ch);
- } else {
+ }
+
+ switch (ch) {
+ case '-':
/* eat the dash
*/
ch = get_char(file);
@@ -488,6 +484,37 @@ get_range(bitstr_t *bits, int low, int h
ch = get_number(&num2, low, names, ch, file, "/, \t\n");
if (ch == EOF || num1 > num2)
return (EOF);
+ break;
+ case '~':
+ /* eat the tilde
+ */
+ ch = get_char(file);
+ if (ch == EOF)
+ return (EOF);
+
+ /* get the (optional) number following the tilde
+ */
+ ch = get_number(&num2, low, names, ch, file, ", \t\n");
+ if (ch == EOF)
+ ch = get_char(file);
+ if (ch == EOF || num1 > num2) {
+ unget_char(ch, file);
+ return (EOF);
+ }
+
+ /* get a random number in the interval [num1, num2]
+ */
+ num3 = num1;
+ num1 = arc4random_uniform(num2 - num3 + 1) + num3;
+ /* FALLTHROUGH */
+ default:
+ /* not a range, it's a single number.
+ */
+ if (EOF == set_element(bits, low, high, num1)) {
+ unget_char(ch, file);
+ return (EOF);
+ }
+ return (ch);
}
}