8. Cron Jobs

The usual way of defining cron jobs in /etc/crontab has some limitations:

Therefore SUSE added two extensions to the cron package:

  1. It allows running cron jobs on a regular basis (hourly, daily, weekly, and monthly) even if the computer is switched off for a part of the day. Each package can define such jobs in a separate file.

  2. It allows defining jobs to be done at exact times in separate files.

The following two sections describe these extensions and explain how to use then in a package.

8.1. Jobs Started Regularly

Some packages need to perform maintenance duties on a regular basis, such as purging old cache files or rebuilding search databases. For this purpose, SUSE Linux provides the following directories:

  • /etc/cron.hourly

  • /etc/cron.daily

  • /etc/cron.weekly

  • /etc/cron.monthly

As defined in /etc/crontab, cron executes the script /usr/lib/cron/run-crons every 15 minutes. It looks for scripts in the directories /etc/cron.{hourly,daily,weekly,monthly} and checks which should be executed. The information about last run is stored in files below /var/spool/cron/lastrun. This scheme makes sure that scripts do get executed on a regular basis as opposed to adding a static cron entry that must be executed at a fixed time. Fixed times are possible as well, as described below.

Any package can put a script in any of these directories. The script is usually included in the package as an extra source. It is installed in the %install section and listed in the %files section.

This example is taken from the package man. There are even two cron scripts installed to keep them simple and synoptic:

Source2:      cron.daily.do_mandb
Source3:      cron.daily.clean_catman
[...]

%install
mkdir -p ${RPM_BUILD_ROOT}/etc/cron.daily
install -m 700 %{SOURCE2} ${RPM_BUILD_ROOT}\
/etc/cron.daily/do_mandb
install -m 700 %{SOURCE3} ${RPM_BUILD_ROOT}\
/etc/cron.daily/clean_catman
[...]

%files
[...]
/etc/cron.daily/clean_catman
/etc/cron.daily/do_mandb

There are not limitations on the cron scripts. However, they should perform proper checks before doing any action. Otherwise, the repeated failure notifications can flood the mail account of the system administrator. For example, the script should check for the existence of a binary before it attempts to execute it. It should return “0” even if the action failed unless it is really important to report problems.

This example comes from the package wwwoffle:

#!/bin/sh
#
# purge the wwwoffle cache directory according to
# the parameters defined in /etc/wwwoffle/wwwoffle.conf
#
if [ -x /usr/bin/wwwoffle ] ; then
        /sbin/checkproc /usr/sbin/wwwoffled && \
        /usr/bin/wwwoffle -purge -c /etc/wwwoffle/wwwoffle.conf
        exit 0
else
        exit 0
fi

See the example for the proper checks. The action can be done only if the binary exists and the service is running.

The following and more complicated example comes from the package man:

#!/bin/sh
#
# clean_catman. This script was split off cron.daily
# Please add your local changes to cron.daily.local
# since this file will be overwritten, when updating your system.
#
# Copyright (c) 1996-2002 SuSE GmbH Nuernberg, Germany.
#
# please send bugfixes or comments to feedback@suse.de.
#
# Authors:
#    Burchard Steinbuild, feedback to http://www.suse.de/feedback
#    Florian La Roche, feedback to http://www.suse.de/feedback
#
# paranoia settings
#
umask 022

PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH

if [ -f /etc/sysconfig/cron ] ; then
    . /etc/sysconfig/cron
fi

# Delete too old preformatted man-pages.

if test "$DELETE_OLD_CATMAN" = yes ; then
    if test -z "$CATMAN_ATIME" ; then
        # Default is 7 days
        CATMAN_ATIME=7
    fi
    test -e /var/cache/man -a -x /usr/bin/safe-rm && \
        find /var/cache/man -name '*.gz' -type f \
     -atime +$CATMAN_ATIME  -print0 | \
        xargs --no-run-if-empty --max-lines=200 --null -- \
     /usr/bin/safe-rm
fi

exit 0

See the example for the paranoia setting at the beginning. They are useful to reduce the vulnerability of this script from the security point of view. See also the proper use of /etc/sysconfig. The user need not edit the script himself, but he can configure the system setting at one place.

8.2. Jobs Started at Exact Time

Some packages require finer control of their scheduling than hourly, daily, weekly or monthly. For this purpose, SUSE Linux provides the directory /etc/cron.d.

This directory is not for scripts It is for config files that use the same format as /etc/crontab. See crontab(5) man page for more information. Cron treats files in this directory the same way as /etc/crontab. It monitors this directory for changes and runs the jobs as they are configured. The advantage is that each package could have its configuration in a separate file.

Any package can put a config file in this directory. The config filename should be the same as the package name and must consist solely of uppercase and lowercase letters, digits, underscores, and hyphens.

The config file can be included in the package as an extra source. Then it is installed in %install section and mentioned in %files section. It is a good idea to mark the file as %config because the user would want to change timing. This example comes from the package awstats:

Source4:      cron.d.awstats
[...]

%install
[...]
install -d -m 755 $RPM_BUILD_ROOT/etc/cron.d
install -m 700 %{SOURCE4} $RPM_BUILD_ROOT/etc/cron.d/awstats
[...]

%files
[...]
%config /etc/cron.d/awstats

The config file /etc/cron.d/awstats looks like:

#update reports every 6 hour
0    */6 * * *     root  /usr/sbin/awstats-update

A very interesting solution is used in the package mailman. There, the cron config file is already included in the original sources and is installed by make install. The difference is that the configuration is not used by default. It is installed as /usr/lib/mailman/cron/crontab and copied to /etc/init.d when the service is started and removed when the service is stopped. This is because the cron jobs are part of the functions provided by the service and they should not be provided when the service is stopped.

Here is the related part from the init script /etc/init.d/mailman:

ETC_CT=/etc/cron.d/mailman
MM_CT=/usr/lib/mailman/cron/crontab
MM_CTRL=/usr/lib/mailman/bin/mailmanctl
MM_PID=/var/lib/mailman/data/master-qrunner.pid
[...]

case "$1" in
    start)
        echo -n "Starting mailman"
        if ! /sbin/checkproc -k -p $MM_PID /usr/bin/python; then
                install -m 0644 $MM_CT $ETC_CT 
                $MM_CTRL --quiet --stale-lock-cleanup start 
        else
                rc_reset
        fi
        rc_status -v
        ;;
    stop)
        echo -n "Shutting down mailman"
        rm -f $ETC_CT
        /sbin/killproc -p $MM_PID -TERM /usr/bin/python
        rc_status -v
        ;;

Here are some parts of the config file that is copied to /etc/cron.d:

#
# if you want to make changes to this file, please modify 
# /usr/lib/mailman/cron/crontab and restart mailman
#
# At 8AM every day, mail reminders to admins as to pending
# requests. They are less likely to ignore these reminders if
# they're mailed early in the morning, but of course, this is
# local time... ;)
0 8 * * * mailman /usr/bin/python -S /usr/lib/mailman/cron/checkdbs
#
# At 9AM, send notifications to disabled members that are due
# to be reminded to re-enable their accounts.
0 9 * * * mailman /usr/bin/python -S /usr/lib/mailman/cron/disabled
[...]
# At 3:27am every night, regenerate the gzip'd archive file.  Only
# turn this on if the internal archiver is used and
# GZIP_ARCHIVE_TXT_FILES is false in mm_cfg.py
27 3 * * * mailman /usr/bin/python -S /usr/lib/mailman/cron/nightly_gzip
[...]