chiark / gitweb /
probe: set nullglob, so it works when disabled
[modbot-mtm.git] / probes / modrelays-probe
index 1c4ead4ffcdd63c677c81a7a64bb09b67eb4dbe9..4ce164856cdaee895cab95243735835a2c7beaea 100755 (executable)
@@ -1,49 +1,80 @@
 #!/bin/bash
 
-set -e
+set -e$MODRELAYS_PROBE_SET_X
+
+MODRELAYS=moderators.isc.org
+PROBE_TIMEOUT=$(( 20 * 60 ))
+PROBE_EXPIRE=$(( 32 * 86400 ))
+
+shopt -s nullglob
+
+case "$1" in
+received)
+       mode="$1"
+       cd "$2"
+       shift; shift; set "$mode" "$@"
+       ;;
+esac
+
+. ../global-settings
+. ./settings
 
 id=$(date +%s)_$$
+statedir=probes/probes
+lockfile=$statedir/.lock
 
 fail () {
        printf >&2 "%s\n" "modrelays-probe: error: $1"
        exit 16
 }
 
-record-probing () {
-       # implicitly uses newsgroup, id, domain
-       # prints working dir filename
+compute-td () {
+       # implicitly uses GROUP, id, domain
+       # caller must "local td", which will be set
        local probeid=$1
 
-       probeid=${probeid//[^=-.,_0-9A-Za-z]/%/}
+       probeid="$domain,${probeid//[^-=:.,_0-9A-Za-z]/%},$id"
        case $probeid in
-       .*|*/*) fail "yikes, sanitisation bug!" ;;
+       .*|*/*) fail "yikes, sanitisation bug ($probeid) !" ;;
        esac
 
-       local newtd="monitoring/modrelays.probes/$probeid"
-       mkdir -p $newtd
-       printf "%s" $newtd
+       td="$statedir/$probeid"
+}
+
+record-probing () {
+       compute-td "$@"
+       mkdir -p $td
+}
+
+record-probing-start () {
+       record-probing "$@"
+       if ! [ -e "$td/started" ]; then
+               date -R >"$td/started"
+       fi
 }
 
 record-outcome () {
        local probeid=$1
        local outcome=$2
        local message=$3
-       local td=`record-probing "$probeid"`
-       printf "%s\n" >"$td"/"$outcome"
+       local td
+       record-probing "$probeid"
+       printf "%s\n" >"$td"/"$outcome" "$message"
 }
 
-record-success () { record-outcome "$1" ok '' }
-record-tempfail () { record-outcome "$1" tempfail "$2" }
-record-permfail () { record-outcome "$1" permfail "$2" }
+record-success () { record-outcome "$1" ok ''; }
+record-tempfail () { record-outcome "$1" tempfail "$2"; }
+record-permfail () { record-outcome "$1" permfail "$2"; }
 
 probe-addr () {
        local mx=$1
        local addr=$2
 
-       local td=`record-probing "mx=$mx,addr=$addr"`
+       local td
+       record-probing-start "mx=$mx,addr=$addr"
 
        set +e
-       swaks   --to "${GROUP//./-/}@$domain" \
+       swaks   --to "${GROUP//./-}@$domain" \
                --server $addr \
                --tls-optional-strict \
                --header 'Subject: test modrelays probe test' \
@@ -61,14 +92,15 @@ probe-addr () {
        local rhs
        local prefix
        local expect_no_5xx='initial connection'
-       while read <$td/swaks.log prefix rhs; do
+       exec 4<$td/swaks.log
+       while read <&4 prefix rhs; do
                case "$prefix" in
                '<'*)
                        case "$rhs" in
                        5*)
                                if [ "x$expect_no_5xx" != x ] && \
                                   [ "x$permfail" = x ]; then
-                                       permfail="$rhs ($expect_no_5xx)" ;;
+                                       permfail="$rhs ($expect_no_5xx)"
                                fi
                                ;;
                        esac
@@ -80,6 +112,7 @@ probe-addr () {
                        esac
                        ;;
                *)
+               esac
        done
 
        if [ "x$permfail" = x ]; then
@@ -91,7 +124,8 @@ probe-addr () {
 
 probe-domain () {
        local domain=$1
-       local td=`record-probing dns`
+       local td
+       record-probing-start dns
        
        set +e
        adnshost -Fi -Tn +Do +Dt -t mx $domain >$td/dns
@@ -101,13 +135,13 @@ probe-domain () {
        case $rc in
        0)
                # have a list of MX's
-               exec <$td/dns
+               exec 3<$td/dns
                local pref
                local mx
                local statustype
                local rhs
-               while read pref mx statustype rhs; do
-                       case $statustype in
+               while read <&3 pref mx statustype statustypenum rhs; do
+                       case $statustypenum in
                        0)
                                # have a list of relays
                                case $rhs in
@@ -116,9 +150,10 @@ probe-domain () {
                                        record-permfail "mx=$mx" \
                                                "dns format $rhs"
                                        continue
-                               ;;
-                               rhs=${rhs%%* (}
-                               rhs=${rhs# )}
+                                       ;;
+                               esac
+                               rhs=${rhs##* (}
+                               rhs=${rhs% )}
                                local addr
                                for addr in $rhs; do
                                        case $addr in
@@ -129,11 +164,13 @@ probe-domain () {
                                ;;
                        [123])
                                # temporary errors
-                               record-tempfail "mx=$mx" "dns $rc $rhs"
+                               record-tempfail "mx=$mx" \
+                                       "dns $rc $statustype $rhs"
                                ;;
                        *)
                                # yikes
-                               record-permfail "mx=$mx" "dns $rc $rhs"
+                               record-permfail "mx=$mx" \
+                                       "dns $rc $statustype $rhs"
                                ;;
                        esac
                done
@@ -141,9 +178,9 @@ probe-domain () {
                return
                ;;
        6)
-               permfail, try A
+               permfail, try A
                set +e
-               adnshost -Fi -Tn +Do +Dt -t a $domain >$td
+               adnshost -Fi -Tn +Do +Dt -t a $domain >$td/dns
                rc=$?
                set -e
                ;;
@@ -152,10 +189,10 @@ probe-domain () {
        case $rc in
        0)
                # have a list of A's (dealt with MXs above)
-               exec <$td/dns
+               exec 3<$td/dns
                local addr
-               while read addr; do
-                       probe-addr '<no-mx>' $addr
+               while read <&3 addr; do
+                       probe-addr 'NONE' $addr
                done
                record-success dns
                return
@@ -176,24 +213,174 @@ probe-domain () {
 no_args () {
        case $1 in
        0) return ;;
-       *) fail
+       *) fail "no arguments to $mode allowed" ;;
+       esac
+}
 
-mode_all () {
+acquire_lock () {
+       local lock_mode="$1"
+       if [ x"$WEBSTUMP_PROBE_LOCK" = x"$lockfile" ]; then return; fi
+       WEBSTUMP_PROBE_LOCK=$lockfile \
+       exec with-lock-ex $lock_mode "$lockfile" "$0" "$mode" "$@"
+}
+
+maybe-report () {
+       local outcome=$1
+
+       if $found_to_report; then return; fi
+       if ! [ -e "$attempt/$outcome" ]; then return; fi
+       found_to_report=true
+
+       read <"$attempt/$outcome" message
+
+       local reported
+       if [ -e "$attempt/reported" ]; then
+               read <"$attempt/reported" reported
+       fi
+       if [ "x$outcome" = "x$reported" ]; then return; fi
+
+       if [ x"$outcome" = x"ok" ] && [ x"$reported" = x ]; then
+               echo ok >"$attempt/reported"
+               return
+       fi
+
+       local info=${attempt##*/}
+       info=${info//,/ }
+
+       delim=`od -N 50 -An -x -w50 </dev/urandom`
+       delim=${delim// /}
+
+       local email="$attempt/.report.$outcome"
+       cat >"$email" <<END
+To: $ADMIN
+Subject: mod relay probe $outcome $info
+Content-Type: multipart/mixed; boundary="$delim"
+MIME-Version: 1.0
+
+--$delim
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+The moderation relay probe
+  $info
+END
+
+       if [ -e "$attempt/started" ]; then
+               local started
+               read started <"$attempt/started"
+               cat >>"$email" <<END
+started at
+  $started
+END
+       fi
+
+       cat >>"$email" <<END
+resulted in the outcome
+  $outcome
+END
+       if [ "x$message" != x ]; then
+               cat >>"$email" <<END
+with the message
+  $message
+END
+       fi
+
+       if [ "x$reported" != x ]; then
+               cat >>"$email" <<END
+This is even though previously the outcome seemed to be
+  $reported
+and this was reported previously.
+END
+       fi
+
+       cat >>"$email" <<END
+
+Logs are in
+  $attempt
+and concatenated to this email.
+
+END
+
+       local log
+       for log in "$attempt"/*; do
+               cat >>"$email" <<END
+--$delim
+Content-Type: text/plain; charset="utf-8"
+Content-Disposition: inline; filename="${log##*/}"
+Content-Description: "${log##*/}"
+Content-Transfer-Encoding: 8bit
+
+END
+               cat >>"$email" <"$log"
+               echo >>"$email"
+       done
+
+       cat >>"$email" <<END
+--$delim--
+END
+
+       /usr/sbin/sendmail -odb -oem -oee -t <"$email"
+       echo "$outcome" >"$attempt"/reported
+}
+
+mode_report () {
+       acquire_lock -w "$@"
+
+       local attempt
+       for attempt in $statedir/*; do
+
+               local now=$(date +%s)
+               local age=$(stat -c %Y "$attempt")
+               age=$(( $now - $age ))
+
+               local found_to_report=false
+               maybe-report ok
+               maybe-report permfail
+               maybe-report tempfail
+
+               if ! [ -e $attempt/reported ] && \
+                    [ $age -gt $PROBE_TIMEOUT ]; then
+                       echo >"$attempt"/timeout \
+       "Message did not arrive after ${PROBE_TIMEOUT}s"
+               fi
+
+               maybe-report timeout
+
+               if [ -e $attempt/reported ] && \
+                  [ $age -gt $PROBE_EXPIRE ]; then
+                       rm -rf "$attempt"
+               fi
+       done
+}
+
+mode_received () {
        no_args $#
-       for domain in $MODRELAYS; do
-               probe $domain
+
+       local hn group id domain mx addr
+       while read hn group id domain mx addr; do
+               if [ x"$hn" != x"X-WebSTUMP-Relay-Probe:" ]; then continue; fi
+               if [ x"$group" != x"$GROUP" ]; then continue; fi
+               case " $id $domain $mx $addr" in
+               */*|' '.*)      fail "bad syntax" ;;
+               esac
+               local td
+               compute-td "mx=$mx,addr=$addr"
+               >"$td/ok" ||:
+               return
        done
 }
 
-mode_auto () {
+mode_all () {
        no_args $#
-       xxx do something to cause sleeping
-       mode_all
+       for domain in $MODRELAYS; do
+               probe-domain $domain
+       done
 }
 
-mode_relay () {
-       relay="$1"
-       probe "$relay"
+mode_domain () {
+       for domain in "$@"; do
+               probe-domain $domain
+       done
 }
 
 mode=$1; shift||: