Blame SOURCES/cronie-1.4.11-race-on-crontab-modification.patch

2b7e1b
diff -ru cronie-1.4.11/src/database.c cronie-1.4.11_patched/src/database.c
2b7e1b
--- cronie-1.4.11/src/database.c	2018-10-19 15:29:55.630225195 +0200
2b7e1b
+++ cronie-1.4.11_patched/src/database.c	2018-10-19 15:32:14.552093860 +0200
2b7e1b
@@ -48,6 +48,7 @@
2b7e1b
 #include "pathnames.h"
2b7e1b
 
2b7e1b
 #define TMAX(a,b) ((a)>(b)?(a):(b))
2b7e1b
+#define TMIN(a,b) ((a)<(b)?(a):(b))
2b7e1b
 
2b7e1b
 /* size of the event structure, not counting name */
2b7e1b
 #define EVENT_SIZE  (sizeof (struct inotify_event))
2b7e1b
@@ -237,6 +238,8 @@
2b7e1b
 	if ((crontab_fd = check_open(tabname, uname, pw, &mtime)) == -1)
2b7e1b
 		goto next_crontab;
2b7e1b
 
2b7e1b
+	mtime = TMIN(new_db->mtime, mtime);
2b7e1b
+
2b7e1b
 	Debug(DLOAD, ("\t%s:", fname));
2b7e1b
 
2b7e1b
 	if (old_db != NULL)
2b7e1b
@@ -261,7 +264,7 @@
2b7e1b
 		 * we finish with the crontab...
2b7e1b
 		 */
2b7e1b
 		Debug(DLOAD, (" [delete old data]"));
2b7e1b
-			unlink_user(old_db, u);
2b7e1b
+		unlink_user(old_db, u);
2b7e1b
 		free_user(u);
2b7e1b
 		log_it(fname, getpid(), "RELOAD", tabname, 0);
2b7e1b
 	}
2b7e1b
@@ -328,18 +331,18 @@
2b7e1b
 	cron_db new_db;
2b7e1b
 	DIR_T *dp;
2b7e1b
 	DIR *dir;
2b7e1b
-	struct timeval time;
2b7e1b
+	struct timeval timev;
2b7e1b
 	fd_set rfds;
2b7e1b
 	int retval;
2b7e1b
 	char buf[BUF_LEN];
2b7e1b
 	pid_t pid = getpid();
2b7e1b
-	time.tv_sec = 0;
2b7e1b
-	time.tv_usec = 0;
2b7e1b
+	timev.tv_sec = 0;
2b7e1b
+	timev.tv_usec = 0;
2b7e1b
 
2b7e1b
 	FD_ZERO(&rfds);
2b7e1b
 	FD_SET(old_db->ifd, &rfds);
2b7e1b
 
2b7e1b
-	retval = select(old_db->ifd + 1, &rfds, NULL, NULL, &time);
2b7e1b
+	retval = select(old_db->ifd + 1, &rfds, NULL, NULL, &timev;;
2b7e1b
 	if (retval == -1) {
2b7e1b
 		if (errno != EINTR)
2b7e1b
 			log_it("CRON", pid, "INOTIFY", "select failed", errno);
2b7e1b
@@ -348,6 +351,7 @@
2b7e1b
 	else if (FD_ISSET(old_db->ifd, &rfds)) {
2b7e1b
 		new_db.head = new_db.tail = NULL;
2b7e1b
 		new_db.ifd = old_db->ifd;
2b7e1b
+		new_db.mtime = time(NULL) - 1;
2b7e1b
 		while ((retval = read(old_db->ifd, buf, sizeof (buf))) == -1 &&
2b7e1b
 			errno == EINTR) ;
2b7e1b
 
2b7e1b
@@ -452,14 +456,17 @@
2b7e1b
 	DIR *dir;
2b7e1b
 	pid_t pid = getpid();
2b7e1b
 	int is_local = 0;
2b7e1b
+	time_t now;
2b7e1b
 
2b7e1b
 	Debug(DLOAD, ("[%ld] load_database()\n", (long) pid));
2b7e1b
 
2b7e1b
-		/* before we start loading any data, do a stat on SPOOL_DIR
2b7e1b
-		 * so that if anything changes as of this moment (i.e., before we've
2b7e1b
-		 * cached any of the database), we'll see the changes next time.
2b7e1b
-		 */
2b7e1b
-		if (stat(SPOOL_DIR, &statbuf) < OK) {
2b7e1b
+	now = time(NULL);
2b7e1b
+
2b7e1b
+	/* before we start loading any data, do a stat on SPOOL_DIR
2b7e1b
+	 * so that if anything changes as of this moment (i.e., before we've
2b7e1b
+	 * cached any of the database), we'll see the changes next time.
2b7e1b
+	 */
2b7e1b
+	if (stat(SPOOL_DIR, &statbuf) < OK) {
2b7e1b
 		log_it("CRON", pid, "STAT FAILED", SPOOL_DIR, errno);
2b7e1b
 		statbuf.st_mtime = 0;
2b7e1b
 	}
2b7e1b
@@ -492,13 +499,17 @@
2b7e1b
 	 * Note that old_db->mtime is initialized to 0 in main(), and
2b7e1b
 	 * so is guaranteed to be different than the stat() mtime the first
2b7e1b
 	 * time this function is called.
2b7e1b
+	 *
2b7e1b
+	 * We also use now - 1 as the upper bound of timestamp to avoid race,
2b7e1b
+	 * when a crontab is updated twice in a single second when we are
2b7e1b
+         * just reading it.
2b7e1b
 	 */
2b7e1b
-	if (old_db->mtime == TMAX(crond_stat.st_mtime,
2b7e1b
-			TMAX(statbuf.st_mtime, syscron_stat.st_mtime))
2b7e1b
+	if (old_db->mtime == TMIN(now - 1, TMAX(crond_stat.st_mtime,
2b7e1b
+			TMAX(statbuf.st_mtime, syscron_stat.st_mtime)))
2b7e1b
 		) {
2b7e1b
 		Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n",
2b7e1b
 				(long) pid));
2b7e1b
-			return 0;
2b7e1b
+		return 0;
2b7e1b
 	}
2b7e1b
 
2b7e1b
 	/* something's different.  make a new database, moving unchanged
2b7e1b
@@ -506,8 +517,7 @@
2b7e1b
 	 * actually changed.  Whatever is left in the old database when
2b7e1b
 	 * we're done is chaff -- crontabs that disappeared.
2b7e1b
 	 */
2b7e1b
-	new_db.mtime = TMAX(crond_stat.st_mtime,
2b7e1b
-		TMAX(statbuf.st_mtime, syscron_stat.st_mtime));
2b7e1b
+	new_db.mtime = now - 1;
2b7e1b
 	new_db.head = new_db.tail = NULL;
2b7e1b
 #if defined WITH_INOTIFY
2b7e1b
 	new_db.ifd = old_db->ifd;