#!/bin/bash
#< Script to forcibly remove messages from sendmail mail queue

AWK="/bin/awk"
BASENAME="/bin/basename"
DATE="/bin/date"
ECHO="/bin/echo"
EGREP="/bin/egrep"
GREP="/bin/grep"
FIND="/usr/bin/find"
HOSTNAME="/bin/hostname"
KILL="/bin/kill"
PS="/bin/ps"
RM="/bin/rm"
SED="/bin/sed"
SENDMAIL="/usr/lib/sendmail"
SERVICE="/sbin/service"
SLEEP="/bin/sleep"
WC="/usr/bin/wc"
XARGS="/usr/bin/xargs"

THISPROG="$( ${BASENAME} $0 )"
MY_HOSTNAME="$( ${HOSTNAME} )"

MQUEUE="/var/spool/mqueue"
LOGFILE="/var/log/mailq_purge.log"
NUM_HOURS="INVALID"  # force user to specify -n (see check_args below)

E_SUCCESS=0
E_ERROR=1

function print_usage {
   {
      ${ECHO} "Usage: ${THISPROG} [-h] -n <num_hours>"
      ${ECHO} "       -n   Remove messages older than <num_hours>"
      ${ECHO} "       -h   Display this usage message"
   } >&2
}

function print_log {
   TIMESTAMP=$( ${DATE} )
   LEVEL="$1"
   MESSAGE="$2"
   ${ECHO} "${TIMESTAMP} ${MY_HOSTNAME} ${LEVEL} ${MESSAGE}" >> ${LOGFILE}
}

function check_args {
   ${ECHO} "${NUM_HOURS}" | ${EGREP} -q '^[0-9]+$' || {
      print_log "ERROR" "Incorrect arguments passed to script"
      print_usage
      exit ${E_ERROR}
   }
}

function check_num_mailq {
   NUMQ=$( ${SENDMAIL} -bp -OMaxQueueRunSize=1 | ${SED} -n '1 s/^[^(]*(\([0-9][0-9]*\) r.*$/\1/p' )
   ${ECHO} "${NUMQ}" | ${EGREP} -q '^[0-9]+$' || {
      print_log "ERROR" "Unable to get number of messages in mailq"
      exit ${E_ERROR}
   }
   ${ECHO} "${NUMQ}"
}

function check_sendmail {
   SENDMAIL_PROCESSES=$( ${PS} -ef | ${GREP} '[s]endmail' | ${WC} -l | ${AWK} '{print $1}' )
   if [ "${SENDMAIL_PROCESSES}" -eq "0" ]; then
      print_log "INFO" "There are no sendmail processes running"
   else
      print_log "INFO" "There are ${SENDMAIL_PROCESSES} sendmail processes running"
   fi 
   ${ECHO} "${SENDMAIL_PROCESSES}"
}

function stop_sendmail {
   COUNTER=0
   print_log "INFO" "Attempting to stop sendmail gracefully"
   ${SERVICE} sendmail stop >/dev/null 2>&1
   while [ "${COUNTER}" -lt "6" ]; do
      STILL_RUNNING=$( check_sendmail )
      if [ "${STILL_RUNNING}" -ne "0" ]; then
         print_log "WARNING" "Sendmail still running after $(( ${COUNTER} * 5 )) seconds"
      else
         print_log "INFO" "Sendmail stopped"
         return
      fi
      ${SLEEP} 5
      (( COUNTER = COUNTER + 1 ))
   done
   print_log "WARNING" "About to kill sendmail forcefully - sending SIGTERM"
   ${PS} -ef | ${GREP} '[s]endmail' | ${AWK} '{print $2}' | ${XARGS} ${KILL} -15
   ${SLEEP} 5
   STILL_RUNNING=$( check_sendmail )
   if [ "${STILL_RUNNING}" -ne "0" ]; then
      print_log "WARNING" "Sendmail still running after SIGTERM - sending SIGKILL"
   else
      print_log "INFO" "Sendmail killed"
      return
   fi
   ${PS} -ef | ${GREP} '[s]endmail' | ${AWK} '{print $2}' | ${XARGS} ${KILL} -9
   ${SLEEP} 5
   STILL_RUNNING=$( check_sendmail )
   if [ "${STILL_RUNNING}" -ne "0" ]; then
      print_log "ERROR" "Sendmail still running after SIGKILL - Good luck with that"
      exit ${E_ERROR}
   else
      print_log "INFO" "Sendmail killed"
      return
   fi
   # never reached
   return
}

function start_sendmail {
   print_log "INFO" "Attempting to start sendmail"
   ${SERVICE} sendmail start >/dev/null 2>&1
   ${SLEEP} 5
   IS_RUNNING=$( check_sendmail )
   if [ "${IS_RUNNING}" -eq "0" ]; then
      print_log "ERROR" "Could not start sendmail"
      exit ${E_ERROR}
   else
      print_log "INFO" "Sendmail started"
      return
   fi
   
}

function purge_mailq {
   print_log "INFO" "About to purge mail queue - removing anything older than ${NUM_HOURS} hours"
   ${FIND} "${MQUEUE}" -type f -mmin +$(( 60 * ${NUM_HOURS} )) -print0 | ${XARGS} -0 ${RM} -f
   print_log "INFO" "Mail queue purged"
}

while getopts ":hn:" OPTION; do
  case ${OPTION} in
     "h")  print_usage && exit 0   ;;
     "n")  NUM_HOURS=${OPTARG}     ;;
     *  )  print_usage && exit 1   ;;
  esac
done

shift $(( ${OPTIND} - 1 ))

if [ "$#" -ne "0" ]; then
   print_usage && exit 1
fi

check_args

NUMQ_BEFORE=$( check_num_mailq )
print_log "INFO" "${NUMQ_BEFORE} messages in mailq before processing"

PROCS_BEFORE=$( check_sendmail )
if [ "${PROCS_BEFORE}" -eq "0" ]; then
   print_log "ERROR" "Nothing to do - sendmail not running" && exit ${E_ERROR}
fi

stop_sendmail
purge_mailq
start_sendmail

NUMQ_AFTER=$( check_num_mailq )
print_log "INFO" "${NUMQ_AFTER} messages in mailq after processing"

exit ${E_SUCCESS}