]> arthur.barton.de Git - ax-unix.git/commitdiff
Add new "mail-wrapper" script
authorAlexander Barton <alex@barton.de>
Sun, 22 Oct 2017 19:19:09 +0000 (21:19 +0200)
committerAlexander Barton <alex@barton.de>
Sun, 22 Oct 2017 19:19:09 +0000 (21:19 +0200)
Makefile
mail/Makefile [new file with mode: 0644]
mail/wrapper/Makefile [new file with mode: 0644]
mail/wrapper/mail-wrapper [new file with mode: 0755]

index 5fa87e8b207428c7abffdf47dc58496acb843616..370eeba06c2167137d7483c794d95b62ea6e41f7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,6 @@
 # (at your option) any later version.
 #
 
-SUBDIRS = lib bup
+SUBDIRS = lib bup mail
 
 include Makefile.ax
diff --git a/mail/Makefile b/mail/Makefile
new file mode 100644 (file)
index 0000000..d6b784a
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# ax-unix: Alex' UNIX Tools & Scripts
+# Copyright (c)2013-2017 Alexander Barton (alex@barton.de)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+
+SUBDIRS = wrapper
+
+include ../Makefile.ax
diff --git a/mail/wrapper/Makefile b/mail/wrapper/Makefile
new file mode 100644 (file)
index 0000000..9843e50
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# ax-unix: Alex' UNIX Tools & Scripts
+# Copyright (c)2013-2017 Alexander Barton (alex@barton.de)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+
+ALL = mail-wrapper
+
+include ../../Makefile.ax
+
+install-local: mail-wrapper
+       install -d -o $(USER) -g $(GROUP) -m 755 \
+        $(DESTDIR)/usr/local/bin
+       install -p -o $(USER) -g $(GROUP) -m 755 mail-wrapper \
+        $(DESTDIR)/usr/local/bin/mail-wrapper
diff --git a/mail/wrapper/mail-wrapper b/mail/wrapper/mail-wrapper
new file mode 100755 (executable)
index 0000000..045f94b
--- /dev/null
@@ -0,0 +1,219 @@
+#!/usr/bin/env bash
+#
+# mail-wrapper -- Report results of a command by email
+# Copyright (c)2017 Alexander Barton (alex@barton.de)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+
+NAME=$(basename "$0")
+
+# Include "ax-common.sh":
+ax_common_sourced=
+for dir in "$HOME/lib" "$HOME/.ax" /usr/local /opt/ax /usr; do
+       [ -z "$ax_common_sourced" ] || break
+       ax_common="${dir}/lib/ax/ax-common.sh"
+       # shellcheck source=/usr/local/lib/ax/ax-common.sh
+       [ -r "$ax_common" ] && . "$ax_common"
+done
+if [ -z "$ax_common_sourced" ]; then
+       echo "Error ($(basename "$0")): \"ax-common.sh\" not found, aborting!"
+       echo "Please install 'ax-unix', \"Alex' UNIX Tools & Scripts\", and try again."
+       exit 99
+fi
+unset dir ax_common ax_common_sourced
+
+usage() {
+       echo >&2
+       echo "Usage:" >&2
+       echo "  $NAME [--help|--usage]" >&2
+       echo "  $NAME {parameters} [<command> [<arg> [<…>]]]" >&2
+       echo >&2
+       echo "  -C                      Use the \"C\" locale, no localized (error) messages." >&2
+       echo "  --errors|-e             Generate email on errors only." >&2
+       echo "  --from|-f               Email address of the sender of the email." >&2
+       echo "  --subject|-s <subject>  Subject for the email." >&2
+       echo "  --to|-t <address>       Email address to send the email to." >&2
+       echo >&2
+       echo "When no <command> is given, $NAME reads from standard input." >&2
+       echo >&2
+       exit "${1:-0}"
+}
+
+syntax_error() {
+       ax_error -l "Syntax error!"
+       usage 2
+}
+
+# Initialize internal state.
+unset is_error
+host=$(hostname -f 2>/dev/null || hostname)
+
+# Some defaults (can be adjusted by command line parameters).
+unset do_errors_only
+unset subject
+from="${LOGNAME:-root} <${LOGNAME:-root}@$host>"
+to="$from"
+
+# Parse the command line ...
+while [[ $# -gt 0 ]]; do
+       case "$1" in
+               "-C")
+                       unset LANG
+                       export LC_ALL="C"
+                       ;;
+               "--debug"|"-D")
+                       export DEBUG=1
+                       ;;
+               "--errors"|"-e")
+                       do_errors_only=1
+                       ;;
+               "--from"|"-f")
+                       shift
+                       [[ $# -gt 0 ]] || syntax_error
+                       from="$1"
+                       ;;
+               "--help"|"--usage")
+                       usage
+                       ;;
+               "--subject"|"-s")
+                       shift
+                       [[ $# -gt 0 ]] || syntax_error
+                       subject="$1"
+                       ;;
+               "--to"|"-t")
+                       shift
+                       [[ $# -gt 0 ]] || syntax_error
+                       to="$1"
+                       ;;
+               "-"*)
+                       syntax_error
+                       ;;
+               *)
+                       # Command to execute follows in command line.
+                       break
+                       ;;
+       esac
+       shift
+done
+
+# Initialize the "buffer file" on file handle #3. This file will store all
+# output, stdout and stderr combined. The file is immediately unliked so that
+# we can't leak stale files. Afterwards this script accesses the "buffer file"
+# by its file descriptor only.
+buffer_file=$(mktemp) \
+       || ax_abort -l "Failed to create buffer file: \"$buffer_file\"!"
+ax_debug "buffer_file=\"$buffer_file\""
+exec 3>"$buffer_file" \
+       || ax_abort -l "Failed to redirect FD #3 to buffer file!"
+rm "$buffer_file" \
+       || ax_error -l "Failed to delete buffer file: \"$buffer_file\"!"
+buffer_file="/dev/fd/3"
+
+if [[ $# -gt 0 ]]; then
+       # Execute command and save output in buffer file.
+       # Use a sub-shell to not pollute our name space!
+       error_file=$(mktemp) \
+               || ax_abort -l "Failed to create error buffer file: \"$error_file\"!"
+       ax_debug "error_file=\"$error_file\""
+       exec 4>"$error_file" \
+               || ax_abort -l "Failed to redirect FD #4 to error file!"
+       rm "$error_file" \
+               || ax_error -l "Failed to delete error buffer file: \"$error_file\"!"
+       error_file="/dev/fd/4"
+
+       job=$(basename "$1")
+
+       ax_debug "Running command \"$*\" ..."
+       exit_code=$(
+               "$@" 2>&1 1>&3 | tee "$error_file" >&3
+               echo "${PIPESTATUS[0]}"
+       )
+else
+       # Read from stdin and save it to the buffer file.
+       error_file="/dev/null"
+       job="Job"
+
+       ax_debug "Reading from stdin ..."
+       while read -r line; do
+               echo "$line" >&3 \
+                       || ax_abort -l "Failed to write to buffer file!"
+       done
+       exit_code=0
+fi
+
+ax_debug "exit_code=$exit_code"
+
+count_all=$(wc -l <"$buffer_file" || ax_abort -l "Failed to count buffer file!")
+count_err=$(wc -l <"$error_file" || ax_abort -l "Failed to count error file!")
+
+# Error or no error -- that's the question! An error is assumed when either the
+# exit code of the command was non-zero or there was output to stderr.
+[[ $count_err -gt 0 || $exit_code -ne 0 ]] && is_error=1
+
+# Construct email subject ...
+[[ -z "$subject" ]] && subject="$host: $job report"
+[[ -n "$is_error" ]] && subject="$subject - ERROR!" || subject="$subject - success"
+
+ax_debug "from=\"$from\""
+ax_debug "to=\"$to\""
+ax_debug "subject=$subject"
+
+if [[ -n "$DEBUG" ]]; then
+       echo "--- stdout+stderr ---"
+       cat "$buffer_file"
+       echo "--- stderr ---"
+       cat "$error_file"
+       echo "---"
+fi
+
+ax_debug "count_all=$count_all"
+ax_debug "count_err=$count_err"
+ax_debug "is_error=$is_error"
+
+# No errors detected (exit code & stderr), and email should be sent on errors
+# only: so exit early!
+[[ -z "$is_error" && -n "$do_errors_only" ]] && exit $exit_code
+
+# No error detected and no output at all: skip email, exit early:
+[[ -z "$is_error" && $count_all -eq 0 ]] && exit $exit_code
+
+# Build the report mail.
+# Make sure to ignore all mail(1) configuration files, system wide /etc/mailrc
+# (by using the "-n" option) as well as ~/.mailrc (by setting the MAILRC
+# environment varialbe).
+export MAILRC=/dev/null
+(
+       echo "$job report:"
+       echo
+       echo " - Host: $host"
+       echo " - User: $(id -un)"
+       echo " - Exit code: $exit_code"
+       echo
+       if [[ $# -gt 0 ]]; then
+               # A command name is known (not stdin), show it!
+               echo "Command:"
+               echo "$@"
+               echo
+       fi
+       if [[ $count_err -gt 0 ]]; then
+               # Prefix mail with all error messages.
+               echo "Error summary:"
+               echo "-----------------------------------------------------------------------------"
+               cat "$error_file" \
+                       || ax_abort -l "Failed to dump error file!"
+               echo "-----------------------------------------------------------------------------"
+               echo
+       fi
+       if [[ $count_all -ne $count_err ]]; then
+               # Show full output when different to "error output" only.
+               cat "$buffer_file" \
+                       || ax_abort -l "Failed to dump buffer file!"
+       fi
+) | mail -n -a "From: $from" -s "$subject" "$to" \
+       || ax_abort -l "Failed to send email to \"$to\"!"
+
+exit $exit_code