Bug 178836

Summary: Editing crontab ("crontab -e") too quickly causes missed updates.
Product: Red Hat Enterprise Linux 4 Reporter: Björn Augustsson <oggust>
Component: vixie-cronAssignee: Marcela Mašláňová <mmaslano>
Status: CLOSED ERRATA QA Contact: Ben Levenson <benl>
Severity: medium Docs Contact:
Priority: medium    
Version: 4.0CC: biorn, borgan
Target Milestone: ---   
Target Release: ---   
Hardware: i386   
OS: Linux   
Whiteboard:
Fixed In Version: RHBA-2007-0685 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2007-08-02 08:05:41 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
short script that attempts to add a crontab entry.
none
Strace of me editing the crontab in less than 1 sec. :)
none
Patch that zeroes mtime of the temp file before edit, and checks it after. none

Description Björn Augustsson 2006-01-24 18:12:15 UTC
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20050921 Red Hat/1.7.12-1.1.3.2

Description of problem:
If you do "crontab -e", and edit the file too quickly, the crontab command is likely to miss the update. The reason is that it checks the mtime of its file before and after the edit, but mtime only has second resolution. 

So being really fast (eg doing it from a script) is very likely to fail, and lose the update. (Around 50/50, with my test case)

I have a short test case, that I'll attach, showing this.

A workaround in my case (using the "ed" editor) is to add a "!sleep 1" line to the ed script. Ugly.

Note that this also affects RHEL 3 (current package version: vixie-cron-3.0.1-76_EL3 ).

(Perhaps interestingly, in my testing it's more likely to happen on RHEL3 than 4. The tests were done on very different hardware though, and it does fail on both.)

I did an ugly, incomplete fix that tests the file size in addition to the mtime, which fixes my test case, but will also fail for the case of updating a file but not changing the size. The proper fix is likely to either 
* md5sum the file before and after (or compare the whole thing, crontabs tend to not be very large)
* Always update, don't care if it was written to or not.

/August.

Version-Release number of selected component (if applicable):
vixie-cron-4.1-36.EL4

How reproducible:
Sometimes

Steps to Reproduce:
1. See attached test case.
2.
3.
  

Actual Results:  (often) crontab: installing new crontab


Expected Results:  crontab: installing new crontab


Additional info:

Comment 1 Björn Augustsson 2006-01-24 18:14:50 UTC
Created attachment 123623 [details]
short script that attempts to add a crontab entry.

Comment 2 Jason Vas Dias 2006-01-25 15:38:56 UTC
"crontab -e" is really meant for human editors, not scripts.

A much better way of generating crontabs with scripts would be of the form:

  crontab -l | $script | crontab

Use of the 'crontab' command without any arguments replaces the users crontab
with stdin - this is a Red Hat extension to crontab.

So your script could be coded as follows:

---
#!/bin/bash
CRONLINE='50 * * * * echo test'
crontab -l |
( while read line; do
   if [ "$line" = "$CRONLINE" ]; then
      CRONLINE='';
   fi;
   echo $line;
  done
  if [ -n "$CRONLINE" ]; then
     echo "$CRONLINE";
  fi;
) | crontab;
--- 

However, I will look into making crontab access the full 64-bit nanosecond
file modification time available in modern kernels, as an enhancement in
future cron versions.

Show me an strace of a user crontab -e human editor session that completes in
less than 1 second and I'll change this back to a non-enhancement bug :-) .
 

Comment 3 Björn Augustsson 2006-01-25 17:28:29 UTC
> Show me an strace of a user crontab -e human editor session that 
> completes in less than 1 second and I'll change this back to a 
> non-enhancement bug :-) .

Aha! A challenge!

[august@two august]$ time crontab -e
crontab: no changes made to crontab
 
real    0m0.633s
user    0m0.000s
sys     0m0.000s
[august@two august]$

is my current highscore, (lowscore?) where it actually missed the update.
The command is ddZZ (in vi ; delete a line, save the file and exit. A 
reasonable edit... :) )

I've attached an strace version. 

This made my day.

/August.

Comment 4 Björn Augustsson 2006-01-25 17:31:05 UTC
Created attachment 123681 [details]
Strace of me editing the crontab in less than 1 sec. :)

Comment 5 Björn Augustsson 2006-01-26 11:42:26 UTC
I don't agree that stuff like

  crontab -l | $script | crontab

is better. In fact I think it's a bit scary to do that. What if the crontab
processes re-open the crontab file at any point? I'd feel better about

crontab -l > $tempfile ; edit the file ; crontab $tempfile

but then you need a temporary file, with all the hassle that implies. 
(Temp file security, races (several instances of this mechanism doing the same
thing), you name it.)

I like just editing it using -e, it makes things clean and easy.

Oh, and FWIW, I'm down to 490 ms now... :-)

/August.

Comment 6 Björn Augustsson 2006-02-25 13:42:43 UTC
I had another look. 

First of all, I was a bit off in my judgement of the RHEL4 cron. It copies the
mtime of the existing crontab to the temporary one (that you get to edit), and
checks against that. 

So it's safe against a fast edit. _One_ fast edit. If you try to edit the
crontab twice within a second, it still breaks.
(yes, this is a realworld problem, it's not a totally synthetic test.)

Anyway, attached is a patch that sets the mtime of the temp file to 0 (jan
1,1970), and checks if it's still that after the edit. It works. (Even makes
the code shorter and more obvious)

As an aside, I had a look at what solaris (9) does. Their (non vixie-) cron is
safe against fast edits, but all edits take just over a second. Apparently they
have what's essentially my "!sleep 1" workaround, but it's built in!

Here's a benchmark where you really win. Something for marketing ? :-)

Linux: (RHEL 4, on an old PIII.)

[august@arnold vixie-cron-4.1]$ time for i in `seq 1 100` ; do ./add-entry.sh
2>/dev/null  ; done
                                                                               
                                              
real    0m3.897s
user    0m2.268s
sys     0m1.578s
[august@arnold vixie-cron-4.1]$

Solaris (9, on a v880)

bash-2.05b$ time for i in `seq 1 100` ; do ./add-entry.sh  2>&1  >/dev/null  ; done
 
real    1m45.318s
user    0m0.970s
sys     0m3.080s
bash-2.05b$

/August.

Comment 7 Björn Augustsson 2006-02-25 13:44:42 UTC
Created attachment 125246 [details]
Patch that zeroes mtime of the temp file before edit, and checks it after.

Comment 8 Marcela Mašláňová 2006-09-20 14:44:25 UTC
Hello,
nice time ;-). 
I tried your script and it's 30/70 for install new crontab :) I'll try your 
patch, but I received cron after freeze, so I can push it into FC-7 and then 
maybe to updates.
Thanks for your patience.

Comment 15 Karel Volný 2007-07-17 09:46:42 UTC
updated version is going to be released ... and we are better than Solaris 
now, 100 lines inserted below 10 s on all testing machines ;-)

Comment 17 Red Hat Bugzilla 2007-08-02 08:05:41 UTC
An advisory has been issued which should help the problem
described in this bug report. This report is therefore being
closed with a resolution of ERRATA. For more information
on the solution and/or where to find the updated files,
please follow the link below. You may reopen this bug report
if the solution does not work for you.

http://rhn.redhat.com/errata/RHBA-2007-0685.html