From owner-freebsd-current@FreeBSD.ORG Tue Jan 31 16:49:34 2012 Return-Path: Delivered-To: current@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 4BA94106566B for ; Tue, 31 Jan 2012 16:49:34 +0000 (UTC) (envelope-from jhb@freebsd.org) Received: from cyrus.watson.org (cyrus.watson.org [65.122.17.42]) by mx1.freebsd.org (Postfix) with ESMTP id 250518FC1D for ; Tue, 31 Jan 2012 16:49:34 +0000 (UTC) Received: from bigwig.baldwin.cx (bigwig.baldwin.cx [96.47.65.170]) by cyrus.watson.org (Postfix) with ESMTPSA id D288046B06 for ; Tue, 31 Jan 2012 11:49:33 -0500 (EST) Received: from jhbbsd.localnet (unknown [209.249.190.124]) by bigwig.baldwin.cx (Postfix) with ESMTPSA id 64103B960 for ; Tue, 31 Jan 2012 11:49:33 -0500 (EST) From: John Baldwin To: current@freebsd.org Date: Tue, 31 Jan 2012 11:49:32 -0500 User-Agent: KMail/1.13.5 (FreeBSD/8.2-CBSD-20110714-p10; KDE/4.5.5; amd64; ; ) MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Message-Id: <201201311149.32760.jhb@freebsd.org> X-Greylist: Sender succeeded SMTP AUTH, not delayed by milter-greylist-4.2.7 (bigwig.baldwin.cx); Tue, 31 Jan 2012 11:49:33 -0500 (EST) Cc: Subject: Race between cron and crontab X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 31 Jan 2012 16:49:34 -0000 A co-worker ran into a race between updating a cron tab via crontab(8) and cron(8) yesterday. Specifically, cron(8) failed to notice that a crontab was updated. The problem is that 1) by default our filesystems only use second granularity for timestamps and 2) cron only caches the seconds portion of a file's timestamp when checking for changes anyway. This means that cron can miss updates to a spool directory if multiple updates to the directory are performed within a single second and cron wakes up to scan the spool directory within the same second and scans it before all of the updates are complete. Specifically, when replacing a crontab, crontab(8) first creates a temporary file in /var/cron/tabs and then uses a rename to install it followed by touching the spool directory to update its modification time. However, the creation of the temporary file already changes the modification time of the directory, and cron may "miss" the rename if it scans the directory in between the creation of the temporary file and the rename. The "fix" I am planning to use locally is to simply force crontab(8) to sleep for a second before it touches the spool directory, thus ensuring that it the touch of the spool directory will use a later modification time than the creation of the temporary file. Note that crontab -r is not affected by this race as it only does one atomic update to the directory (unlink()). Index: crontab.c =================================================================== --- crontab.c (revision 225431) +++ crontab.c (working copy) @@ -604,6 +604,15 @@ replace_cmd() { log_it(RealUser, Pid, "REPLACE", User); + /* + * Creating the 'tn' temp file has already updated the + * modification time of the spool directory. Sleep for a + * second to ensure that poke_daemon() sets a later + * modification time. Otherwise, this can race with the cron + * daemon scanning for updated crontabs. + */ + sleep(1); + poke_daemon(); return (0); -- John Baldwin