#!/bin/bash
#
# tuxis-cron is taken from bitcron, once written by Pim van Pelt <pim@ipng.nl>
# modifications by BIT employees (NOT TO BE DISTURBED). Please use
# support@tuxis.nl if you have questions regarding tuxis-cron
#
# See 'man 1 tuxis-cron' for documentation.
#
# You MUST NOT edit this file.
# You should be editing the cron definition file.
#


TUXIS_CRON_SEARCHPATH="/usr/share/tuxis-cron/cronscripts/ /etc/cronscripts/ ./"


fatal() 
{
    echo ""
    echo "===== FATAL ====="
    echo $1
    echo "===== FATAL ====="
    ERR=$(($ERR + 1))
    FATAL=1;
    func_end
}


silent_fatal()
{
    echo ""
    echo "===== FATAL (SILENCED) ====="
    echo $1
    echo "===== FATAL (SILENCED) ====="
    ERR=$(($ERR + 1))
    SILENT_ERR=$(($SILENT_ERR + 1))
    FATAL=1;
    func_end
}


error() 
{
    echo ""
    echo "===== ERROR ====="
    echo $1
    echo "===== ERROR ====="
    ERR=$(($ERR + 1))
}


warning() 
{
    echo ""
    echo "===== WARNING ====="
    echo $1
    echo "===== WARNING ====="
    WRN=$(($WRN + 1))
}

age()
{
    ONE=$(echo $1 | sed 's/^0*//')
    TWO=$(echo $2 | sed 's/^0*//')
    RES=$(($ONE-$TWO))

    if [ $RES -lt 0 ]; then
        RES=$(($RES+365))
    fi

    echo $RES
}

func_sendmail()
{
    # Send mail to MAILTO when there are no warnings and no errors, or only silent errors.
    if [ $WRN -eq 0 -a $ERR -eq 0 ] || [ $ERR -eq $SILENT_ERR -a $WRN -eq 0 ]; then
        if [ ! -z "$MAILTO" ]; then
            (
                echo "From: $HOSTNAME $NAME <`whoami`@$FQDN>"
                echo "To: $MAILTO"
                echo "Subject: [$FQDN] $TUXIS_PROGRAM_NAME $NAME - successful"
                echo "Date: `date '+%a, %d %b %Y %T %z'`"
                echo "Errors-To: $MAILTO"
                echo "Reply-To: $MAILTO"
                echo "X-Mailer: tuxis-cron "'$Revision$'
                echo "X-tuxis-cron: $NAME"
                echo "X-Precedence: automatic"
                echo ""
                echo "Dear reader,"
                echo ""
                echo "This is the $TUXIS_PROGRAM_NAME program on $HOSTNAME informing"
                echo "you I have successfully executed the $TUXIS_PROGRAM_NAME $NAME."
                echo ""
                echo "The log that I have for this cronjob follows:"
                cat $LOG
                echo ""
                echo "This mail is purely FYI, user intervention is not required."
                echo ""
                echo "-- "
                echo "Cheers,"
                echo "    $HOSTNAME"
            ) | /usr/sbin/sendmail -t
        fi
        if [ ! -z "$TUXIS_ERRORCOUNT" ]; then
            echo 0 > $ERRCOUNTF
        fi
        if [ ! -z "$TUXIS_MAILCOUNT" ]; then
            echo 0 > $MAILCOUNTF
        fi
    else
        SEND_ERR="YES"

        if [ ! -z "$TUXIS_ERRORCOUNT" ]; then
            ERRCOUNT=$((ERRCOUNT+1))
            echo $ERRCOUNT > $ERRCOUNTF
            if [ $ERRCOUNT -lt $TUXIS_ERRORCOUNT ]; then
                SEND_ERR="NO"
            fi
        fi

        if [ ! -z "$TUXIS_MAILCOUNT" ]; then
            MAILCOUNT=$((MAILCOUNT+1))
            echo $MAILCOUNT > $MAILCOUNTF
            if [ $MAILCOUNT -gt $TUXIS_MAILCOUNT ]; then
                SEND_ERR="NO"
            fi
        fi

        if [ "A$SEND_ERR" == "AYES" ]; then
            [ ! -z $ERRCOUNTF -a -r $ERRCOUNTF ] && rm $ERRCOUNTF
            (
                echo "From: $HOSTNAME $NAME <`whoami`@$FQDN>"
                echo "To: $ESCALATE_MAILTO"
                if [ ! -z "$MAILTO" ]; then
                    echo "Cc: $MAILTO"
                fi
                if [ $FATAL -eq 0 ]; then
                    echo "Subject: [$FQDN] $TUXIS_PROGRAM_NAME $NAME - $ERR error(s), $WRN warning(s)"
                else
                    echo "Subject: [$FQDN] $TUXIS_PROGRAM_NAME $NAME - FATAL - $ERR error(s), $WRN warning(s)"
                fi
                echo "Date: `date '+%a, %d %b %Y %T %z'`"
                echo "Errors-To: $ESCALATE_MAILTO"
                echo "Reply-To: $ESCALATE_MAILTO"
                echo "X-Mailer: tuxis-cron "'$Revision$'
                echo "X-Precedence: automatic"
                echo "X-tuxis-cron: $NAME"
                echo "X-Errors: $ERR"
                echo "X-Warnings: $WRN"
                echo ""
                echo "Dear reader,"
                echo ""
                echo "This is the $TUXIS_PROGRAM_NAME program on $HOSTNAME informing"
                echo "you that there was a problem running the $TUXIS_PROGRAM_NAME $NAME."
                echo ""
                echo "The logfile that lead up to this:"
                cat $LOG
                echo ""
                echo "I hope you can deal with this situation ASAP."
                echo ""
                echo "-- "
                echo "Cheers,"
                echo "    $HOSTNAME"
            ) | /usr/sbin/sendmail -t
        fi
    fi
}


func_begin()
{
    # If the process with PID 1 is not /sbin/init, it's safe to assume we're in
    # a namespace, which is when we usually need this option. If TUXIS_APPEND_RANDOM
    # is already set, don't override it.
    if [ -z "$TUXIS_APPEND_RANDOM" ]; then
        if [ "$(/bin/ps -o cmd p 1 --no-headers)" = "/sbin/init" ]; then
            TUXIS_APPEND_RANDOM="no"
        else
            TUXIS_APPEND_RANDOM="yes"
        fi
    fi

    if [ "$TUXIS_APPEND_RANDOM" = "yes" ]; then
        MASTERLOG_TMP_SUFFIX="TMP-$RANDOM.$$"
    else
        MASTERLOG_TMP_SUFFIX="TMP.$$"
    fi

    if [ -z "$MASTERLOG" ]; then
        LOG="/tmp/tuxis-cron-$NAME.$MASTERLOG_TMP_SUFFIX"
    else
        LOG="${MASTERLOG}.$MASTERLOG_TMP_SUFFIX"
    fi

    touch $LOG

    if [ "$PROTECTLOG" = "yes" ]; then
        chmod 0600 $LOG
    fi

    exec 3>&1 4>&2 1>$LOG 2>&1

    WRN=0
    ERR=0
    SILENT_ERR=0
    FATAL=0
    LOCKED=0
    FQDN=$(hostname -f)
    HOSTNAME=$(hostname -s)
    ERRCOUNT=0

    if [ "A`echo $FQDN | grep '\.'`" = "A" ]; then
        FQDN="${FQDN}.unknown.customers.tuxis.net"
    fi
    
    echo "Beginning $NAME cronjob at `date`"

    # Optional error counter.
    if [ ! -z "$TUXIS_ERRORCOUNT" ]; then
        ERRCOUNTF="/tmp/.tuxis-cron-$NAME.errcount"
        if [ -e "$ERRCOUNTF" ]; then
            ERRCOUNT=`cat $ERRCOUNTF`
        fi
    fi

    # Optional mail counter.
    if [ ! -z "$TUXIS_MAILCOUNT" ]; then
        MAILCOUNTF="/tmp/.tuxis-cron-$NAME.mailcount"
        if [ -e "$MAILCOUNTF" ]; then
            MAILCOUNT=`cat $MAILCOUNTF`
        fi
    fi

    # Set program name if not set in cron definition.
    if [ -z "$TUXIS_PROGRAM_NAME" ]; then
        TUXIS_PROGRAM_NAME="tuxis-cron"
    fi

    # Optional concurrent process locking.
    if [ ! -z "$TUXIS_CRONLOCK" ]; then
        if [ "$TUXIS_CRONLOCK" = "yes" ]; then
            echo "WARNING: You should set TUXIS_CRONLOCK to a path, not 'yes'."
        fi
        if [ -e "$TUXIS_CRONLOCK" ]; then
            PID=`cat "$TUXIS_CRONLOCK"`
            if kill -0 $PID >/dev/null 2>&1; then
                LOCKED=1
                
                dtnow=$(date +%s)
                dtthen=$(stat -c"%Y" "${TUXIS_CRONLOCK}")
                dtdiff=$(( $dtnow - $dtthen ))
                dtdays=$(( $dtdiff / 86400 ))
                if [ $dtdays -ge 1 ]; then
                    echo ""
                    echo "[!!]                                         [!!]"
                    echo "[!!]                                         [!!]"
                    echo "[!!] THIS JOB WAS LOCKED FOR $dtdays DAY(s)! [!!]"
                    echo "[!!]                                         [!!]"
                    echo "[!!]                                         [!!]"
                    echo ""
                fi

                fatal "$PID is still active. Stopping execution."
            else
                warning "Stale lockfile found? Process $PID seems inactive. Resuming."
            fi
        fi
        echo "Concurrency locking enabled: $TUXIS_CRONLOCK"
        echo $$ > "$TUXIS_CRONLOCK"
    fi

    echo ""
}


func_end()
{
    echo ""
    echo "Done with $NAME cronjob at `date`"
    echo ""
    echo "There were $ERR error(s) and $WRN warning(s)"
    if [ $FATAL -ne 0 ]; then
        echo "This script had a fatal error!"
    fi
    
    exec 1>&3 2>&4
    
    func_sendmail
    
    if [ ! -z "$MASTERLOG" ]; then
        if [ "$DATELOGS" = "yes" ]; then
            # cat current logfile to this date.log
            DATE=$(date +"%Y%m%d")
            DOY=$(date +"%j")
            DATE_SUFFIX="${DATE}_${DOY}"
            cat "$LOG" >> $MASTERLOG.$DATE_SUFFIX
            LOGDIR=$(dirname ${MASTERLOG})
            LOGFILE=$(basename ${MASTERLOG})

            KEEPLOGS=30
            PATTERN=".*/${LOGFILE}\.[0-9]+_[0-9]+\(\.gz\)?$"
            for lf in `find $LOGDIR -regex ${PATTERN} 2>/dev/null`; do
                FDOY=$(echo ${lf} | sed -e 's/.*[0-9]\+_\([0-9]\+\)\(.gz\)\?/\1/')
                if [ `age $DOY $FDOY` -gt $KEEPLOGS ]; then
                    rm -f -- ${lf} >/dev/null 2>&1
                elif [ `age $DOY $FDOY` -gt 2 ]; then
                    gzip $lf >/dev/null 2>&1
                fi
            done
        else
            # Rotate logs.
            if [ -e $MASTERLOG ]; then
                [ -e $MASTERLOG.9 ] && rm -f -- $MASTERLOG.9
                [ -e $MASTERLOG.8 ] && mv -f -- $MASTERLOG.8 $MASTERLOG.9
                [ -e $MASTERLOG.7 ] && mv -f -- $MASTERLOG.7 $MASTERLOG.8
                [ -e $MASTERLOG.6 ] && mv -f -- $MASTERLOG.6 $MASTERLOG.7
                [ -e $MASTERLOG.5 ] && mv -f -- $MASTERLOG.5 $MASTERLOG.6
                [ -e $MASTERLOG.4 ] && mv -f -- $MASTERLOG.4 $MASTERLOG.5
                [ -e $MASTERLOG.3 ] && mv -f -- $MASTERLOG.3 $MASTERLOG.4
                [ -e $MASTERLOG.2 ] && mv -f -- $MASTERLOG.2 $MASTERLOG.3
                [ -e $MASTERLOG.1 ] && mv -f -- $MASTERLOG.1 $MASTERLOG.2
                [ -e $MASTERLOG.0 ] && mv -f -- $MASTERLOG.0 $MASTERLOG.1
                [ -e $MASTERLOG ]   && mv -f -- $MASTERLOG   $MASTERLOG.0
            fi
            cp -f -- "$LOG" "$MASTERLOG"
        fi
    fi

    rm -f -- $LOG >/dev/null 2>&1

    # Do not remove the lockfile if this proces quits due to
    # it being locked by another process running. Stale lockfiles
    # are fixed by the locking-section in func_begin() above.
    if [ $LOCKED -eq 0 ]; then
        if [ ! -z "$TUXIS_CRONLOCK" ]; then
            rm -f -- "$TUXIS_CRONLOCK" >/dev/null 2>&1
        fi
    fi

    if [ ! -z $RETVAL ]; then
        exit $RETVAL
    elif [ $ERR -ne 0 ]; then
        exit 127
    elif [ $WRN -ne 0 ]; then
        exit 126
    fi

    exit 0
}


func_stdout_err()
{
    echo "tuxis-cron: $1"
    echo ""
    echo "Usage:"
    echo " user@host:~\$ tuxis-cron /path/to/my.cron"
    echo ""
    echo "Please read 'man 1 tuxis-cron'."
    exit 128
}


# Here goes!
if [ $# -lt 1 ]; then
    func_stdout_err "Missing tuxis-cron script"
fi

CRONDEFINITION=$1; shift;
#
# Search through paths if the cron definition was not an absolute path.
#
if [ ${CRONDEFINITION%%/*} ]; then
    for DIR in $TUXIS_CRON_SEARCHPATH; do
        if [ -r ${DIR}${CRONDEFINITION} ]; then
            break;
        fi
    done
else
    DIR=""
fi

if [ ! -r ${DIR}${CRONDEFINITION} ]; then
    func_stdout_err "Unreadable cron definition: ${DIR}${CRONDEFINITION}"
fi

. ${DIR}${CRONDEFINITION}

[ -z "$NAME" ] && func_stdout_err "tuxis-cron: Missing mandatory variable NAME in ${DIR}${CRONDEFINITION}"
[ -z "$AUTHOR" ] && func_stdout_err "tuxis-cron: Missing mandatory variable AUTHOR in ${DIR}${CRONDEFINITION}"
[ -z "$ESCALATE_MAILTO" ] && func_stdout_err "tuxis-cron: Missing mandatory variable ESCALATE_MAILTO in ${DIR}${CRONDEFINITION}"

# Check the name of the script, we don't want any errors because of invalid filenames (which are based on $NAME)
echo "$NAME" | egrep -qi '^[-_a-z0-9\.]+$'
[ $? -eq 0 ] || func_stdout_err "tuxis-cron: Invalid name '$NAME', please use names compatible with '^[-_a-z0-9]+$'"

func_begin
func_cron $*
func_end
