Cron

Cron is an implementation of the standard Unix cron daemon. This implementation of cron is almost posixy – it does not support system crontabs or some of the fancy posix extensions found in other free versions of cron – but it makes up for it by being very small and invented here.

Source Code

version 0.9.1

It has come to my attention that if I have a connection to the syslog daemon and I then restart said daemon, cron will come to a complete and total halt the next time it tries to write a syslog message. Cron attempts writes to syslog before it runs a job, which means that as if I restart syslogd, cron will freeze up as soon as it tries to do anything after that.

Ooops.

Version 0.9.1 removes that feature by opening and closing the syslog connection every time it wants to write a message.

version 0.9
Why lookie here, it’s my old nemesis tm_mon rearing its ugly head again. In the crontab, months run from 1 to 12, while tm_mon months run from 0 to 11. I didn’t realize that I’d made this mistake again until the clock rolled over to 2008 and cron simply stopped working altogether.
version 0.8
One thing I kept noticing with cron was that occasionally a runjob would get confused and leave a zombie. I couldn’t figure out why it was doing this, and spent a lot of time first finding out which job it was (it turned out to be when I started postoffice as a daemon process) and then trying to twiddle the guts of runjob so that it wouldn’t leave a zombie. I finally fixed that particular wagon, and after it kept working for several days I decided it was time to scoot 0.8 out the door before something else broke.
version 0.7

0.6 was almost perfect, except for a couple of minor annoyances. One of them was cosmetic; since I was using a universal child process status eater occasionally parts of the input->job->mail chain would get disconnected from cron and end up orphaned. The other one, which was actually a performance bug, was that occasionally cron would hiccup and gobble up a bunch of processor time for no apparent reason.

It turned out that my all-singing-all-dancing sigchld handler was the culprit for both of these features, and the second was that the handler would occasionally gallop off into a cpu-eating frenzy and eat between 10 and 20 seconds of cpu time between job runs. The solution to both of these problems was to redo the signal handling so that cron itself would use a “one process=one reap” handler, while runjob() would revert to using wait(), thus leaving the process chain intact until the end.

version 0.6
No notable bugs this time around, just some fussy tweaking:
  1. When I wrote cron, I simply slept 60 seconds between cron runs. Over time, this would tend to drift and I eventually lost a minute when the seconds rolled over at the wrong time. For 0.6, I track the # of seconds past the minute and try to adjust the sleep time so cron will do the next run at 30 seconds past the minute.
  2. I hadn’t been cleaning out the environment, so jobs would pick up whatever was in the environment of the process that started the cron daemon. This is aesthetically displeasing (and probably insecure), so I redid the code to use execle() instead of popen() when running jobs.
  3. As a side-effect of this, I build the entire environment for the job when I parse the crontab. There are only five items in the environment (PATH, SHELL, USER, LOGNAME, and HOME,) so it’s a fairly inexpensive operation.
  4. Finally I’d discovered that, even though I’d double-forked and setsid()ed myself away from the controlling terminal, cron would still stick and prevent me from logging out. That was because std{in,out,err} were still attached to my terminal. I fixed this wagon by reattaching them to /dev/null.
version 0.5
And yet another buglet; I wasn’t sanitizing the input environment, so $HOME was carried in from cron and (not surprisingly) ended up being set to the less than useful /home/root. While I was at it I also added a configure check for the path to mail and converted the mailto() function into a more-generic jobenv() function.
version 0.4

Much to my (complete lack of) surprise, I found a couple of buglets in cron. Not on pell, but on a rhel3 system that also got the new cron installed. On the rhel3 system select() returns ready-for-reading when a file descriptor goes EOF, so it was triggering and cheerfully giving me zero-length output every time it ran a job. This annoying feature continued even when I guarded the test with !FD_ISSET(0, &errors) [because EOF isn’t actually an error on the rhel3 box, so I had to replace a call to pipe() with a call to socketpair() so I could use recv(0,peek,1,MSG_PEEK) to see whether select returned “yes, we have input!” or “yes, we have EOF”.

Somewhere in the middle of all this I got sick and tired of doing “make CC="cc CRONDIR=\"/var/spool/cron\"",” so I took 15 minutes to make the code use my configure scripts so I could set up the configuration once and leave it there while I tested out the socketpair()/recv() code.

version 0.2
This is the ‘chock-full-of-security-holes’ release. I started writing it on Sunday, Oct 14, got the cron daemon running on Monday, Oct 15, and wrote the crontab command and the documentation in between dinner and setting up a new fruit-OS PC. Cron doesn’t have very many places where it can have whopping security violations, but if there are any I’ve probably left them in for the wily hacker to exploit.

Trivia

the amazing bloatotron