diff -Naur a/cron.8 b/cron.8 --- a/cron.8 2018-06-03 19:09:42.562436425 -0500 +++ b/cron.8 2018-06-03 21:38:57.535075370 -0500 @@ -30,17 +30,18 @@ .RB [ \-n ] .SH DESCRIPTION .I Cron -should be started from /etc/rc or /etc/rc.local. It will return immediately, -so you don't need to start it with '&'. The \-n option changes this default -behavior causing it to run in the foreground. This can be useful when -starting it out of init. +should normally be started via the init.d script. It will return immediately. +The \-n option changes this default behavior causing it to run in the +foreground. This can be useful when starting it out of init. .PP .I Cron -searches /var/cron/tabs for crontab files which are named after accounts in -/etc/passwd; crontabs found are loaded into memory. +searches /var/spool/cron/crontabs for crontab files which are named after +accounts in /etc/passwd; crontabs found are loaded into memory. .I Cron also searches for /etc/crontab which is in a different format (see -.IR crontab (5)). +.IR crontab +(5)). This version of cron also looks in /etc/cron.d +for files and parses them in the same manner as /etc/crontab. .I Cron then wakes up every minute, examining all stored crontabs, checking each command to see if it should be run in the current minute. When executing @@ -49,10 +50,8 @@ .PP Additionally, .I cron -checks each minute to see if its spool directory's modtime (or the modtime -on -.IR /etc/crontab ) -has changed, and if it has, +checks each minute to see if the modtime has changed for the spool directory, +/etc/crontab, or /etc/cron.d. If it has, .I cron will then examine the modtime on all crontabs and reload those which have changed. Thus @@ -84,7 +83,8 @@ In this version of .BR cron , /etc/crontab must not be readable or writable by any user other than root. -In other words, it should be mode 0600. +In other words, it should be mode 0600. The same holds true for files in +/etc/cron.d. .SH "SEE ALSO" .IR crontab (1), .IR crontab (5) diff -Naur a/crontab.1 b/crontab.1 --- a/crontab.1 2018-06-03 19:09:42.562436425 -0500 +++ b/crontab.1 2018-06-03 21:22:44.319733805 -0500 @@ -88,8 +88,8 @@ crontab(5), cron(8) .SH FILES .nf -/var/cron/cron.allow -/var/cron/cron.deny +/var/spool/cron/cron.allow +/var/spool/cron/cron.deny .fi .SH STANDARDS The diff -Naur a/database.c b/database.c --- a/database.c 2018-06-03 19:09:42.582437220 -0500 +++ b/database.c 2018-06-03 20:49:21.555262391 -0500 @@ -28,7 +28,7 @@ #include "cron.h" -#define TMAX(a,b) ((a)>(b)?(a):(b)) +#define TMAX(a,b,c) ((a)>(b)?(((a)>(c))?a:c):((b)>(c)?(b):(c))) static void process_crontab(const char *, const char *, const char *, struct stat *, @@ -36,7 +36,7 @@ void load_database(cron_db *old_db) { - struct stat statbuf, syscron_stat; + struct stat statbuf, syscron_stat, crond_stat; cron_db new_db; DIR_T *dp; DIR *dir; @@ -53,6 +53,13 @@ (void) exit(ERROR_EXIT); } + /* we do the same for CROND_DIR + */ + if (stat(CROND_DIR, &crond_stat) < OK) { + log_it("CRON", getpid(), "STAT FAILED", CROND_DIR); + (void) exit(ERROR_EXIT); + } + /* track system crontab file */ if (stat(SYSCRONTAB, &syscron_stat) < OK) @@ -65,7 +72,7 @@ * so is guaranteed to be different than the stat() mtime the first * time this function is called. */ - if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) { + if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime, crond_stat.st_mtime)) { Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n", (long)getpid())) return; @@ -76,7 +83,7 @@ * actually changed. Whatever is left in the old database when * we're done is chaff -- crontabs that disappeared. */ - new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime); + new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime, crond_stat.st_mtime); new_db.head = new_db.tail = NULL; if (syscron_stat.st_mtime) @@ -116,6 +123,37 @@ } closedir(dir); + /* we perform the same task now for CROND_DIR + */ + if (!(dir = opendir(CROND_DIR))) { + log_it("CRON", getpid(), "OPENDIR FAILED", CROND_DIR); + (void) exit(ERROR_EXIT); + } + + while (NULL != (dp = readdir(dir))) { + char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; + + /* avoid file names beginning with ".". this is good + * because we would otherwise waste two guaranteed calls + * to getpwnam() for . and .., and also because user names + * starting with a period are just too nasty to consider. + */ + if (dp->d_name[0] == '.') + continue; + + if (strlen(dp->d_name) >= sizeof fname) + continue; /* XXX log? */ + (void) strcpy(fname, dp->d_name); + + if (!glue_strings(tabname, sizeof tabname, CROND_DIR, + fname, '/')) + continue; /* XXX log? */ + + process_crontab("root", NULL, tabname, &crond_stat, + &new_db, old_db); + } + closedir(dir); + /* if we don't do this, then when our children eventually call * getpwnam() in do_command.c's child_process to verify MAILTO=, * they will screw us up (and v-v). @@ -207,7 +245,7 @@ goto next_crontab; } if ((statbuf->st_mode & 07777) != 0600) { - log_it(fname, getpid(), "BAD FILE MODE", tabname); + log_it(fname, getpid(), "BAD FILE MODE (expected 0600)", tabname); goto next_crontab; } if (statbuf->st_uid != ROOT_UID && (pw == NULL || diff -Naur a/pathnames.h b/pathnames.h --- a/pathnames.h 2018-06-03 19:09:42.570436703 -0500 +++ b/pathnames.h 2018-06-03 21:04:02.563618663 -0500 @@ -35,7 +35,7 @@ * to; SPOOL_DIR, CRON_ALLOW, CRON_DENY, and LOG_FILE * are all relative to this directory. */ -#define CRONDIR "/var/cron" +#define CRONDIR "/var/spool/cron" #endif /* SPOOLDIR is where the crontabs live. @@ -46,7 +46,14 @@ * newer than they were last time around (or which * didn't exist last time around...) */ -#define SPOOL_DIR "tabs" +#define SPOOL_DIR "crontabs" + + /* CROND_DIR is where the system wide extentions + * to /etc/crontab are placed. These are to be + * treated similarly to the files from SPOOL_DIR + * except they will be parsed as /etc/crontab is + */ +#define CROND_DIR "/etc/cron.d" /* cron allow/deny file. At least cron.deny must * exist for ordinary users to run crontab.