All rights are reserved by the author, except that this document may be freely reproduced in full (not in part) in any form, and may be quoted from provided that i) the quotation is attributed to "Dale Mellor, 2003", and ii) the quotation is only used in the context established by the rest of the document.
Cron is a program designed to allow users, including system administrators, to schedule jobs to be run at regularly repeating but arbitrary times. As the user may not be present on the system when the job runs, the jobs are run under a long-running daemon process belonging to the root user (but restricted to real user privileges), and the output and results of running the job are e-mailed to the real user for later analysis.
A cron program has been present in UNIX (tm) operating systems since early Berkeley and AT&T System V versions.
A freely available re-engineering of the program due to Paul Dixie has been available since 1985, and has in fact become the de-facto standard implementation, especially with users of the GNU operating system. Prior to its final release, Paul polled the usenet community for their opinions and criticisms of the existing programs, from which the following main points arose.
Sometime early in 1994 Matthew Dillon released a brand new version of cron based on his own assertion that a much simpler program would be less insecure and more bug-free. The changes that were subsequently made by Jordan Mendelson invalidates this claim somewhat. This version cut out all the capability to influence the jobs' environments, and only the standard Bourne shell was used to execute the command lines. Matthew did, however, recognise the importance of allowing individual users to manage their crontabs in their own filespace.
While not supposed to be a cron replacement, Anacron was registered on Savannah in 2000, and solves the problem of reliably running system maintainence jobs when the host computer is frequently powered down (it may be a personal desktop, for example). The approach is for anacron to store its internal state while it is running. Each time the program is started, it looks for an old dump of the state, and works out if any jobs have been missed in the time the program has not run.
More recently, in 2002, a gcron project was registered on Savannah, the FSF's development management system, to develop a cron program for the GNU system. To date, the specification document is not half complete and there seems to be no attempt at an implementation.
The mcron program represents a complete re-think of the cron concept originally laid down in the BSD and AT&T Unices, and later rationalized by Paul Vixie. The original idea was to have a daemon that wakes up every minute, scans a set of files under a special directory, and determines from those files if any shell commands should be executed in this minute.
The new idea is to read the required command instructions, work out which command needs to be executed next, and then sleep until the inferred time has arrived. On waking the commands are run, and the time of the next command is computed. Furthermore, the specifications are written in scheme, allowing at the same time simple instructions to be composed and very much more flexible ones than the original Vixie format will allow. This has several useful advantages over the original idea.
This improved simplicity and useability comes about through the use of Guile as the programming medium and extension language (in the sense that the crontab files now become extensions of the cron core).
Surely there must be some disadvantage to this? One could argue that the program will not run as fast or efficiently as the original. (Matthew Dillon might argue that it is feature-laden and thus definitely inefficient). However, as long as the cron core runs in a small fraction of a second (and only when there is known work to do), it is perfectly capable of performing its function (kicking jobs off with a frequency no higher than one per second - and that's a whole improvement over the original); furthermore, inefficiency (if there is any) will not be noticed in terms of overall system performance in this case.
The purpose of this document is to explore the issues that can be addressed in a new cron program, and give a hint as to what the specifications for such a program might be. It is thus not completely definitive, but is a very strong indicator of how the program should work (the functioning of the Vixie version is a definitive specification for the compatibility mode, however).
As it is intended that the new program be a single self-contained Guile script, the architecture will be fairly simple, as will the analysis of user requirements. Thus further documents are not developed, and this document contains strains of analysis and top-level design decisions itself.
Having determined the problem at hand above, and the overall approach to the new design, the next chapter discusses how the new program needs to appear to the users. The various sections consider the commands that need to be available to crontab files, the means of invoking the system, and how the requirements of backwards compatibility with Vixie cron can be met.
Note that the term 'crontab files' appears regularly. The new system will not have direct equivalents (except where compatibility mandates), but for the purpose of this document the configuration files for the new program will often be referred to as 'crontab files', as the intended purpose is the same.
As with the original crontab files, the mcron configuration files need to contain a list of commands and time specifications. Thus the basic entry should take the form
(job (lambda (last-time) next_time_calculation) (lambda () instructions_to_run))where the first argument is a function which computes the next time the command should run given the previous time it was run is passed in the last-time argument, and the second argument is a function to run at the computed times.
As a convenience to the user, functions need to be predefined for the computation of times, so that lines like
(job (lambda (last-time) (next-hour-from last-time)) (lambda () ...))can be used; this can be made more convenient still by allowing a form
(job '(next-hour) (lambda () ...))where the first argument is now evaluated by a lambda expression provided by the mcron process itself, and the function next-hour would take the current time from a global variable which the mcron process would set prior to calling any next-time function.
Finally, the functionality of the original cron can be easily achieved (surpassed) by allowing the next-hour function to take a set of suitable hours, and providing a function which will generate such a set from a range specification, so that lines like
(job '(next-hour (range 0 24 2)) (lambda () ...))can be used to specify a job running every 2 hours, or even
(job '(next-hour-from (next-day) '(2)) (lambda ()...))will execute a job at 2 a.m. every morning.
Of course, using scheme as the configuration language allows the sophisticated user to do much more; he could look at environment variables, the load on the system, and perform complex computations in the determination of the next time to run a command.
However, to make the system more convenient to die-hard users, and to help the implementation of Vixie compatibility, lines of the form
(job "* * 2 * *" "/bin/program")should also be allowed, where the first string is a Vixie-style time specification, and the second string holds commands that should be run under the user's default shell. As before, the mcron system will need to provide lambda functions to do the work implied by the strings.
As noted above, it is prudent to allow Vixie-style time specifications to be given to the job procedure as an ASCII string, but for full compatibility it will be necessary to read these strings 'loose' in configuration files. As files with these 'loose' lines in them cannot be valid Guile, it follows that they will need to be different files, and some functionality is needed in mcron to read them.
The functionality mentioned above also needs to be able to read environment declarations in the Vixie-style configuration files. It will be necessary to accumulate the 'environment modifiers' as the file is read, and then store the current set of modifications with each job specification as the job lines are executed.
When the jobs themselves are executed, it is a simple matter to modify the environment with the required modifications.
The program needs to run under two distinct guises, to allow for a system-wide cron daemon to be instantiated or for a single cron instance which services particular crontabs.
For full compatibility, the distinction can be made according to the name of the command the program is run as. If this is 'cron' or 'crond', then Vixie-compatibility should be assumed and the program should detach from the controlling terminal, read configuration from /var/cron/tabs/* and /etc/crontab, and listen for updates to these files.
When run as 'mcron', the program should only read from files specified on the command line, or files in the user's ~/.cron directory. These files may be a mix of Vixie-style (.vixie or .vix extension) or Guile files (.guile or .gle extension). There is no need for UID processing or detaching from the terminal - the process can run as a regular user job in the system.
The program also needs to recognize if it is invoked as crontab. In this case it needs to emulate the Vixie crontab command, and if changes are made to any configuration file then the central server will need to be notified. This is not the exact same functioning as the original cron processes, but the result will be the same. This notification can take the form of a message placed in a /var/cron/update file to indicate which user's files have been modified, and then a SIGHUP sent to the daemon process (located via a /var/run/cron.pid file).
In non-compatibility mode we need a function available to configuration files to indicate that output from the command should be mailed to some user afterwards. An example of how this might look is
(job '(time-specifier) (lambda () (with-email "command")))
Then the compatilibility mode can achieve the effect of traditional crons by using this function implicitly.
As outlined above, mcron can be run as a user process, with no more privileges as that user normally has. It will not be possible for him to access system files, or to corrupt the operation of the cron system for other users (including system administrators).
However, in compatibility mode, users need access to a daemon process run by root. This is not a problem with conventional crons because the configuration files are merely parsed lumps of ASCII text. However, mcron configuration files are program code in their own right (that's where the power of mcron comes from), so the security situation is a little more tricky.
Because of this, it is likely that the compatibility mode will be regarded from the start as a decprecated feature, and users will be encouraged to use the new modes of operation.
In a Vixie compatibility mode of operation the central mcron server will run as a root process. It will execute users configuration files also as a root process.
It is clear that, to secure the system, the user's files will have to be read in a threaded or forked environment, SUID'd to the user in question. Issue is, how to allow the user to pass variables, including functions, between his scripts? Remember that his next-time commands will need access to these variables. How to pass the results back to the root process?
Exactly the same considerations need to be given to the execution of the next-time commands as to the reading of the configuration files.
The user commands can safely be run under the same security model that existing cron programs use; a separate process is created to run the code, and that process runs under the UID of the user who created the job specification. It must also cd to the user's home directory, and set the PATH environment variable.
All the above considerations imply that it will be extremely difficult to make a central, root-owned daemon process secure if the user is able to supply arbitrary scheme code to specify the jobs he wants to run. It therefore follows that Vixie compatibility mode should be pure and only allow the input of Vixie-style crontab files as created by the crontab command.
On the other hand, it is seen that if an mcron daemon is run by each individual user, then only the privileges of that user are available, and the security should then be ensured by the operating system.
These considerations imply that mcron should have two distinct personalities, a pure Vixie personality and an mcron personality which can also read Vixie files, but not the system ones in /etc/crontab or /var/cron/tabs.