#!/bin/sh set -eu usage() { cat <<-'EOF' Usage: cronjob COMMAND... cronjob -h EOF } help() { cat <<-'EOF' Options: -h, --help show this message COMMAND the command to be executed Execute the given command, and send the output to email, with special treatment to the status code. It kills the job it it lasts more than one hour. It load the appropriate files, so that the actual cron declaration is smaller. Examples: Run a backup: $ cronjob backup -q cron EOF } for flag in "$@"; do case "$flag" in --) break ;; --help) usage help exit ;; *) ;; esac done while getopts 'h' flag; do case "$flag" in h) usage help exit ;; *) usage >&2 exit 2 ;; esac done shift $((OPTIND - 1)) if [ -z "${1:-}" ]; then printf 'Missing COMMAND.\n\n' >&2 usage >&2 exit 2 fi if [ "$(id -un)" != 'root' ]; then printf 'This script must be run as root.\n\n' >&2 usage >&2 exit 2 fi set +eu # shellcheck source=/dev/null . /etc/rc set -eu epoch() { awk 'BEGIN { srand(); print(srand()); }' } now() { date '+%Y-%m-%dT%H:%M:%S%:z' } uuid() { od -xN20 /dev/random | head -n1 | awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' } mkstemp() { name="${TMPDIR:-/tmp}/uuid-tmpname with spaces.$(uuid)" touch "$name" printf '%s' "$name" } pre() { # Same as: # sed -u "s|^|[$CMD]: |" # but the "-u" option is not POSIX IFS='' while read -r line; do printf '[%s]: %s\n' "$CMD" "$line" done } duration() { minutes=$((${1} / 60)) seconds=$((${1} % 60)) printf '%sm%ss' "$minutes" "$seconds" } CMD="$*" HOSTNAME="$(hostname)" FROM="cronjob@$HOSTNAME" TIMEOUT='14400' # four hours STATUS_F="$(mkstemp)" OUT="$(mkstemp)" email() { { cat <<-EOF Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: $FROM To: root@localhost Subject: (exit status: $(cat "$STATUS_F")) - $HOSTNAME: $CMD EOF cat "$OUT" } | sendmail -t -f "$FROM" rm -f "$OUT" "$STATUS_F" } trap email EXIT { cat <<-EOF Running commad: $CMD Starting at: $(now) EOF START="$(epoch)" STATUS=0 timeout "$TIMEOUT" "$@" || STATUS=$? printf '%s' "$STATUS" > "$STATUS_F" END="$(epoch)" DURATION_SECONDS=$((END - START)) cat <<-EOF Finished at: $(now) Duration: $(duration "$DURATION_SECONDS") EOF } 2>&1 | pre | ts '%Y-%m-%dT%H:%M:%S' | tee "$OUT" >> /var/log/cronjobs.log