On Tue, Apr 14, 2020 at 01:05:44PM -0600, Todd C. Miller wrote:
> 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
>
hi. i think the doc parts read fine.
a couple of points:
- in STANDARDS, i guess stick to using Ql rather than Dq
- the system crontab and the example in acme-client.1 should probably be
changed too
jmc
> 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);
> }
> }
>
>