Red Hat Bugzilla – Bug 178836
Editing crontab ("crontab -e") too quickly causes missed updates.
Last modified: 2007-11-30 17:07:22 EST
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-126.96.36.199
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.
Version-Release number of selected component (if applicable):
Steps to Reproduce:
1. See attached test case.
Actual Results: (often) crontab: installing new crontab
Expected Results: crontab: installing new crontab
Created attachment 123623 [details]
short script that attempts to add a crontab entry.
"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:
CRONLINE='50 * * * * echo test'
crontab -l |
( while read line; do
if [ "$line" = "$CRONLINE" ]; then
if [ -n "$CRONLINE" ]; then
) | 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 :-) .
> 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
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.
Created attachment 123681 [details]
Strace of me editing the crontab in less than 1 sec. :)
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... :-)
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
Solaris (9, on a v880)
bash-2.05b$ time for i in `seq 1 100` ; do ./add-entry.sh 2>&1 >/dev/null ; done
Created attachment 125246 [details]
Patch that zeroes mtime of the temp file before edit, and checks it after.
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.
updated version is going to be released ... and we are better than Solaris
now, 100 lines inserted below 10 s on all testing machines ;-)
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.