#!/bin/bash version=0.10 program=${0##*/} progdir=${0%/*} if [ "$progdir" = "$program" ]; then progdir="."; fi # ---------------------------------------------------------------------- function usage() { # Possible sections: # NAME # SYNOPSIS # CONFIGURATION [Normally only in Section 4] # DESCRIPTION # OPTIONS [Normally only in Sections 1, 8] # EXIT STATUS [Normally only in Sections 1, 8] # RETURN VALUE [Normally only in Sections 2, 3] # ERRORS [Typically only in Sections 2, 3] # ENVIRONMENT # FILES # VERSIONS [Normally only in Sections 2, 3] # CONFORMING TO # NOTES # BUGS # EXAMPLE # SEE ALSO cat <. Repository: https://svn.tools.ietf.org/svn/tools/utils/trunk/check-mail-pipeline COPYRIGHT Copyright 2018 the IETF Trust. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the conditions laid out in the 3-clause BSD license is followed. License text: https://opensource.org/licenses/BSD-3-Clause EOF } # ---------------------------------------------------------------------- bold=$(tput bold) red=$(tput setaf 1) green=$(tput setaf 2) reset=$(tput sgr 0) function die() { echo -e "\n$program: error: $*" > /dev/stderr exit 1 } typeset -i errors=0 function err() { fail echo -e "${red}$*${reset}" > /dev/stderr errors=$(( $errors + 1 )) } function note() { if [ -n "$VERBOSE" ]; then echo -e "\n$*"; fi } function label() { note "$*" label=$(echo $*) } function item() { echo -n "[....] $label: $*" } function ok() { echo -e "\r[ ${bold}${green}OK${reset} ]" } function fail() { echo -e "\r[${bold}${red}FAIL${reset}]" } # ---------------------------------------------------------------------- function version() { echo -e "$program $version" } # ---------------------------------------------------------------------- trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR # ---------------------------------------------------------------------- # Option parsing # Options shortopts=hvV longopts=help,verbose,version # Default values if [ "$(uname)" = "Linux" ]; then args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi eval set -- "$args" sed="sed -r" else # Darwin, BSDs args=$(getopt -o$shortopts $SV $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args sed="sed -E" fi while true ; do case "$1" in -h| --help) usage; exit;; # Show this help, then exit -v| --verbose) VERBOSE=1;; # Be more talkative -V| --version) version; exit;; # Show program version, then exit --) shift; break;; *) die "Internal error, inconsistent option specification: '$1'";; esac shift done # ---------------------------------------------------------------------- # The program itself function check_perm() { perm="$1" owner="$2" file="$3" item $file _errors=$errors if [ -e "$file" ]; then set -- $(ls -l $file) [ $perm = $1 ] || err "Expected permissions $perm but found $1 for $file" [ $owner = "$3:$4" ] || err "Expected owner $owner but found $3:$4 for $file" else err "The file '$file' is missing" fi [ $_errors = $errors ] && ok } # ---------------------------------------------------------------------- tempfile=$(mktemp) label "Check listeners" item "postfix" lsof -i -a -c master > $tempfile if fgrep -q '*:smtp ' $tempfile; then ok; else err "Did not find a master listener on port 25"; fi for port in 10025 10026 10027 10028; do item "port $port" if grep -q "localhost:$port" $tempfile; then ok; else err "Did not find a master listener on port $port"; fi done item "amavis" lsof -i -a -u vscan +c0 > $tempfile if grep -q "/usr/sbin/amavi.*localhost:10024" $tempfile; then ok; else err "Did not find an amavis listener on port 10024"; fi item "opendkim" lsof -i -a -c opendkim > $tempfile if grep -q ":ddi-tcp-4" $tempfile; then ok; else err "Did not find an opendkim listener on port 8891"; fi # ---------------------------------------------------------------------- label "Check binary locations" # postconfirmd socket should exist # postconfirmc client needs suid in order to have the right user when called from .procmail for cmd in postconfirmc postconfirmd ; do item "$cmd" found="$(for file in $(type -aP $cmd); do readlink -f $file; done | uniq)" count=$(echo "$found" | wc -l) if [ $count = 1 ]; then ok; else err "Expected to find one and only one $cmd binary, but found $count:\n$found"; fi done # ---------------------------------------------------------------------- label "Check running processes" for bin in /usr/bin/postconfirmd; do cmd=${bin##*/} item "$bin" if ps -f -C "$cmd" | grep -q "$bin"; then ok; else err "Expected $bin to be running, but didn't find it"; fi done # ---------------------------------------------------------------------- label "Check systemd status" for service in postconfirm; do item "postconfirm" if systemctl is-active -q $service; then ok; else err "Systemctl says $service is not running"; fi done # ---------------------------------------------------------------------- label "Check ownership and permissions" check_perm -rwsr-xr-x nobody:mailman /usr/bin/postconfirmc check_perm -rwxr-xr-x root:root /usr/bin/postconfirmd # ---------------------------------------------------------------------- label "Check mail to RT" ID="$(uuidgen)@$(hostname -f)" mail="To: \"IETF Action\" From: \"Henrik Levkowetz\" Delivered-To: ietf-action@ietf.org Subject: Test $(date) Date: $(date -R) Return-Path: Content-Type: text/plain; charset=\"utf-8\" Content-Transfer-Encoding: 7bit Message-ID: <$ID> Test, please disregard. $(date) " item "send mail" sendtime=$(date +'%Y-%m-%dT%H:%M') if mailreplay <(echo "$mail"); then ok; else err "Sending mail to RT failed"; fi logtmp=$(mktemp) postfind -q -f "$ID" /var/log/mail > $logtmp item "/var/log/mail: delivery" if grep -q 'delivered to command: /usr/bin/procmail' $logtmp; then ok; else err "The email to RT does not seem to have been delivered. Log lines:"; cat $logtmp; fi # ---------------------------------------------------------------------- label "Check mail to mailing list" ID="$(uuidgen)@$(hostname -f)" mail="To: From: \"Henrik Levkowetz\" Delivered-To: testlist@ietf.org Subject: Test $(date) Date: $(date -R) Return-Path: Content-Type: text/plain; charset=\"utf-8\" Content-Transfer-Encoding: 7bit Message-ID: <$ID> Test, please disregard. $(date) " item "send mail" if mailreplay <(echo "$mail"); then ok; else err "Sending mail to mailman testlist failed"; fi postfind -q -f "$ID" /var/log/mail > $logtmp item "/var/log/mail: delivery" if grep -q "delivered to command: /a/postconfirm/mailman post testlist" $logtmp; then ok; else err "The email to testlist does not seem to have been delivered. Log lines:"; cat $logtmp; fi # ---------------------------------------------------------------------- label "Check for errors and problems in log files" greptmp=$(mktemp) item "/var/log/mail: procmail" if sed -n -e "/$sendtime/,\$p" /var/log/mail | grep -m1 'procmail: Error while writing to ".*postconfirmc"' > $greptmp; then err "Procmail -> postconfirm error:"; echo ""; cat $greptmp; else ok; fi item "/var/log/mail: postconfirmc" if sed -n -e "/$sendtime/,\$p" /var/log/mail | grep -m1 "postconfirm: error" > $greptmp; then err "Postconfirmc error"; echo ""; cat $greptmp; else ok; fi item "/var/log/mail: Traceback" if sed -n -e "/$sendtime/,\$p" /var/log/mail | grep -m1 "Traceback" > $greptmp; then err "Traceback seen"; echo ""; cat $greptmp; else ok; fi # ---------------------------------------------------------------------- # Summary if [ $errors = 0 ]; then echo "All checks passed." elif [ $errors = 1 ]; then echo "Found $errors error." else echo "Found $errors errors." fi exit $errors