From 4c0856d1f1c4b6763a34e7dd87a6e702cb94018c Mon Sep 17 00:00:00 2001 From: Alexander Barton Date: Sun, 22 Oct 2017 21:19:09 +0200 Subject: [PATCH] Add new "mail-wrapper" script --- Makefile | 2 +- mail/Makefile | 13 +++ mail/wrapper/Makefile | 19 ++++ mail/wrapper/mail-wrapper | 219 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 mail/Makefile create mode 100644 mail/wrapper/Makefile create mode 100755 mail/wrapper/mail-wrapper diff --git a/Makefile b/Makefile index 5fa87e8..370eeba 100644 --- 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 index 0000000..d6b784a --- /dev/null +++ b/mail/Makefile @@ -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 index 0000000..9843e50 --- /dev/null +++ b/mail/wrapper/Makefile @@ -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 index 0000000..045f94b --- /dev/null +++ b/mail/wrapper/mail-wrapper @@ -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} [ [ [<…>]]]" >&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 for the email." >&2 + echo " --to|-t
Email address to send the email to." >&2 + echo >&2 + echo "When no 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 -- 2.39.2