1#!/bin/bash
2#
3#
4#
5# IMPORTANT NOTE: THIS SCRIPT IS TO BE REPLACED BY THE MORE PORTABLE
6#                 check_system_crontabs.sh.
7#                 It is only kept here until we are confident the non-bash
8#                 version is stable.
9#
10#
11#
12# check_system_crontabs.bash
13#
14# Script to check and see if any system (f)crontabs have changed, and if so
15# build a fcrontab file from /etc/crontab, /etc/fcrontab and files from
16# /etc/cron.d/ and notify fcron about the change.
17#
18# WARNING : - if you run this script, your system fcrontab will be overridden
19#             by the content of /etc/crontab, /etc/fcrontab, /etc/cron.d :
20#             save the content of your system fcrontab first if necessary.
21#
22#           - you should not have the same lines in /etc/crontab
23#             and /etc/fcrontab, in which case the jobs would get run twice.
24#             (/etc/crontab may for example be used for Vixie-cron compatible
25#              lines, and /etc/fcrontab for the other ones)
26#
27#           - you must understand that the contents of all the mentionned
28#             files will go to a single file : the system fcrontab.
29#             This means that you should pay attention to the global option
30#             settings and environment variable assignments, which may
31#             affect the other files too while you thought it wouldn't.
32#
33# This script was originally created for a Debian server. A number of
34# Debian packages like to drop files in /etc/cron.d/ and it would be nice
35# to have something automatically create a system fcrontab file and notify
36# fcron when those files change, so the system administrator doesn't have
37# to try and keep up with it all manually.
38#
39# It is planned that such feature is integreated directly in fcron
40# in the future (in a cleaner and more efficient way). Until then,
41# this script should be useful.
42#
43# I recommend running this script using dnotify or a similar program
44# (dnotify wait for a change in a file or a directory, and run a command
45# when it happens), with a something like that:
46# dnotify -b -p 1 -q 0 -MCDR /etc /etc/cron.d -e /usr/local/sbin/check_system_crontabs
47# in your boot scripts.
48#
49# Because we don't want the system fcrontab to be generated every few seconds
50# if the sys admin is working in /etc/ (the script has probably been called
51# by dnotify because of a change in /etc, but it may be a file not related
52# to fcron), the script will by default sleep so as to avoid being run too
53# often. The default sleep time is 30 seconds, and can be adjusted by changing
54# DEFAULT_SLEEP_TIME_BEFORE_REBUILD below, or by passing -s X, where X is
55# the number of seconds to sleep.  X can be 0.
56#
57# If you can't use dnotify, you should run this script from the system fcrontab
58# with a line like this:
59#
60# @ 1 /usr/local/sbin/check_system_crontabs -s 0
61#
62# To force an immediate rebuild at the commandline try:
63#   check_system_crontabs -f -i
64#
65# For more help on command-line options:
66#   check_system_crontabs -h
67#
68# Changelog
69# =========
70# Date        Author             Description
71# ----        ------             -----------
72# 2004/11/12  maasj@dm.org       Original version
73# 2005/02/24  Thibault Godouet   Modified to be used with dnotify
74#                                + bug fixes and enhancement.
75# 2005/04/27  Daniel Himler      Security enhancements and cleanups.
76# 2005/09/14  Damon Harper       Command lines options, cleanups.
77#
78
79##############################
80# DEFAULT CONFIGURATION
81
82DEFAULT_SLEEP_TIME_BEFORE_REBUILD=30
83DEFAULT_CROND_DIR=/etc/cron.d
84DEFAULT_CRONTAB_FILE=/etc/crontab
85DEFAULT_FCRONTAB_FILE=/etc/fcrontab
86
87FCRONTAB_PROG=/usr/bin/fcrontab
88FCRONTABS_DIR=/var/spool/fcron
89
90# END OF DEFAULT CONFIGURATION
91##############################
92
93FCRONTAB_FILE_TMP=
94
95cleanup() {
96  # remove temporary file (if any)
97  [ -e "$FCRONTAB_FILE_TMP" ] && rm -f $FCRONTAB_FILE_TMP
98}
99trap "eval cleanup" INT TERM HUP
100
101if [ -x "`type -p mktemp`" ]; then
102	FCRONTAB_FILE_TMP=`mktemp /tmp/fcrontab.XXXXXX`
103else
104	FCRONTAB_FILE_TMP=/tmp/fcrontab.$$
105fi
106
107info() {
108  [ -n "$VERBOSE" ] && echo "$@" >&2
109}
110
111die() {
112  echo check_system_crontabs: "$@" >&2
113  echo Try check_system_crontabs -h for help. >&2
114  exit 1
115}
116
117usage() {
118  cat <<_EOF_ >&2
119Description: Rebuild the systab file from various system crontabs.
120Usage: check_system_crontabs [options]
121  OPTIONS:
122    -v          Verbose; tell what is happening.
123    -f          Force rebuild, even if no changes are found.
124    -s SECONDS  Sleep for SECONDS before rebuilding.
125                Default: $DEFAULT_SLEEP_TIME_BEFORE_REBUILD seconds.
126    -i          Interactive use with no delay; same as -s 0.
127    -p PATHNAME Full path to or filename of the fcrontab binary; use this
128                only if it cannot be found automatically.
129    -F FILE     System fcrontab file (default $DEFAULT_FCRONTAB_FILE).
130    -C FILE     System crontab file (default $DEFAULT_CRONTAB_FILE).
131    -D DIR      System crontab directory (default $DEFAULT_CROND_DIR).
132    -h          This help text.
133_EOF_
134  exit
135}
136
137SLEEP_TIME_BEFORE_REBUILD="$DEFAULT_SLEEP_TIME_BEFORE_REBUILD"
138CROND_DIR="$DEFAULT_CROND_DIR"
139CRONTAB_FILE="$DEFAULT_CRONTAB_FILE"
140FCRONTAB_FILE="$DEFAULT_FCRONTAB_FILE"
141FCRONTAB_PROG=
142VERBOSE=
143FORCE=
144
145# read command line arguments
146while [ "$#" -gt 0 ]; do
147  case "$1" in
148  -v)
149    VERBOSE=true
150    ;;
151  -f)
152    FORCE=true
153    ;;
154  -s)
155    SLEEP_TIME_BEFORE_REBUILD="$2"
156    shift
157    ;;
158  -i)
159    SLEEP_TIME_BEFORE_REBUILD=0
160    ;;
161  -p)
162    FCRONTAB_PROG="$2"
163    shift
164    ;;
165  -F)
166    FCRONTAB_FILE="$2"
167    shift
168    ;;
169  -C)
170    CRONTAB_FILE="$2"
171    shift
172    ;;
173  -D)
174    CROND_DIR="$2"
175    shift
176    ;;
177  -h)
178    usage
179    ;;
180  *)
181    die "Invalid option: $1!"
182    ;;
183  esac
184  shift
185done
186
187# find fcrontab executable path
188if [ -n "$FCRONTAB_PROG" ]; then
189  [ -d "$FCRONTAB_PROG" ] && FCRONTAB_PROG="$FCRONTAB_PROG/fcrontab"
190  [ ! -x "$FCRONTAB_PROG" ] && die "Invalid fcrontab executable or path specified with -p!"
191else
192  if [ -x "`type -p fcrontab`" ]; then
193    FCRONTAB_PROG="`type -p fcrontab`"
194  elif [ -x /usr/bin/fcrontab ]; then
195    FCRONTAB_PROG=/usr/bin/fcrontab
196  elif [ -x /usr/local/bin/fcrontab ]; then
197    FCRONTAB_PROG=/usr/local/bin/fcrontab
198  else
199    die "Unable to locate fcrontab binary! Specify with -p."
200  fi
201fi
202
203# sanity check
204if [ -z "$CROND_DIR" -o -z "$CRONTAB_FILE" -o -z "$FCRONTAB_FILE" ]; then
205  die "Must specify all system crontab files."
206fi
207
208# Function to scan for valid files in $CROND_DIR
209crond_files()
210{
211  [ ! -d $CROND_DIR ] && return
212  local FILES=`echo $CROND_DIR/*`
213  local FILE
214  [ "$FILES" = "$CROND_DIR/*" ] && return
215  for FILE in $FILES; do
216    if [ ! -d $FILE -a $FILE = "${FILE%\~}" ]; then
217      echo $FILE
218    fi
219  done
220}
221
222
223# Function to build up a system crontab and tell fcron it's changed
224rebuild_and_notify()
225{
226  logger -i -p cron.notice -t "check_system_crontabs" "Rebuilding the system fcrontab..."
227
228  # put a warning message at the top of the file
229  echo -e "########################################" > $FCRONTAB_FILE_TMP
230  echo -e "# WARNING!!!  DO NOT EDIT THIS FILE!!! #" >> $FCRONTAB_FILE_TMP
231  echo -e "########################################" >> $FCRONTAB_FILE_TMP
232  echo -e "# Do not edit this file!  It is automatically generated from" >> $FCRONTAB_FILE_TMP
233  echo -e "# the $CRONTAB_FILE, the $FCRONTAB_FILE and $CROND_DIR/* files whenever one of" >> $FCRONTAB_FILE_TMP
234  echo -e "# those files is changed.\n#\n\n" >> $FCRONTAB_FILE_TMP
235
236  # include the standard system crontab file if it exists and is not a symbolic link
237  if [ -f $CRONTAB_FILE -a ! -L $CRONTAB_FILE ]; then
238    echo -e "\n\n########################################\n# $CRONTAB_FILE\n########################################\n" >> $FCRONTAB_FILE_TMP
239    cat $CRONTAB_FILE >> $FCRONTAB_FILE_TMP
240  fi
241
242  # print a nice filename header for each file in /etc/cron.d/
243  # and include its contents into the new fcron system crontab
244  for i in `crond_files` ; do
245    echo -e "\n\n########################################\n# $i\n########################################\n" >> $FCRONTAB_FILE_TMP
246    cat $i >> $FCRONTAB_FILE_TMP
247  done
248
249  # include the system fcrontab file if it exists and is not a symbolic link
250  if [ -f $FCRONTAB_FILE -a ! -L $FCRONTAB_FILE ]; then
251    echo -e "\n\n########################################\n# $FCRONTAB_FILE\n########################################\n" >> $FCRONTAB_FILE_TMP
252    cat $FCRONTAB_FILE >> $FCRONTAB_FILE_TMP
253  fi
254
255  # Replace "@hourly" style Vixie cron extensions which fcron doesn't parse
256  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
257
258  # notify fcron about the updated file
259  $FCRONTAB_PROG $FCRONTAB_FILE_TMP -u systab
260}
261
262NEED_REBUILD=0
263
264# by default, sleep to avoid too numerous executions of this script by dnotify.
265if [ -n "$SLEEP_TIME_BEFORE_REBUILD" -a "$SLEEP_TIME_BEFORE_REBUILD" != 0 ]; then
266  if ! sleep $SLEEP_TIME_BEFORE_REBUILD; then
267    # sleep time was invalid or sleep interrupted by signal!
268    cleanup
269    exit 1
270  fi
271fi
272
273# First check if we're forcing a rebuild:
274if [ -n "$FORCE" ]; then
275
276  NEED_REBUILD=1
277
278else
279
280  if [ -d $CROND_DIR ]; then
281
282    # This test works for file creation/deletion (deletion is not detected
283    # by the next test)
284    if [ $CROND_DIR -nt $FCRONTABS_DIR/systab.orig ]; then
285
286      info "Changes detected in $CROND_DIR"
287      NEED_REBUILD=1
288
289    else
290
291      # Test each one and see if it's newer than our timestamp file
292      for i in `crond_files` ; do
293        if [ $i -nt $FCRONTABS_DIR/systab.orig ]; then
294
295          info "Changes detected in $CROND_DIR"
296          NEED_REBUILD=1
297
298        fi
299      done
300
301    fi
302
303  fi
304
305  # Test the standard /etc/crontab file and see if it has changed
306  if [ -f $CRONTAB_FILE -a $CRONTAB_FILE -nt $FCRONTABS_DIR/systab.orig ]; then
307
308    info "Changes detected in $CRONTAB_FILE"
309    NEED_REBUILD=1
310
311  fi
312
313  # Test the standard /etc/fcrontab file and see if it has changed
314  if [ -f $FCRONTAB_FILE -a $FCRONTAB_FILE -nt $FCRONTABS_DIR/systab.orig ]; then
315
316    info "Changes detected in $FCRONTAB_FILE"
317    NEED_REBUILD=1
318
319  fi
320
321fi
322
323if [ $NEED_REBUILD -eq 1 ]; then
324
325  info "Rebuilding fcron systab."
326  rebuild_and_notify
327
328elif [ -n "$VERBOSE" ]; then
329
330  info "Not rebuilding fcron systab; no changes found."
331
332fi
333
334cleanup
335