summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2020-04-15 01:59:35 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2020-04-15 01:59:35 +0000
commita69625443ec6afc469b0f69ed71716e6e5a2b8ec (patch)
treefdb388a21d3dc30d481af40c0c66d96f2fcbac2b
parentba9b883e5956e7bc06530c9af36709a8d70a5688 (diff)
Add support for random values using the '~' operator.
For example "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 '~' are omitted, the appropriate limit (low or high) for the field will be used. OK deraadt@ beck@ jmc@
-rw-r--r--usr.sbin/cron/crontab.524
-rw-r--r--usr.sbin/cron/entry.c61
2 files changed, 65 insertions, 20 deletions
diff --git a/usr.sbin/cron/crontab.5 b/usr.sbin/cron/crontab.5
index f9b4446cf87..ef9ac0a6672 100644
--- a/usr.sbin/cron/crontab.5
+++ b/usr.sbin/cron/crontab.5
@@ -17,9 +17,9 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.\" $OpenBSD: crontab.5,v 1.37 2020/01/06 19:44:09 job Exp $
+.\" $OpenBSD: crontab.5,v 1.38 2020/04/15 01:59:34 millert Exp $
.\"
-.Dd $Mdocdate: January 6 2020 $
+.Dd $Mdocdate: April 15 2020 $
.Dt CRONTAB 5
.Os
.Sh NAME
@@ -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 ,
@@ -338,6 +352,10 @@ field may use 7 to represent Sunday.
Ranges may include
.Dq steps .
.It
+Random intervals are supported using the
+.Ql ~
+character.
+.It
Months or days of the week can be specified by name.
.It
Mailing after a successful run can be suppressed with
diff --git a/usr.sbin/cron/entry.c b/usr.sbin/cron/entry.c
index bb0221bffb6..e3f919a86e3 100644
--- a/usr.sbin/cron/entry.c
+++ b/usr.sbin/cron/entry.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: entry.c,v 1.49 2018/06/13 11:27:30 job Exp $ */
+/* $OpenBSD: entry.c,v 1.50 2020/04/15 01:59:34 millert Exp $ */
/*
* Copyright 1988,1990,1993,1994 by Paul Vixie
@@ -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 high, const char *names[],
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);
}
}