HEX
Server: Apache
System: Linux info 3.0 #1337 SMP Tue Jan 01 00:00:00 CEST 2000 all GNU/Linux
User: u80650282 (6669564)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: //bin/uicron
#!/bin/sh

set -e

UICRON=${0##*/}

DEBUG=${UICRON_DEBUG:-0}
unset UICRON_DEBUG

usage() {
    cat <<EOF
Usage: ${0##*/} -c COMMAND_STRING

 COMMAND_STRING   commands to be passed to the executing shell

This is a cronjob wrapper to provide a combination of useful features,
such as:
 * locking (see runlock(1))
 * timeouts (see timeout(1))
 * start time randomization
 * improved exit/output behaviour (see chronic(1))

The wrapper behaves like a shell and may be set in the crontab SHELL
variable for a cronjob.

Behaviour is controlled via environment variables:
 * UICRON_LOCKING=on|off (default: on)
 * UICRON_TIMEOUT=<integer> (default: 600)
 * UICRON_DELAY=<integer 0..60>|auto (default: auto)
 * UICRON_CHRONIC=on|off (default: off)

For start time randomization, i.e., UICRON_DELAY=auto, an integer hash
between 0 and 50 is computed from the environment variable UICRON_ID
if present, or from a hash over the command string. This means that
the same cronjob always starts with the same delay, but the delay is
different for each individual cronjob. Set UICRON_DELAY=0 to disable
this behaviour.

EOF
}

error() {
    echo "$UICRON: $1" >&2
    exit "${2:-42}"
}

debug() {
    if [ $DEBUG -gt 0 ]; then echo [DEBUG] "$@"; fi
}


#
# Parse UICRON command-line parameters
#

while true; do
    case "$1" in
	-h|--help)
	    usage
	    exit 0
	    ;;
	-c)  # shell behaviour
	    shift
	    break
	    ;;
	-*)
	    error "invalid option '$1'"
	    ;;
	*)
	    usage >&2
	    exit 42
	    ;;
    esac
done


if [ -n "${UICRON_ID}" ]; then
    CRONJOB_ID="${UICRON_ID}"
else
    CRONJOB_ID=$( echo "$*" | sha1sum | awk '{print $1}' )
fi
unset UICRON_ID

#
# Parse UICRON environment variables
#

CHRONIC="${UICRON_CHRONIC:-off}"
CHRONIC_CMD=""
case "$CHRONIC" in
    yes|on|1)
	CHRONIC=on
	CHRONIC_CMD="/usr/bin/chronic"
	;;
    no|off|0)
	CHRONIC=off
	;;
    *)
	error "invalid chronic value '${UICRON_CHRONIC}' (expect bool)"
	;;
esac
unset UICRON_CHRONIC

LOCKING="${UICRON_LOCKING:-on}"
LOCKING_CMD=""
case "$LOCKING" in
    yes|on|1)
	LOCKING=on
	FILETAG="${LOGNAME}-${CRONJOB_ID}"
	LOCKING_CMD="/usr/bin/runlock -f/tmp/runlock-$FILETAG"
	;;
    no|off|0)
	LOCKING=off
	;;
    *)
	error "invalid locking value '${UICRON_LOCKING}' (expect bool)"
	;;
esac
unset UICRON_LOCKING

TIMEOUT="${UICRON_TIMEOUT:-600}"
TIMEOUT_CMD=""
case "$TIMEOUT" in
    ''|*[!0-9]*)  # not a positive number
	error "invalid timeout '$TIMEOUT' (expect integer)"
	;;
    *)
	TIMEOUT_CMD="/usr/bin/timeout -k5 -sTERM $TIMEOUT"
	;;
esac
unset UICRON_TIMEOUT

SHELL="${UICRON_SHELL:-/bin/sh}"
SHELL_CMD=""
case "$SHELL" in
    /bin/sh|/bin/bash)
	SHELL_CMD="$SHELL -c"
	;;
    *)
	error "invalid SHELL '$SHELL' (expect /bin/sh or /bin/bash)"
	;;
esac
unset UICRON_SHELL

DELAY="${UICRON_DELAY:-auto}"
case "$DELAY" in
    auto)
	#
	# By default, vary start delay between 0 and 50 according to
	# the modulus of a reduced md5 hash sum:
	#
	# This behaviour
	#  - distributes ccron loads within the first 50 seconds of
	#    each minute
	#  - gives 10 seconds runtime without overlap in worst case
	#    until the next run is scheduled
	#
	DELAY=$( perl -MDigest::MD5=md5 -wle 'print unpack("L", md5(shift)) % 50' "${CRONJOB_ID}" )
	;;
    ''|*[!0-9]*)  # not a positive number
	error "invalid delay '$DELAY' (expect integer)"
	;;
    *)
	if [ "$DELAY" -ge 60 ]; then
	    error "delay is greater than maximum ($DELAY >= 60)"
	fi
	;;
esac
unset UICRON_DELAY


debug "Cronjob Summary:"
debug "  Context:   $CHRONIC_CMD $LOCKING_CMD $TIMEOUT_CMD $SHELL_CMD '...'"
debug "  Command:   ""$*"
debug "  Delay:     $DELAY seconds"
debug ""

debug "--> Delay $DELAY seconds ..."
sleep $DELAY

debug "--> Execute command within the wrapper context ..."
exec $CHRONIC_CMD $LOCKING_CMD $TIMEOUT_CMD $SHELL_CMD "$*"