File run-crons of Package cronie

#!/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


openSUSE Build Service is sponsored by