#!/bin/sh set -e UICRON=${0##*/} DEBUG=${UICRON_DEBUG:-0} unset UICRON_DEBUG usage() { cat < (default: 600) * UICRON_DELAY=|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 "$*"