A couple thoughts:
1) I'm not sure how useful random months or days of the week are, but for
consistency maybe?
2) if I do something like
R * * * * /usr/local/bin/thing1
R * * * * /usr/local/bin/thing2
R * * * * /usr/local/bin/thing3
...
R * * * * /usr/local/bin/thing1000
I still have the thundering herd problem a bit, in that all my things fire at
the same R.
In my hearts desire I'd love for "R" to be chosen for each line once at
startup. (so in
the above example the things are randomly distributed). but not sure how hard
that is..
If it saves code and effort I really think this is only useful for hours and
minutes
On Mon, Apr 13, 2020 at 12:54:34PM -0600, Todd C. Miller wrote:
> On Mon, 13 Apr 2020 10:00:52 -0600, Bob Beck wrote:
>
> > +1000. a new random time chosen at cron start.
> >
> > We see this all the time, and it would be a lot better than the hacks for
> > all
> > the things
> >
> > "R" for random sounds good to me
>
> How about this? If you guys like it I'll update the man page too.
> So far only tested with a crontab entry like:
>
> R * * * * date
>
> - todd
>
> Index: usr.sbin/cron/cron.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/cron/cron.c,v
> retrieving revision 1.78
> diff -u -p -u -r1.78 cron.c
> --- usr.sbin/cron/cron.c 11 Feb 2020 12:42:01 -0000 1.78
> +++ usr.sbin/cron/cron.c 13 Apr 2020 16:25:44 -0000
> @@ -129,6 +129,7 @@ main(int argc, char *argv[])
> syslog(LOG_INFO, "(CRON) STARTUP (%s)", CRON_VERSION);
> }
>
> + init_random();
> load_database(&database);
> scan_atjobs(&at_database, NULL);
> set_time(TRUE);
> 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 13 Apr 2020 18:49:56 -0000
> @@ -54,6 +54,12 @@ static const char *ecodes[] = {
> "out of memory"
> };
>
> +typedef enum r_type {
> + r_minute, r_hour, r_dom, r_month, r_dow
> +} rtype_e;
> +
> +static int rand_vals[5];
> +
> static const char *MonthNames[] = {
> "Jan", "Feb", "Mar", "Apr", "May", "Jun",
> "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
> @@ -70,6 +76,8 @@ static int get_list(bitstr_t *, int, int
> get_number(int *, int, const char *[], int, FILE *, const char
> *),
> set_element(bitstr_t *, int, int, int);
>
> +static int set_random(bitstr_t *, int, int, FILE *);
> +
> void
> free_entry(entry *e)
> {
> @@ -187,10 +195,15 @@ load_entry(FILE *file, void (*error_func
> goto eof;
> }
> } else {
> - if (ch == '*')
> - e->flags |= MIN_STAR;
> - ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
> - NULL, ch, file);
> + if (ch == 'R') {
> + ch = set_random(e->minute, rand_vals[r_minute], ch,
> + file);
> + } else {
> + if (ch == '*')
> + e->flags |= MIN_STAR;
> + ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
> + NULL, ch, file);
> + }
> if (ch == EOF) {
> ecode = e_minute;
> goto eof;
> @@ -199,10 +212,14 @@ load_entry(FILE *file, void (*error_func
> /* hours
> */
>
> - if (ch == '*')
> - e->flags |= HR_STAR;
> - ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
> - NULL, ch, file);
> + if (ch == 'R') {
> + ch = set_random(e->hour, rand_vals[r_hour], ch, file);
> + } else {
> + if (ch == '*')
> + e->flags |= HR_STAR;
> + ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
> + NULL, ch, file);
> + }
> if (ch == EOF) {
> ecode = e_hour;
> goto eof;
> @@ -211,10 +228,15 @@ load_entry(FILE *file, void (*error_func
> /* DOM (days of month)
> */
>
> - if (ch == '*')
> - e->flags |= DOM_STAR;
> - ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
> - NULL, ch, file);
> + if (ch == 'R') {
> + /* Note: the DOM value may not exist in this month. */
> + ch = set_random(e->dom, rand_vals[r_dom], ch, file);
> + } else {
> + if (ch == '*')
> + e->flags |= DOM_STAR;
> + ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
> + NULL, ch, file);
> + }
> if (ch == EOF) {
> ecode = e_dom;
> goto eof;
> @@ -223,8 +245,12 @@ load_entry(FILE *file, void (*error_func
> /* month
> */
>
> - ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
> - MonthNames, ch, file);
> + if (ch == 'R') {
> + ch = set_random(e->month, rand_vals[r_month], ch, file);
> + } else {
> + ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
> + MonthNames, ch, file);
> + }
> if (ch == EOF) {
> ecode = e_month;
> goto eof;
> @@ -233,10 +259,14 @@ load_entry(FILE *file, void (*error_func
> /* DOW (days of week)
> */
>
> - if (ch == '*')
> - e->flags |= DOW_STAR;
> - ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
> - DowNames, ch, file);
> + if (ch == 'R') {
> + ch = set_random(e->dow, rand_vals[r_dow], ch, file);
> + } else {
> + if (ch == '*')
> + e->flags |= DOW_STAR;
> + ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
> + DowNames, ch, file);
> + }
> if (ch == EOF) {
> ecode = e_dow;
> goto eof;
> @@ -407,6 +437,20 @@ load_entry(FILE *file, void (*error_func
> }
>
> static int
> +set_random(bitstr_t *bits, int val, int ch, FILE *file)
> +{
> + bit_set(bits, val);
> +
> + /* Advance past whitespace after the 'R'. */
> + ch = get_char(file);
> + if (ch == EOF)
> + return (EOF);
> + Skip_Blanks(ch, file);
> +
> + return (ch);
> +}
> +
> +static int
> get_list(bitstr_t *bits, int low, int high, const char *names[],
> int ch, FILE *file)
> {
> @@ -587,4 +631,45 @@ set_element(bitstr_t *bits, int low, int
>
> bit_set(bits, (number-low));
> return (0);
> +}
> +
> +void
> +init_random(void)
> +{
> + rtype_e r;
> +
> + for (r = 0; r < sizeof(rand_vals) / sizeof(rand_vals[0]); r++) {
> + switch (r) {
> + case r_minute:
> + rand_vals[r_minute] = arc4random_uniform(MINUTE_COUNT);
> + syslog(LOG_DEBUG,
> + "(CRON) RANDOM (MINUTE %d)", rand_vals[r_minute]);
> + break;
> + case r_hour:
> + rand_vals[r_hour] = arc4random_uniform(HOUR_COUNT);
> + syslog(LOG_DEBUG,
> + "(CRON) RANDOM (HOUR %d)", rand_vals[r_hour]);
> + break;
> + case r_dom:
> + rand_vals[r_dom] = arc4random_uniform(DOM_COUNT);
> + syslog(LOG_DEBUG,
> + "(CRON) RANDOM (DOM %d)", rand_vals[r_dom]);
> + break;
> + case r_month:
> + rand_vals[r_month] = arc4random_uniform(MONTH_COUNT);
> + syslog(LOG_DEBUG,
> + "(CRON) RANDOM (MONTH %d)", rand_vals[r_month]);
> + break;
> + case r_dow:
> + /* Decrement so we only count Sunday once. */
> + rand_vals[r_dow] = arc4random_uniform(DOW_COUNT - 1);
> + syslog(LOG_DEBUG,
> + "(CRON) RANDOM (DOW %d)", rand_vals[r_dow]);
> + break;
> + default:
> + syslog(LOG_ERR,
> + "(CRON) ERROR (unexpected random type %d)", r);
> + break;
> + }
> + }
> }
> Index: usr.sbin/cron/funcs.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/cron/funcs.h,v
> retrieving revision 1.29
> diff -u -p -u -r1.29 funcs.h
> --- usr.sbin/cron/funcs.h 5 Feb 2018 03:52:37 -0000 1.29
> +++ usr.sbin/cron/funcs.h 13 Apr 2020 16:25:12 -0000
> @@ -55,3 +55,5 @@ entry *load_entry(FILE *,
> void (*)(const char *), struct passwd *, char **);
>
> FILE *cron_popen(char *, char *, struct passwd *, pid_t *);
> +
> +void init_random(void);
>