#!/bin/sh
#
# check_system_crontabs
#
# Script to check and see if any system (f)crontabs have changed, and if so 
# build a fcrontab file from /etc/crontab, /etc/fcrontab and files from
# /etc/cron.d/ and notify fcron about the change.
#
# WARNING : - if you run this script, your system fcrontab will be overridden
#             by the content of /etc/crontab, /etc/fcrontab, /etc/cron.d :
#             save the content of your system fcrontab first if necessary.
#
#           - you should not have the same lines in /etc/crontab
#             and /etc/fcrontab, in which case the jobs would get run twice.
#             (/etc/crontab may for example be used for Vixie-cron compatible
#              lines, and /etc/fcrontab for the other ones)
#
#           - you must understand that the contents of all the mentionned
#             files will go to a single file : the system fcrontab.
#             This means that you should pay attention to the global option
#             settings and environment variable assignments, which may
#             affect the other files too while you thought it wouldn't.
#             
# This script was originally created for a Debian server. A number of
# Debian packages like to drop files in /etc/cron.d/ and it would be nice
# to have something automatically create a system fcrontab file and notify
# fcron when those files change, so the system administrator doesn't have
# to try and keep up with it all manually.
#
# It is planned that such feature is integreated directly in fcron
# in the future (in a cleaner and more efficient way). Until then,
# this script should be useful.
#
# I recommend running this script using dnotify or a similar program
# (dnotify wait for a change in a file or a directory, and run a command
# when it happens), with a something like that:
# dnotify -b -p 1 -q 0 -MCDR /etc /etc/cron.d -e /usr/local/sbin/check_system_crontabs
# in your boot scripts.
#
# If you can't use dnotify, you should run this script from the system fcrontab
# with a line like this:
#
# @ 1 /usr/local/sbin/check_system_crontabs
#
# and set SLEEP_TIME_BEFORE_REBUILD to 0.
#
# Changelog
# =========
# Date        Author             Description
# ----        ------             -----------
# 2004/11/12  maasj@dm.org       Original version
# 2005/02/24  Thibault Godouet   Modified to be used with dnotify 
#                                + bug fixes and enhancement.
# 2005/04/27  Daniel Himler      Security enhancements and cleanups.
#

CROND_DIR=/etc/cron.d
FCRONTAB_PROG=/usr/bin/fcrontab
CRONTAB_FILE=/etc/crontab
FCRONTAB_FILE=/etc/fcrontab
if [ -x `type -p mktemp` ]; then
	FCRONTAB_FILE_TMP=`mktemp /tmp/fcrontab.XXXXXX`
else
	FCRONTAB_FILE_TMP=/tmp/fcrontab.$$
fi
FCRONTABS_DIR=/var/spool/fcron
SLEEP_TIME_BEFORE_REBUILD=30

# Function to build up a system crontab and tell fcron it's changed
rebuild_and_notify()
{
  logger -i -p cron.notice -t "check_system_crontabs" "Rebuilding the system fcrontab..."

  # put a warning message at the top of the file
  echo -e "########################################" > $FCRONTAB_FILE_TMP
  echo -e "# WARNING!!!  DO NOT EDIT THIS FILE!!! #" >> $FCRONTAB_FILE_TMP
  echo -e "########################################" >> $FCRONTAB_FILE_TMP
  echo -e "# Do not edit this file!  It is automatically generated from" >> $FCRONTAB_FILE_TMP
  echo -e "# the $CRONTAB_FILE, the $FCRONTAB_FILE and $CROND_DIR/* files whenever one of" >> $FCRONTAB_FILE_TMP
  echo -e "# those files is changed.\n#\n\n" >> $FCRONTAB_FILE_TMP

  # include the standard system crontab file if it is not a symbolic link
  if [ ! -L $CRONTAB_FILE ]; then
    echo -e "\n\n########################################\n# $CRONTAB_FILE\n########################################\n" >> $FCRONTAB_FILE_TMP
    cat $CRONTAB_FILE >> $FCRONTAB_FILE_TMP
  fi

  # print a nice filename header for each file in /etc/cron.d/
  # and include its contents into the new fcron system crontab
  for i in $CROND_DIR/* ; do
    if [ $i != "RCS" ] ; then
      echo -e "\n\n########################################\n# $i\n########################################\n" >> $FCRONTAB_FILE_TMP
      cat $i >> $FCRONTAB_FILE_TMP
    fi
  done

  # include the system fcrontab file if it is not a symbolic link
  if [ ! -L $FCRONTAB_FILE ]; then
    echo -e "\n\n########################################\n# $FCRONTAB_FILE\n########################################\n" >> $FCRONTAB_FILE_TMP
    cat $FCRONTAB_FILE >> $FCRONTAB_FILE_TMP
  fi

  # Replace "@hourly" style Vixie cron extensions which fcron doesn't parse
  sed -i -e "s/@yearly/0 0 1 1 */g" -e "s/@annually/0 0 1 1 */g" -e "s/@monthly/0 0 1 * */g" -e "s/@weekly/0 0 * * 0/g" -e "s/@daily/0 0 * * */g" -e "s/@midnight/0 0 * * */g" -e "s/@hourly/0 * * * */g" $FCRONTAB_FILE_TMP

  # notify fcron about the updated file
  $FCRONTAB_PROG $FCRONTAB_FILE_TMP -u systab
}

NEED_REBUILD=0

# change to the directory of cron files
cd $CROND_DIR

# We don't want the system fcrontab to be generated every few seconds
# if the sys admin is working in /etc/ (we have probably been called
# because of a change in /etc, but it may be a file not related to fcron),
# so we sleep so as to avoid this script be run too often.
sleep $SLEEP_TIME_BEFORE_REBUILD

# This test works for file creation/deletion (deletion is not detected
# by the next test)
if [ $CROND_DIR -nt $FCRONTABS_DIR/systab.orig ]; then

  NEED_REBUILD=1

else

  # Test each one and see if it's newer than our timestamp file
  for i in $CROND_DIR/* ; do
    if [ $i != "RCS" ]; then
      if [ $i -nt $FCRONTABS_DIR/systab.orig ]; then

        NEED_REBUILD=1

      fi
    fi
  done

fi

# Test the standard /etc/crontab file and see if it has changed
if [ $NEED_REBUILD -eq 0 -a \( $CRONTAB_FILE -nt $FCRONTABS_DIR/systab.orig -o $FCRONTAB_FILE -nt $FCRONTABS_DIR/systab.orig \) ]; then

  NEED_REBUILD=1

fi

if [ $NEED_REBUILD -eq 1 ]; then

  rebuild_and_notify

fi

rm -f $FCRONTAB_FILE_TMP
