trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
errors=3
-cbase=$HOME/sysd-mail-once-state
+tmp=(~)
+cbase="${tmp[0]}/sysd-mail-once-state"
to=root
-case "$1" in
- -h|--help)
- cat <<EOF
-Usage: sysd-mail-once [-t TO_ADDRESS] [-ERRORS] SERVICE COMMAND [ARGS...]
-For systemd timers, email on repeated failure & success after failure.
+dryrun=false
+while [[ $1 == -* ]]; do
+ case "$1" in
+ -h|--help)
+ cat <<EOF
+Usage: sysd-mail-once [-t TO_ADDRESS] [-ERRORS] SERVICE COMMAND [COMMAND_ARGS...]
+
+For use with systemd timers, to email (with exim) on repeated failure &
+success after failure.
In the service triggered by the timer, prepend this script to the ExecStart.
The email will contain the service's logs for the last ERRORS runs.
Stores error counts in $cbase
--ERRORS: ERRORS is the number of failurs to accumulate before mailing the error.
- Default is 3.
+-ERRORS ERRORS is the number of failurs to accumulate before mailing the error.
+ Default is 3.
+
+-t TO_ADDRESS Address to email about errors
+-n Dry run. Execute command but only print out what we would do in response.
EOF
- exit 0
- ;;
- -[0-9]*)
- errors=${1#-}
- shift
- ;;
- -t)
- to="$2"
- shift 2
- ;;
-esac
-service=$1
+ exit 0
+ ;;
+ -[0-9]*)
+ errors=${1#-}
+ ;;
+ -t)
+ to="$2"
+ shift
+ ;;
+ -n)
+ dryrun=true
+ ;;
+ *)
+ echo "error: unexpected arg: $1"
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+if (( $# < 2 )); then
+ echo "error: expected at least 2 args after options. args:"
+ exit 1
+fi
+
+service="$1"
shift
+
+# maybe run, depending on $dryrun
+m() {
+ if $dryrun; then
+ printf "%s\n" "$*"
+ else
+ "$@"
+ fi
+}
+# maybe run, with stdin
+mi() {
+ if $dryrun; then
+ printf "%s <<'EOF'\n" "$*"
+ cat
+ echo EOF
+ else
+ "$@"
+ fi
+}
+e() {
+ printf "dryrun: %s\n" "$*"
+}
+
c=$cbase/$service # c for command file path base
-glob="$c[0-9]*"
+if $dryrun; then
+ e "c=$c"
+fi
+
+glob="${c}[0-9]*"
arr=($glob); file="${arr[0]}"; [[ $glob != "$file" ]] || file=
-[[ -d $cbase ]] || mkdir -p $cbase
+if [[ $dryrun && $file ]]; then
+ e "file=$file"
+fi
+
+if ! [[ -d $cbase ]]; then
+ mkdir -p $cbase
+fi
if [[ ! $file ]]; then
cursor=$(journalctl --show-cursor -qn0|sed 's/^\s*--\scursor:\s*//')
if (( code )); then
send_mail=false
if [[ $file ]]; then
- i=${file#$c}
+ i=${file#"$c"}
if (( i < errors )); then
new_file=$c$((i+1))
- mv $file $new_file
+ m mv $file $new_file
file=$new_file
if [[ $file == $c$errors ]]; then
send_mail=true
fi
else
file=${c}1
- printf "%s\n" "$cursor" >$file
+ if $dryrun; then
+ printf "dryrun: creating $file, contents: %s\n" "$cursor"
+ else
+ printf "%s\n" "$cursor" >$file
+ fi
if (( errors == 1 )); then
send_mail=true
fi
fi
if $send_mail; then
- journalctl -u $service.service --after-cursor=$(<$file) | \
- mail -s "$HOSTNAME: $service exit code: $code" "$to"
+ echo "sysd-mail-once: emailing on $errors errors. exit code: $code"
+ mi exim -odf -t <<EOF
+To: $to
+From: $(id -u -n)@$(hostname -f)
+Subject: $HOSTNAME: $service exit code: $code
+
+$(journalctl -u $service.service --after-cursor="$(<$file)")
+EOF
fi
else
if [[ $file ]]; then
- rm -f $file
+ m rm -f $file
if [[ $file == $c$errors ]]; then
- echo | mail -s "$HOSTNAME: $service success" "$to"
+ echo "sysd-mail-once: emailing success after >= $errors errors."
+ mi exim -odf -t <<EOF
+To: $to
+From: $(id -u -n)@$(hostname -f)
+Subject: $HOSTNAME: $service success
+
+EOF
fi
fi
fi