#!/bin/bash
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to switch
+# its license to GPL.
-# Usage: mail-test-check [slow] [anything]
+# Copyright 2024 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# Usage: mailtest-check [slow] [int|nonint]
#
# slow: do slow checks, like spamassassin
#
-# anything: consider non-interactive, dont print unless something went
+# for non-interactive, dont print unless something went
# wrong
+#set -x
-source /b/errhandle/err
[[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@"
+source /b/bash-bear-trap/bash-bear
+
shopt -s nullglob
e() { $int || return 0; printf "mailtest-check: %s\n" "$*"; }
getspamdpid() {
if [[ ! $spamdpid || ! -d /proc/$spamdpid ]]; then
# try twice in case we are restarting, it happens.
- for i in 1 2; do
- spamdpid=$(systemctl show --property MainPID --value spamassassin | sed 's/^[10]$//' ||:)
+ for (( i=0; i<2; i++ )); do
+ spamdpid=$(systemctl show --property MainPID --value $spamd_ser | sed 's/^[10]$//' ||:)
if [[ $spamdpid ]]; then
break
fi
done
fi
}
-pr() {
- if $doprom && [[ -e /var/lib/prometheus/node-exporter ]]; then
- cat >>/var/lib/prometheus/node-exporter/mailtest-check.prom.$$
- fi
-}
#### begin arg processing ####
fi
#### end arg processing ####
-
-if ! $int; then
- sleep 60
-fi
+# we put this in to avoid dns errors that happen on reboot,
+# but I want to debug them.
+# if ! $int; then
+# sleep 60
+# fi
# TODO, get je to deliver the local mailbox: /m/md/INBOX
# dovecot appears to setup, i can t be sure.
+maini=0
+
+spamd_ser=spamd
+if systemctl cat spamassassin &>/dev/null; then
+ spamd_ser=spamassassin
+fi
+
source /a/bin/bash_unpublished/source-state
doprom=false
main() {
+ local -a p_unexpected_spamd_results p_missing_dnswl p_last_usec
case $HOSTNAME in
bk)
folders=(/m/md/{expertpathologyreview.com,amnimal.ninja}/testignore)
;;
*)
folders=(/m/md/l/testignore)
- froms=(testignore@je.b8.nz testignore@expertpathologyreview.com testignore@amnimal.ninja ian@iankelling.org z@zroe.org iank@gnu.org)
+ # save some cpu cycles
+ froms=(testignore@je.b8.nz ian@iankelling.org)
+ if (( maini % 10 == 0 )); then
+ froms=(testignore@je.b8.nz testignore@expertpathologyreview.com testignore@amnimal.ninja ian@iankelling.org z@zroe.org)
+ fi
if ! $int; then
### begin rsyncing fencepost email ###
# We dont want to exit if rsync fails, that will get caught by
try_left=$(( try_limit - ( EPOCHSECONDS - try_start_time) ))
timeout=120 # somewhat arbitrary value
if (( try_left < 0 )); then
+ echo "mailtest-check: failed to rsync fencepost > $try_limit seconds"
break
fi
if (( try_left < timeout )); then
fi
e spamdpid: $spamdpid
if [[ ! $spamdpid ]]; then
- echo mailtest spamd pid not found. systemctl status spamassassin:
- systemctl status spamassassin
+ echo mailtest spamd pid not found. systemctl status $spamd_ser:
+ systemctl status $spamd_ser
fi
tmpfile=$(mktemp)
declare -i unexpected=0
- declare -i missing_dnswl=0
for folder in ${folders[@]}; do
for from in ${froms[@]}; do
+ declare -i missing_dnswl=0
+ #declare -i dnsfail=0
+ declare -i unexpected=0
latest=
last_sec=0
# example line that sed is parsing:
# (-0.1 / 5.0 requ) DKIM_SIGNED=0.1,DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1,SPF_HELO_PASS=-0.001,SPF_PASS=-0.001,TVD_SPACE_RATIO=0.001 autolearn=_AUTOLEARN
resultfile=$(mktemp)
- $spamcpre sudo -u Debian-exim spamassassin -D -t --cf='score PYZOR_CHECK 0' <"$latest" &>$resultfile
+ # add -D for debug info. usually it
+ $spamcpre sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$latest" &>$resultfile
# note: on some mail, its 1 line after the send-test-forward, on others its 2 with a blank inbetween.
# I use the sed -n to filter this.
raw_results="$(tail $resultfile | grep -A2 -Fx /usr/local/bin/send-test-forward | tail -n+2 | sed -nr 's/^\([^)]*\) *//;s/=[^, ]*([, ]|$)/ /gp')"
for r in $raw_results; do
case $r in
+ # This came in t12, but its just dkim + spf, and my
+ # systems aren't all t12, so ignore it for now.
+ DMARC_PASS) : ;;
# got this in an update 2022-01. dun care
T_SCC_BODY_TEXT_LINE|SCC_BODY_SINGLE_WORD) : ;;
# we have a new domain, ignore this.
# it seems like some versions of spamassassin do BODY_SINGLE_WORD, others dont, we dun care.
# bayes_00 is a new one indicating ham, we dont care if its missing.
BAYES_00|BODY_SINGLE_WORD|FROM_FMBLA_NEWDOM*|autolearn) : ;;
+
+ # These have somewhat randomly been added and removed, resulting in useless alerts, so ignore them.
+ RCVD_IN_DNSWL_MED|DKIMWL_WL_HIGH) : ;;
+
SPF_HELO_NEUTRAL)
# some of my domains use neutral spf, treat them the same.
results[SPF_HELO_PASS]=t
keys=(DKIM_SIGNED DKIM_VALID{,_AU,_EF} SPF_HELO_PASS SPF_PASS TVD_SPACE_RATIO)
if [[ $to == *@gnu.org && $from == *@gnu.org ]]; then
keys=(ALL_TRUSTED TVD_SPACE_RATIO)
- elif [[ $to == *@gnu.org ]]; then
- # eggs has RCVD_IN_DNSWL_MED
- keys+=(RCVD_IN_DNSWL_MED)
- elif [[ $from == *@gnu.org ]]; then
- # eggs has these
- keys+=(RCVD_IN_DNSWL_MED DKIMWL_WL_HIGH)
+ # from eggs had DKIMWL_WL_HIGH sometime in 2022, then DKIMWL_WL_MED unti march 2023
fi
for t in ${keys[@]}; do
# echo mailtest-check: cat $latest:
# cat $latest
# echo mailtest-check: end of cat
- # echo "$(tput setaf 5 2>/dev/null ||:)█$(tput sgr0 2>/dev/null||:)%.0s" $(eval echo "{1..${COLUMNS:-60}}")
#fi
fi
rm -f $resultfile
for r in ${results[@]}; do
case $r in
- DKIM_INVALID|T_SPF_TEMPERROR|T_SPF_HELO_TEMPERROR)
- missing_dnswl+=1
- ;;
+ # iank: for when we want to handle dns errors differently.
+ # also uncomment declaration of dnsfail above.
+ # DKIM_INVALID|T_SPF_TEMPERROR|T_SPF_HELO_TEMPERROR)
+ # dnsfail+=1
+ # ;;
*)
unexpected=$(( unexpected + 1 ))
;;
esac
done
for miss in ${missing[@]}; do
- # We expect dns failures from time to time, so
- # we count them separately and alert differently.
+ # At some point we had annoying dns failures that we couldn't solve so we
+ # we counted dns fail related results separately and alert differently.
+ # DKIM_VALID|DKIM_VALID_AU|DKIM_VALID_EF|SPF_HELO_PASS|SPF_PASS|
case $miss in
- DKIM_VALID|DKIM_VALID_AU|DKIM_VALID_EF|SPF_HELO_PASS|SPF_PASS|RCVD_IN_DNSWL_MED|DKIMWL_WL_HIGH)
- missing_dnswl+=1
- ;;
*)
unexpected+=1
;;
esac
done
+ mapfile -O ${#p_missing_dnswl[@]} -t p_missing_dnswl <<EOF
+mailtest_check_missing_dnswl{folder="$folder",from="$from"} $missing_dnswl
+EOF
+ mapfile -O ${#p_unexpected_spamd_results[@]} -t p_unexpected_spamd_results <<EOF
+mailtest_check_unexpected_spamd_results{folder="$folder",from="$from"} $unexpected
+EOF
fi # if spamdpid
fi # if $slow
e $((age_sec / 60)):$(( age_sec % 60 )) ago. to:$to from:$from $latest
# usec = unix seconds
- pr <<EOF
+ mapfile -O ${#p_last_usec[@]} -t p_last_usec <<EOF
mailtest_check_last_usec{folder="$folder",from="$from"} $last_sec
EOF
done # end for from in ${froms[@]}
done # end for folder in ${folders[@]}
- if $slow; then
- pr <<EOF
-mailtest_check_missing_dnswl $missing_dnswl
-mailtest_check_unexpected_spamd_results $unexpected
-EOF
- fi
dir=/var/lib/prometheus/node-exporter
- if [[ -e $dir ]]; then
- mv $dir/mailtest-check.prom.$$ $dir/mailtest-check.prom
+ path=$dir/mailtest-check.prom.$$
+ if $doprom && [[ -e $dir ]]; then
+ for l in "${p_unexpected_spamd_results[@]}"; do
+ printf "%s\n" "$l" >>$path
+ done
+ for l in "${p_missing_dnswl[@]}"; do
+ printf "%s\n" "$l" >>$path
+ done
+ for l in "${p_last_usec[@]}"; do
+ printf "%s\n" "$l" >>$path
+ done
+ mv $path $dir/mailtest-check.prom
# note: node_textfile_mtime_seconds will tell us when this last happened. useful for debugging.
fi
}
while true; do
premain_sec=$EPOCHSECONDS
main
+ maini=$((maini + 1))
sleep $(( 300 - ( EPOCHSECONDS - premain_sec ) ))
done
}
-if [[ $INVOCATION_ID ]]; then
+if [[ $PPID == 1 ]]; then
loop-main
else
main