Crontrolling
Your Crontabs
Matthew Hoskins
Crontab files were designed to be modified from within an editor.
This poses a problem if you want to automate or script crontab manipulations.
Most initial operating system installs come with a basic set of
housekeeping cron jobs scheduled to run at various times. As a systems
administrator, you probably replace many of these built-in jobs
or augment them with homegrown programs. This can easily be accomplished
during the operating system install by replacing the standard crontab
file, wherever it may be. But after installing the OS, you may also
install applications and services. Unless all your systems
do exactly the same thing, you will most likely need to install
different crontab entries on different systems and update them over
the life of a system.
In recent times, cron daemons have added features where cron jobs
can be created as files in a directory (i.e., cron.d, cron.daily,
cron.weekly, etc.). These features were added to allow packaged
software to more easily create and modify jobs, but these methods
are platform dependent. The only relatively independent method is
using the crontab command to list and replace a user's crontab.
My Solution: Crontrol
Cool name, eh? As I stated previously, the only platform-independent
methodology for handling crontabs on most systems is using the "list"
and "replace" functionality of the crontab command. To
automate this process, we list, backup, modify, then save back the
modified crontab file. My goal in this project was to automate and
simplify crontab job management across many systems, which meant
filling in some missing features. I wanted a scriptable, command-line,
self-documenting, portable utility. Other requirements for this
utility included:
- Cron jobs are assigned a short unique textual ID.
- Maintain a library of standard jobs for easy installation on
multiple hosts.
- Ability to enable/disable jobs (commenting/un-commenting).
- Allow entries to contain textual comments.
- Must not hinder or break manual editing of crontab files.
- Automatically backup entire crontab before modification.
- Create/delete/modify crontab entries from command line/scripts.
- Must not disturb existing entries or comments.
- Cross-platform -- must work on any OS with the crontab
command.
- Maintain a "library" of common cron jobs for easy
installation.
Perl is my sledgehammer of choice, so any platform with a crontab
command and Perl should be able to run crontrol. It has been tested
on various flavors of Linux, Solaris, and OpenBSD. Other platforms
may require minor modification; please send me feedback regarding
any modifications you make.
How Crontrol Works
Listing 1 shows a summary of options and major modes of operation
for the crontrol command; invoke with -h to see this on your
own systems.
Let's look at some examples. Suppose we wanted our systems
to say "Hello World!" every 10 minutes during the hours
of 11, 12, 16, and 18. We would formulate a command line similar
to:
$ crontrol -a hello.world -T 'Say hello via cron' -H 11,12,16,18 \
-M 0,10,20,30,40,50 -C 'echo "Hello World!"'
Sure, some systems support the "*/10" notation to denote
"every 10 minutes", but the name of the game here is portability.
Any time fields that are not specified will default to "*".
The previous command results in the following being appended to the
end of the crontab file:
##################################################################
# The following entries were added by CRONTROL
# Entries can be edited by hand provided you preserve the format
# of the metadata stored in the comment.
##################################################################
# %CRONTROL-COMMENT%
0,10,20,30,40,50 11,12,16,18 * * * echo "Hello World!" \
#%CRONTROL%; ID=hello.world; COMMENT=Say hello via cron
This shows that crontrol uses comments for both documentation and
for storing its metadata. The newly created entry is uniquely identified
by the textual id "hello.world", which is used as the id
for all future modification of this entry. The COMMENT= field is purely
for humans and is a place to remind yourself of exactly what you were
trying to accomplish with this job. Here are some more quick examples:
# Comment out/disable the hello.world job
$ crontrol -c hello.world
# Un-comment/re-enable the hello.world job
$ crontrol -u hello.world
# Modify the existing job to only run during hours 9 and 10
$ crontrol -m hello.world -H 9,10
# Modify the command to say Goodbye
$ crontrol -m hello.world -C 'echo "Goodbye world!"'
# Delete the entry completely
$ crontrol -d hello.world
# List all crontab entries with some snazzy formatting
$ crontrol -l
Using Libraries
The key to organized and easy systems administration is uniformity.
To that end, we sys admins usually collect libraries of tools and
procedures that are proven to work well. Crontrol handles this by
keeping a library of commonly used jobs for easy installation on any
system. The library is kept either in the top of the crontrol script
itself, or included with a Perl require() statement. Both formats
are documented in the crontrol script file. Here is an example of
an entry to describe our "hello.world" job:
@LIBRARY=(@LIBRARY,
{
ID => 'hello.world',
MINUTES => '0,10,20,30,40,50',
HOURS => '11,12,16,18',
COMMENT => 'Say hello via cron',
COMMAND => 'echo "Hello World!"'
},
);
This is fairly self-explanatory; the library entries are stored in
a list of associative arrays. Any Perl syntax for accomplishing that
can be used. Some basic examples are included in the distributed version
of the crontrol script file, so you don't really need to be a
Perl programmer to use it. Other time specification fields are: DOW
(day of week), DOM (days of month), and MONTHS. All are numeric and
follow the standard crontab format. Any time specification that is
omitted becomes "*" by default.
Why keep the configuration inside the script? Some of you
are saying, "Use a configuration file!". Simply, I wanted
this utility to have as few dependencies as possible. By putting
the config and library inside the script, I eliminated two possible
extra files. You can simply copy the script to a destination system
and use it. This keeps the program always in sync with the config
and library.
If you want to store your library in a separate file, you have
a couple of options. First, a file named ~/.crontrol will automatically
be read upon startup. You can add library entries using the above
syntax. Second, if you want a global library for all users, create
a require('/some/dir/your.crontrol.global.lib') entry at
the top of the crontrol program. See examples given inside the config
section of the crontrol script.
Adding an entry from the library of your standard jobs is as simple
as a single non-interactive command line. To add the "hello.world"
job to a crontab from the library, you would execute the following
command:
$ crontrol -L hello.world
To list all entries in the library, you would execute:
$ crontrol -B
==================================================================
The library of TEMPLATES contains:
==================================================================
0,10,20,30,40,50 11,12,16,18 * * * echo "Hello World!"
ID: hello.world
Comment: Say hello via cron
After a job entry has been added, you can use any of the previously
mentioned crontrol commands to modify it. If crontrol notices that
an entry in the crontab no longer matches the definition inside the
library, it will let you know with a message similar to the following
whenever crontrol is invoked:
*** NOTICE: MISMATCH between library and crontab for entry ID: [hello.world]
-> HOURS: Library says: [11,12,16,18] but crontab is: [11,12]
-> MINUTES: Library says: [0,10,20,30,40,50] but crontab is: [0,1,10]
There are two ways to correct these mismatches -- either change
crontab or update the library entry. If you want to keep the changes
but stop the complaining, simply change the ID of the installed entry.
Use a command similar to the following:
$ crontrol -m hello.world -I hello.world.local
This would rename an entry with the ID "hello.world" to
"hello.world.local". Crontrol will no longer complain. Alternatively,
if the library represents what you want installed, simply delete and
re-add the entry. Keeping the library up to date will reduce these
situations.
But, what happens if someone modifies an entry manually with an
editor? Well, first and foremost, nothing bad! One of the design
goals was not to complicate manual editing in any way. You
just need to preserve the format after the "#" in each
entry.
Conclusion
Since I have been using crontrol, I have collected nearly 200
standard job entries in my library to support various system- and
application-level automation processes. Crontrol allows me to snap
these jobs into systems automatically and non-interactively, which
is especially useful when setting up a new system.
You can download the Perl script for crontrol from:
http://www.njit.edu/~matt/crontrol/
Then, copy it to a directory in your path (like /usr/local/bin), make
it executable, and start using it. You may need to adjust the path
to your Perl installation on the first line of the script. The script
will create a ~/.archive/ directory, which will be used to save backup
copies of the crontab before each change. Crontrol is released under
GPL. Enjoy!
Matthew Hoskins is a Unix Systems Administrator for The New
Jersey Institute of Technology where he maintains many of the corporate
administrative systems. He enjoys trying to get wildly different
systems and software working together, usually with a thin layer
of Perl (Locally known as "MattGlue"). When not hacking
systems, he can often be found hacking in the kitchen. Matt can
be contacted at: matt@njit.edu. |