dont email usually redundant logs by default
[log-quiet] / sysd-mail-once
index e4305aec3ce621764cf517f9d311c179106bc79b..c5cd8c84670c257448e19e6ec9b73b58f6a1c8f3 100755 (executable)
@@ -18,12 +18,19 @@ set -eE -o pipefail
 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
 
 errors=3
 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
 
 errors=3
-cbase=/var/local/cron-errors
-case "$1" in
+tmp=(~)
+cbase="${tmp[0]}/sysd-mail-once-state"
+to=root
+dryrun=false
+print_all=false
+while [[ $1 == -* ]]; do
+  case "$1" in
     -h|--help)
     -h|--help)
-        cat <<EOF
-Usage: sysd-log-once [-ERRORS] SERVICE COMMAND [ARGS...]
-For systemd timers, email on repeated failure & success after failure.
+      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.
 
 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.
@@ -31,50 +38,142 @@ The email will contain the service's logs for the last ERRORS runs.
 
 Stores error counts in $cbase
 
 
 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.
+
+-a              Email the logs of all errors intead of just the last one.
+-t TO_ADDRESS   Address to email about errors
+-n              Dry run. Execute command but only print out what we would do in response.
 EOF
 EOF
-        exit 0
-        ;;
+      exit 0
+      ;;
     -[0-9]*)
     -[0-9]*)
-        errors=${1#-}
-        shift
-        ;;
-esac
-service=$1
+      errors=${1#-}
+      ;;
+    -a)
+      print_all=true
+      ;;
+    -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
 
 shift
 
-c=$cbase/$service
 
 
-glob="$c[0-9]*"
+# 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
+
+if $dryrun; then
+  e "c=$c"
+fi
+
+glob="${c}[0-9]*"
 arr=($glob); file="${arr[0]}"; [[ $glob != "$file" ]] || file=
 arr=($glob); file="${arr[0]}"; [[ $glob != "$file" ]] || file=
-u=${USER:-root}
-[[ -d $cbase ]] || mkdir -p $cbase
+if [[ $dryrun && $file ]]; then
+  e "file=$file"
+fi
 
 
-if [[ ! $file ]]; then
-    cursor=$(journalctl --show-cursor -qn0|sed 's/^\s*--\scursor:\s*//')
+if ! [[ -d $cbase ]]; then
+  mkdir -p $cbase
 fi
 fi
-if "$@"; then
-    if [[ $file ]]; then
-        rm -f $file
-        if [[ $file == $c$errors ]]; then
-            echo | mail -s "$HOSTNAME: $service success" $u@localhost
+
+
+code=0
+"$@" || code=$?
+if (( code )); then
+  send_mail=false
+  cursor=$(journalctl --show-cursor -qn0|sed 's/^\s*--\scursor:\s*//')
+  if [[ $file ]]; then
+    i=${file#"$c"}
+    if (( i < errors )); then
+      new_file=$c$((i+1))
+      m mv $file $new_file
+      file=$new_file
+      if [[ $file == $c$errors ]]; then
+        send_mail=true
+      else
+        if $dryrun; then
+          printf "dryrun: appending to file $file: %s\n" "$cursor"
+        else
+          printf "%s\n" "$cursor" >>$file
         fi
         fi
+      fi
     fi
     fi
-else # $@ failed
-    if [[ $file ]]; then
-        i=${file#$c}
-        if (( i < errors )); then
-            new_file=$c$((i+1))
-            mv $file $new_file
-            file=$new_file
-            if [[ $file == $c$errors ]]; then
-                journalctl -u $service.service --after-cursor=$(<$file) | \
-                    mail -s "$HOSTNAME: $service failure" $u@localhost
-            fi
-        fi
+  else
+    file=${c}1
+    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
+    if $print_all; then
+      cursor=$(head -n1 $file)
     else
     else
-        file=${c}1
-        printf "%s\n" "$cursor" >$file
+      cursor=$(tail -n1 $file)
+    fi
+    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="$cursor")
+EOF
+  fi
+else
+  if [[ $file ]]; then
+    m rm -f $file
+    if [[ $file == $c$errors ]]; then
+      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
 fi
 fi