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);