305 lines
9.1 KiB
Bash
305 lines
9.1 KiB
Bash
#!/bin/bash
|
|
#
|
|
# /usr/libexec/cron/run-crons
|
|
#
|
|
# Copyright (c) 1998-2001 SuSE GmbH Nuernberg, Germany. All rights reserved.
|
|
#
|
|
# this script looks into /etc/cron.{hourly,daily,weekly,monthly} for
|
|
# scripts to be executed. The info about last run is stored in
|
|
# /var/spool/cron/lastrun
|
|
#
|
|
# concept similar to debian and redhat
|
|
#
|
|
# Changes:
|
|
# 1998 - Burchard Steinbild <bs@suse.de>, 1998
|
|
# initial version
|
|
# before 2001 - va@org.chemie.uni-frankfurt.de
|
|
# send an email with name of date-script instead of cron entry
|
|
# "Subject: cronjob@www - daily - FAILURE"
|
|
# (better one script for each date-sub-script)
|
|
# requires changes to /etc/crontab
|
|
# append > /dev/null 2>&1 to the line calling run-cons
|
|
# 2001-09-11
|
|
# updated to Suse 7.2 merged
|
|
# 2001-09-12
|
|
# changed FAILURE detection, until now all scripts with output
|
|
# had "failed", now only scripts with error status != 0
|
|
# have failed.
|
|
# 2001-09-13 - ro@suse.de
|
|
# merged with 7.3: call logger with exit value for scripts
|
|
# respect MAILTO as cron does
|
|
# use mktemp -d for all tmpfiles
|
|
# add variable to disable mail if all jobs returned 0
|
|
# 2015-06-25 - jmatejek@suse.com
|
|
# bnc#812367 support MAILFROM as cron does
|
|
# 2016-08-08 - tchvatal@suse.com
|
|
# bnc#983925 run crons even on battery
|
|
# 2017-10-24 - jsegitz@suse.de
|
|
# bsc#1062722 - harden run-cron to ensure correct directory permissions
|
|
|
|
if [ -f /etc/sysconfig/cron ]; then
|
|
. /etc/sysconfig/cron
|
|
fi
|
|
|
|
BASENAME=`/usr/bin/basename $0`
|
|
LOGGER="/bin/logger -t $BASENAME[$$]"
|
|
|
|
export LC_TIME=POSIX
|
|
TMPDIR=`mktemp -d /tmp/run-crons.XXXXXX`
|
|
trap "rm -rf $TMPDIR" 0 1 2 3 13 15
|
|
|
|
# We will force to run cron.daily after 14 days, even
|
|
# if you set MAX_NOT_RUN in /etc/sysconfig/cron
|
|
# value is in minutes
|
|
MAX_NOT_RUN_FORCE="20160"
|
|
|
|
# Priority change for sub scripts.
|
|
# range: highest -20 ... 19 lowest prioriy
|
|
# default processes start in level 10
|
|
CRON_SCRIPT_NICE_VALUE=15
|
|
SPOOL=/var/spool/cron/lastrun
|
|
|
|
# CRON Result EMail is sent to
|
|
if [ -z "$MAILTO" ]; then
|
|
SEND_TO="root"
|
|
else
|
|
SEND_TO="$MAILTO"
|
|
fi
|
|
|
|
if [ -z "$MAILFROM" ]; then
|
|
SEND_FROM="root"
|
|
else
|
|
SEND_FROM="$MAILFROM"
|
|
fi
|
|
|
|
# XXX support external specification of $MAILER?
|
|
for POSSIBLE_MAILER in /usr/bin/mail /usr/lib/sendmail /usr/bin/mailx /usr/sbin/sendmail; do
|
|
test -x $POSSIBLE_MAILER && MAILER=$POSSIBLE_MAILER
|
|
done
|
|
if [ -z "$MAILER" ]; then
|
|
echo "Could not find suitable mailer."
|
|
exit 1
|
|
fi
|
|
|
|
export MAIL_CONFIG
|
|
export MAILER
|
|
|
|
function send_email() {
|
|
SUBJECT="$1"; shift
|
|
TMP=`mktemp`
|
|
echo "Subject: $SUBJECT" > "$TMP"
|
|
echo "From: $SEND_FROM" >> "$TMP"
|
|
echo "To: $SEND_TO" >> "$TMP"
|
|
echo >> "$TMP"
|
|
cat "$@" >> "$TMP"
|
|
"$MAILER" -r "$SEND_FROM" "$SEND_TO" < "$TMP"
|
|
rm -f "$TMP"
|
|
}
|
|
|
|
mkdir -p $SPOOL
|
|
|
|
#set verbose
|
|
## stage 1, search directories/scripts to run
|
|
RUN=""
|
|
SECURE_PERMISSIONS="${SECURE_DIR_PERMISSIONS:-755}"
|
|
for CRONDIR in /etc/cron.{hourly,daily,weekly,monthly} ; do
|
|
test -d $CRONDIR || continue
|
|
# this is racy but better than nothing
|
|
if [ ! "$ENFORCE_ROOT_OWNER_GROUP_DIR" = "no" ] && [ ! -O $CRONDIR -o ! -G $CRONDIR ]; then
|
|
echo "wrong owner/group for $CRONDIR, skipping" | logger
|
|
continue
|
|
fi
|
|
ACTUAL_PERMISSIONS=$(stat -c %a $CRONDIR)
|
|
# to have this default to false would be better, but would require a more
|
|
# complicated logic in the loop
|
|
PERMISSIONS_ARE_SECURE=true
|
|
for (( i=0; i<${#ACTUAL_PERMISSIONS}; i++ )); do
|
|
if [ "${ACTUAL_PERMISSIONS:$i:1}" -gt "${SECURE_PERMISSIONS:$i:1}" ]; then
|
|
PERMISSIONS_ARE_SECURE=false
|
|
fi
|
|
done
|
|
if [ ! "$PERMISSIONS_ARE_SECURE" = true ]; then
|
|
echo "wrong permissions $ACTUAL_PERMISSIONS for $CRONDIR, expecting $SECURE_PERMISSIONS. Skipping" | logger
|
|
continue
|
|
fi
|
|
|
|
BASE=${CRONDIR##*/}
|
|
TIME_EXT=${BASE##cron.}
|
|
|
|
test -e $SPOOL/$BASE && {
|
|
case $BASE in
|
|
cron.hourly) TIME="-cmin +60 -or -cmin 60" ;;
|
|
cron.daily)
|
|
# if DAILY_TIME set, run only at a fixed time of day
|
|
if [ "$DAILY_TIME" != "" ] ; then
|
|
DAILY_TIME_NEW="`echo $DAILY_TIME | sed s,:,, | sed s,^0\*,, `"
|
|
test -z "$DAILY_TIME_NEW" && DAILY_TIME_NEW=0
|
|
if [ "$DAILY_TIME_NEW" -gt "2359" ] ; then
|
|
echo "wrong time format in /etc/sysconfig/cron DAILY_TIME, value is $DAILY_TIME" | logger
|
|
fi
|
|
NOW_H=`date +%H%M| sed s,^0\*,,`
|
|
test -z "$NOW_H" && NOW_H=0
|
|
if [ $DAILY_TIME_NEW -gt $(($NOW_H-15)) ] && [ $DAILY_TIME_NEW -le $NOW_H ]; then
|
|
TIME=""
|
|
else
|
|
# take care of MAX_NOT_RUN, default is 7 days
|
|
if [ "$MAX_NOT_RUN" != "0" ] ; then
|
|
TIME="-cmin +$((1440*$MAX_NOT_RUN)) -or -cmin $((1440*$MAX_NOT_RUN))"
|
|
else
|
|
TIME="-cmin +$MAX_NOT_RUN_FORCE -or -cmin $MAX_NOT_RUN_FORCE"
|
|
fi
|
|
fi
|
|
|
|
# run as usual
|
|
else
|
|
TIME="-cmin +1440 -or -cmin 1440"
|
|
fi ;;
|
|
cron.weekly) TIME="-cmin +10080 -or -cmin 10080" ;;
|
|
cron.monthly)
|
|
DAYOFMONTH=`date '+%d'`
|
|
DAYSLASTMONTH=`date -d "-$DAYOFMONTH days" '+%d'`
|
|
if [ $DAYOFMONTH -gt $DAYSLASTMONTH ] ; then
|
|
LASTMONTHSTR="-$DAYOFMONTH days"
|
|
else
|
|
LASTMONTHSTR="last month"
|
|
fi
|
|
NOW=`date +%s`
|
|
LASTMONTH=`date -d "$LASTMONTHSTR" +%s`
|
|
DIFF=`expr '(' $NOW - $LASTMONTH ')' / 86400`
|
|
TIME="-ctime +$DIFF"
|
|
;;
|
|
esac
|
|
# remove all lock files for scripts that are due to run
|
|
eval find $SPOOL/$BASE $TIME | \
|
|
xargs --no-run-if-empty rm
|
|
}
|
|
if test ! -e $SPOOL/$BASE ; then
|
|
# accept this dir, if it isn't empty
|
|
LIST=`find $CRONDIR ! -type d`
|
|
if [ ! -z "$LIST" ] ; then
|
|
RUN="${RUN} ${TIME_EXT}"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
|
|
## STATUS communication variable between
|
|
# function run_scripts ()
|
|
# and loop-over-all-scripts
|
|
# set in run_scripts to FAILURE if this script failed!
|
|
# else it is empty
|
|
# because it is never reset to empty after the initialization
|
|
# it implements an OR like logic over all scripts
|
|
##
|
|
STATUS=""
|
|
|
|
# helper, run all scripts in one cron directory
|
|
function run_scripts (){
|
|
local CRONDIR=$1
|
|
local TIME_EXT=$2
|
|
|
|
|
|
local TEMP_MSG=$TMPDIR/run-crons.${TIME_EXT}.$$
|
|
rm -r $TMPDIR/run-crons.${TIME_EXT}.* >/dev/null 2>&1
|
|
|
|
# keep going when something fails
|
|
set +e
|
|
for SCRIPT in $CRONDIR/* ; do
|
|
test -d $SCRIPT && continue
|
|
case "$SCRIPT" in
|
|
.svn) continue ;;
|
|
*.rpm*) continue ;;
|
|
*.swap) continue ;;
|
|
*.bak) continue ;;
|
|
*.orig) continue ;;
|
|
\#*) continue ;;
|
|
*~) continue ;;
|
|
esac
|
|
if test -x $SCRIPT ; then
|
|
BASESCRIPT=`/usr/bin/basename $SCRIPT`
|
|
|
|
nice -n ${CRON_SCRIPT_NICE_VALUE} $SCRIPT >$TEMP_MSG 2>&1
|
|
|
|
local ERRNO=$?
|
|
if [ 0 -eq $ERRNO ] ; then
|
|
if [ "$SYSLOG_ON_NO_ERROR" = "yes" ]; then
|
|
echo "$BASESCRIPT: OK" | $LOGGER -p info
|
|
fi
|
|
else
|
|
echo "$BASESCRIPT returned $ERRNO" | $LOGGER -p warn
|
|
echo "SCRIPT: $BASESCRIPT exited with RETURNCODE = $ERRNO."
|
|
STATUS="FAILURE"
|
|
fi
|
|
# write some wrapper text around the original output
|
|
if [ -s "$TEMP_MSG" ] ; then
|
|
echo "SCRIPT: output (stdout && stderr) follows"
|
|
echo ""
|
|
cat $TEMP_MSG
|
|
echo -e "SCRIPT: $BASESCRIPT\n------- END OF OUTPUT"
|
|
echo ""
|
|
echo ""
|
|
fi
|
|
|
|
rm -f $TEMP_MSG > /dev/null 2>&1
|
|
else
|
|
echo "WARNING: $SCRIPT is not executable, script is ignored !"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# stage 2:
|
|
# run all scripts and collect output into one mail
|
|
# for each TIME_EXT with a meaningfull subject.
|
|
#
|
|
if [ ! -z "${RUN}" ] ; then
|
|
for EXT in ${RUN} ; do
|
|
CRONDIR="/etc/cron."${EXT}
|
|
test -d $CRONDIR || continue
|
|
BASE=`/usr/bin/basename $CRONDIR`
|
|
TIME_EXT=${BASE##cron.}
|
|
STATUS=""
|
|
|
|
if test ! -e $SPOOL/$BASE ; then
|
|
CONTROL_MAIL=$TMPDIR/run-crons_mail.$$
|
|
JOB_OUTPUT=$TMPDIR/run-crons_output.$$
|
|
|
|
echo "running ${TIME_EXT} cronjob scripts" >> ${CONTROL_MAIL}
|
|
echo "" >> ${CONTROL_MAIL}
|
|
|
|
touch $SPOOL/$BASE
|
|
|
|
run_scripts ${CRONDIR} ${TIME_EXT} >> ${JOB_OUTPUT} 2>&1
|
|
TITLE="cronjob@$HOSTNAME - ${TIME_EXT}"
|
|
if [ -n "${STATUS}" ] ; then
|
|
TITLE="${TITLE} - ${STATUS}"
|
|
else
|
|
TITLE="${TITLE} - OK"
|
|
fi
|
|
if [ -n "${STATUS}" -o "$SEND_MAIL_ON_NO_ERROR" = "yes" ] ; then
|
|
send_email "$TITLE" "$CONTROL_MAIL" "$JOB_OUTPUT"
|
|
elif [ -s ${JOB_OUTPUT} -a "$SEND_OUTPUT_ON_NO_ERROR" = "yes" ] ; then
|
|
send_email "$TITLE" "$CONTROL_MAIL" "$JOB_OUTPUT"
|
|
fi
|
|
|
|
rm -f ${CONTROL_MAIL} ${JOB_OUTPUT}
|
|
fi
|
|
done
|
|
fi
|
|
|
|
#
|
|
# now make sure, we have no lastrun files dated to future
|
|
#
|
|
touch $SPOOL
|
|
NOW=`date -u +%s`
|
|
for i in `find $SPOOL -type f`
|
|
do
|
|
FILEDATE=`date -u -r $i +%s`
|
|
# allow for up to one hour in the future because of summer/wintertime
|
|
if [ $((FILEDATE - NOW)) -gt 3600 ]
|
|
then
|
|
rm $i
|
|
fi
|
|
done
|
|
|
|
|