#! /bin/ksh
#
# ident @(#)immonitor-system	1.11 12/03/98 SMI
#
# Copyright 1998 Sun Microsystems, Inc. All Rights Reserved.
# All Rights Reserved.
#
# Internet Mail System Resources Monitoring Script
# Reports the system resource utilization
#
# Best viewed with tabstop=4

PATH=$PATH:/opt/SUNWmail/sbin:/usr/sbin:/sbin:.
export PATH

PID=$$; export PID

RCPTS_FILE=/tmp/system.mailto.$PID
MAIL_FILE=/tmp/system.mailfile.$PID
SUBJ_FILE=/tmp/system.subject.$PID
LOG_FILE=/tmp/system.log.$PID

export LOG_FILE

export gettext=/usr/bin/gettext
export TEXTDOMAIN=monitor_script

trap 'rm -f $LOG_FILE $RCPTS_FILE $MAIL_FILE $SUBJ_FILE' 0 1 2 3 15 

rm -f $LOG_FILE 

Usage ()
{
	#
	# L10N:
	#
	# Translate threshold and alert_recipients
	#

	echo `$gettext "Usage:"`
	echo "\timmonitor system [-f filesystem=threshold] [-m threshold]"
	echo "\t [-p protocol=threshold] [-r alert_recipients] [-hdv]\n"

	echo `$gettext " [-h]: Display the usage message"`
	echo `$gettext " [-d]: Display the execution steps"`
	echo `$gettext " [-v]: Run in verbose mode"`
	echo `$gettext " [-f filesystem=threshold]: The filesystem to be monitored"`
	echo `$gettext " [-m threshold]: The swap space threshold to be monitored"`
	# L10N: Dont translate "ESTABLISHED"
	echo `$gettext " [-p protocol=threshold]: No. of ESTABLISHED connections to be monitored"`
	echo `$gettext " [-r alert_recipients]: Comma separated list of mail recipients to notify"`
	# L10N: Dont translate SMTP
	echo `$gettext " [-A Host]: Alternate SMTP host to connect to."`
	echo
}

rm -f $RCPTS_FILE $MAIL_FILE $SUBJ_FILE
fflag=0; mflag=0; pflag=0; rflag=0; Debug=0; vflag=0; doAll=0;

# SplitParam_f - Splits the Filesystem=Num[type] into separate args.
# Function to split the argument to -f
# A print "" - indicates error.

SplitParam_f ()
{
	echo $* | nawk -F"=" '{ 
		if (NF != 2) { 
				print ""
				exit (1);
		}

		if (match ($2, "[%kmg]") == 0) {
			print "" ;
			exit (1);
		}
		else {
			split ($2, Arr, "[%kmg]") ;
			len = length (Arr[1]) ; len++ ; 
			SubStr = substr ($2, len, 1);  ## Return the Threshold type

		}
		printf "%s %s %s\n", $1, Arr[1], SubStr ;
		exit (0);
	}'
}

# SplitParam_m - Splits the Num[type] into separate args.
# Function to split the argument to -f
# A print "" - indicates error.

SplitParam_m ()
{
	echo $* | nawk '{
		if (NF != 1) {
				print ""
				exit (1);
		}

		if (match ($1, "[%kmg]") == 0) {
			print "" ;
			exit (1);
		}
		else {
			split ($1, Arr, "[%kmg]") ;
			len = length (Arr[1]) ; len++ ;
			SubStr = substr ($1, len, 1);  ## Return the Threshold type
		}

		printf "%s %s\n", Arr[1], SubStr ;
		exit (0);
	}'
}

# SplitParam_p - Splits the Protocol=Num into separate args.
# Function to split the argument to -p
# A print "" - indicates error.

SplitParam_p ()
{	
	echo $* | nawk -F"=" '{
		if (NF != 2) { 
				print ""
				exit (1);
		}
		printf "%s %s\n", $1, $2
	}'
}

GetFirstArg ()
{
	echo $1 ;
}

GetSecondArg ()
{
	shift ;
	echo $1;
}

GetThirdArg ()
{
	shift; shift;
	echo $1
}

FileSystem="" ; FileThresh="" ; FileThreshType="" ;
SwapThresh="" ; SwapThreshType="" ;
ProtoCol="" ; ProtoThresh="" ;

while getopts :f:m:p:r:A:hdv var
do
	case $var in
	f)	Param=`SplitParam_f $OPTARG`

		if [[ -z $Param ]]
		then
			echo
			echo `$gettext "The argument to -f should be of the form filesys=num[type]"`
			echo "\t" `$gettext "Where type is either k,m,g or %"`
			echo
			Usage ;
			exit 64 ;
		fi

		FSys=`GetFirstArg $Param`
		FThr=`GetSecondArg $Param`
		FTyp=`GetThirdArg $Param`

		if [[ (-z $FSys) || (-z $FThr) || (-z $FTyp) ]]
		then
			echo
			echo `$gettext "The argument to -f should be of the form filesys=num[type]"`
			echo "\t" `$gettext "Where type is either k,m,g or %"`
			echo
			Usage ;
			exit 64 ;
		fi

		# Concatenate the multiple -f's

		FileSystem="$FileSystem $FSys" 
		FileThresh="$FileThresh $FThr"
		FileThreshType="$FileThreshType $FTyp"

		((fflag=fflag+1)) ;;

	m)	Param=`SplitParam_m $OPTARG`

		if [[ -z $Param ]]
		then
			echo
			echo `$gettext "The argument to -m should be of the form num[type]"`
			echo "\t" `$gettext "Where type is either k,m,g or %"`
			echo
			Usage ;
			exit 64;
		fi

		SThr=`GetFirstArg $Param` 
		STyp=`GetSecondArg $Param`

		if [[ (-z $SThr) || (-z $STyp) ]]
		then
			echo
			echo `$gettext "The argument to -m should be of the form num[type]"`
			echo "\t" `$gettext "Where type is either k,m,g or %"`
			echo
			Usage ;
			exit 64 ;
		fi

		# Concatenate the multiple -m's

		SwapThresh="$SwapThresh $SThr"
		SwapThreshType="$SwapThreshType $STyp"

		((mflag=mflag+1)) ;;

	p)	Param=`SplitParam_p $OPTARG`

		if [[ -z $Param ]]
		then
			echo
			echo `$gettext "The argument to -p should be of the form protocol=num"`
			echo
			Usage ;
			exit 64 ;
		fi

		PCol=`GetFirstArg $Param`
		PThr=`GetSecondArg $Param`

		if [[ (-z $PCol) || (-z $PThr) ]]
		then
			echo
			echo `$gettext "The argument to -p should be of the form protocol=num"`
			echo
			Usage ;
			exit 64 ;
		fi

		# Concatenate the multiple -p's

		ProtoCol="$ProtoCol $PCol"
		ProtoThresh="$ProtoThresh $PThr"

		((pflag=pflag+1)) ;;	

	r)	Recpts=$OPTARG
		rflag=1 ;;

	A)  AltHost=$OPTARG  ;; # Alternate SMTP host to connect to

	d | v)	Debug=1 ;;

	: | \?)	Usage
		exit 64 ;;

	h)	Usage ;
		exit 64 ;;
	esac
done

if [[ ($fflag -eq 0) && ($mflag -eq 0) && ($pflag -eq 0) ]]
then
	doAll=1;
fi

if [[ $Debug -eq 1 ]]; then
set -x
fi

TAILOR_FILE=/etc/opt/SUNWmail/imta/imta_tailor
IMS_CNF=/etc/opt/SUNWmail/ims/ims.cnf

if [[ ($doAll -eq 1) || ($fflag -gt 0) ]]
then

	if [[ $doAll -eq 1 ]]
	then
		# Find the filesystems for the imta/ims

		FSimta=`nawk -F"=" '{ if ($1 == "IMTA_ROOT") print $2 }' $TAILOR_FILE`
		FSims=`nawk -F":" '{ if ($1 == "ims-data-root") print $2 }' $IMS_CNF`

		if [[ (-z $FSimta) ]]; then
			# L10N: Dont translate IMTA_ROOT
			$gettext "IMTA_ROOT not found in: "
			echo $TAILOR_FILE
			exit 66; # EX_NOINPUT from sysexits.h
		fi
		if [[ (-z $FSims) ]]; then
			#L10N: dont translate ims-data-root
			$gettext  "ims-data-root Not found in: "
			echo $IMS_CNF
			exit 66; # EX_NOINPUT from sysexits.h
		fi

		FileSystem="$FSimta $FSims"
	fi

	# Read the df o/p beginning 2nd line. The first line contains headers

	df -k $FileSystem | tail +2 | nawk -v Doall=$doAll -v fileSys="$FileSystem" \
		-v logfile=$LOG_FILE -v fileThresh="$FileThresh" \
		-v fileThtype="$FileThreshType"  '
			BEGIN {
				gettext = "/usr/bin/gettext"

		# A variable msGstr is used to help  xgettextsh.sh pick up the messages
		# For localization. While adding a new message take care to add a msGstr
		# with the same values. msGstr contains the actual message.

				if (Doall == 1) {
					msGstr = gettext "Disk usage:" ;
					"gettext \"Disk usage:\" " | getline format ;
					close ("gettext \"Disk usage:\" ");
					print format ;

					msGstr = gettext "\tFilesystem\tused\tcapacity"
					"gettext \"\tFilesystem\tused\tcapacity\" " | getline format ;
					close ("gettext \"\tFilesystem\tused\tcapacity\" ");
					printf format ; printf "\n" ;
				}

				split (fileSys, fSys, " ");
				split (fileThresh, fThr, " ") ;
				split (fileThtype, fTyp, " ") ;
			}

			function alert (fSystem, currVal, tHresh, type) {
				msGstr = gettext "ALERT: <%s> exceeds threshold" ;
				"gettext \"ALERT: <%s> exceeds threshold\" " | getline format ;
				close ("gettext \"ALERT: <%s> exceeds threshold\" ");
				printf format, fSystem ; printf "\n" ;

				msGstr = gettext "\tCurrent value = %0.3f%s/Threshold = %0.3f%s" ;
				"gettext \"\tCurrent value = %0.3f%s/Threshold = %0.3f%s\" " | getline format ;
				close ("gettext \"\tCurrent value = %0.3f%s/Threshold = %0.3f%s\" ") ;
				printf format, currVal, type, tHresh, type ; printf "\n" ;

				# syslog cant handle Internationalized data, so ...
				printf "ALERT: <%s>  exceeds threshold\n", fSystem >> logfile ;
				printf "Current Value = %0.3f%s/Threshold = %0.3f%s\n",
					currVal, type, tHresh, type >> logfile ;
			}

			{
				if (Doall == 1)
					printf "\t%s\t%dk\t%s\n", fSys[NR], $3, $5

				else {
					if (index (fTyp[NR], "k") == 1) { # Type is 'k' ;
						if ($3 > fThr[NR])
							alert(fSys[NR], $3, fThr[NR], fTyp[NR]);
					}
					else if (index (fTyp[NR], "m") == 1) {
						Usg=$3/1024 ;
						if (Usg > fThr[NR])
							alert(fSys[NR], Usg, fThr[NR], fTyp[NR]);
					}
						
					else if (index (fTyp[NR], "g") == 1) {
						Usg=$3/(1024*1024) ;
						if (Usg > fThr[NR])
							alert(fSys[NR], Usg, fThr[NR], fTyp[NR]);
					}
					else if ((ind = index (fTyp[NR], "%")) == 1) {
						Len = length ($5);
						Usg = substr ($5, 1, (Len-ind));
						if (Usg > fThr[NR])
							alert(fSys[NR], Usg, fThr[NR], fTyp[NR]);
					}
					else {
					}
				}
	}' > $MAIL_FILE
fi

if [[ ($doAll -eq 1) || ($mflag -gt 0) ]]
then
	swap -s | nawk  -v Doall=$doAll -v sThresh="$SwapThresh" \
		-v logfile=$LOG_FILE -v sThrtype="$SwapThreshType" '
			BEGIN {
				gettext = "/usr/bin/gettext"
				split (sThresh, sThr, " ");
				split (sThrtype, sTyp, " ") ;
			}

			function alert (currVal, tHresh, type) {

				msGstr = gettext "ALERT: <%s> exceeds threshold" ;
				"gettext \"ALERT: <%s> exceeds threshold\" " | getline format ;
				close ("gettext \"ALERT: <%s> exceeds threshold\" ") ;
				printf format, "Swap space" ; printf "\n" ;

				msGstr = gettext "\tCurrent value = %0.3f%s/Threshold = %0.3f%s" ;
				"gettext \"\tCurrent value = %0.3f%s/Threshold = %0.3f%s\" " | getline format ;
				close ("gettext \"\tCurrent value = %0.3f%s/Threshold = %0.3f%s\" ");
				printf format, currVal, type, tHresh, type ; printf "\n" ;

				# syslog cant handle Internationalized data, so ...

				printf "ALERT: <%s>  exceeds threshold\n", "Swap Space" >> logfile ;
				printf "Current Value = %0.3f%s/Threshold = %0.3f%s\n",
					currVal, type, tHresh, type >> logfile ;
			}
			{
				split ($9, Used, "k") ;
				split ($11, Free, "k") ;
				Total = Used[1] + Free[1] ;

				if (Doall == 1) {
					printf "\n" ;
					printf "Swap space:\n"

					msGstr = gettext "\tkbytes\t\tcapacity" ;
					"gettext \"\tkbytes\t\tcapacity\" " | getline format ;
					close ("gettext \"\tkbytes\t\tcapacity\" ");
					printf format ; printf "\n" ;

					printf "\t%s\t\t%d%\n", $9, (Used[1]/Total)*100 ;
				}
				else {

					if (index (sTyp[NR], "k") == 1) { # Type is 'k' ;
						if (Used[1] > sThr[NR])
							alert(Used[1], sThr[NR], sTyp[NR]);
					}
					else if (index (sTyp[NR], "m") == 1) {
						Usg=Used[1]/1024 ;
						if (Usg > sThr[NR])
							alert(Usg, sThr[NR], sTyp[NR]);
					}
							
					else if (index (sTyp[NR], "g") == 1) {
						Usg=Used[1]/(1024*1024) ;
						if (Usg > sThr[NR])
							alert(Usg, sThr[NR], sTyp[NR]);
					}
					else if (index (sTyp[NR], "%") == 1) {
						Usg = (Used[1]/Total)*100 ;
						if (Usg > sThr[NR])
								alert(Usg, sThr[NR], sTyp[NR]);
					}
					else {
					}
				}	
		}' >>  $MAIL_FILE
fi	

if [[ ($doAll -eq 1) || ($pflag -gt 0) ]]
then
	netstat -P tcp -f inet -n | tail +5 | nawk -v Doall=$doAll \
		-v logfile=$LOG_FILE -v prtCol="$ProtoCol" -v prtThresh="$ProtoThresh" '
			BEGIN {
				gettext = "/usr/bin/gettext"

				if (Doall == 1) {
					pCol[1]="25"  ; pName[1]="smtp" ;
					pCol[2]="143" ; pName[2]="imap" ;
					pCol[3]="110" ; pName[3]="pop3"  ;
					pCol[4]="993" ; pName[4]="imaps";
					pCol[5]="995" ; pName[5]="pop3s";
					pCol[6]=""    ; pName[6]=""     ;
				}
				else {
					split (prtCol, pName, " ");
					for (i=1; pName[i]; i++) {
						if (tolower(pName[i]) == "smtp") {
							pCol[i]="25"  ; pName[i]="smtp" ;
						}
						else if (tolower(pName[i]) == "imap") {
							pCol[i]="143" ; pName[i]="imap" ;
						}
						else if (tolower(pName[i]) == "pop3") {
							pCol[i]="110" ; pName[i]="pop"  ;
						}
						else if (tolower(pName[i]) == "imaps") {
							pCol[i]="993" ; pName[i]="imaps";
						}
						else if (tolower(pName[i]) == "pop3s") {
							pCol[i]="995" ; pName[i]="pop3s";
						}
						else {
						}
					}
					pCol[i]=""    ; pName[i]=""     ;
					split (prtThresh, pThr, " ") ;
				}
			}

			function alert (pRot, currVal, tHresh) {
				if ( pRot == 25)
					name = "smtp" ;
				else if (pRot == 143)
					name = "imap" ;
				else if (pRot == 110)
					name = "pop" ;
				else if (pRot == 993)
					name = "imaps" ;
				else if (pRot == 995)
					name = "pop3s" ;
				else
					name = "" ;

		# L10N: Dont translate ESTABLISHED
				msGstr=gettext "ALERT: <%s> ESTABLISHED connections exceed threshold";
				"gettext \"ALERT: <%s> ESTABLISHED connections exceed threshold\" " | getline format
				close ("gettext \"ALERT: <%s> ESTABLISHED connections exceed threshold\" ");
				printf format, name ; printf "\n" ;

				msGstr = gettext "\tCurrent value = %d/Threshold = %d" ;
				"gettext \"\tCurrent value = %d/Threshold = %d\" " | getline format ;
				close ("gettext \"\tCurrent value = %d/Threshold = %d\" ") ;
				printf format, currVal, tHresh ; printf "\n" ;

				# syslog cant handle Internationalized data, so ...

				printf "ALERT: <%s> ESTABLISHED connections exceed threshold\n",
					name >> logfile ;
				printf "Current value = %d/Threshold = %d\n",
					currVal, tHresh >> logfile ;
			}

			{	if ($0) {
					num=split ($1, prtIn, ".");
					for (i=1; pCol[i]; i++)
						if ( (prtIn[num] == pCol[i]) && ($7 == "ESTABLISHED"))
							Count[pCol[i]]++;
				}
			}
			END {
				if (Doall == 1) {
					printf "\n" ;
					msGstr = gettext "ESTABLISHED connections:" ;
					"gettext \"ESTABLISHED connections:\" " | getline format ;
					close ("gettext \"ESTABLISHED connections:\" ") ;
					printf format ; printf "\n" ;
				}

				for (i=1; pCol[i]; i++) {
					if (Doall == 1) 
						printf "\t%s\t%d\n", pName[i], Count[pCol[i]] ;
					else {
						if (Count[pCol[i]] > pThr[i])
							alert(pCol[i], Count[pCol[i]], pThr[i]);
					}
				}
			}' >>  $MAIL_FILE
fi

SendMail ()
{

# Do not indent. Some of the commands to mconnect need to
# appear before any space/tab.

mconnect $1 << _EOF > /dev/null
helo `uname -n`
mail from: `logname`
`cat $RCPTS_FILE`
data
`cat $SUBJ_FILE`
From: SIMS Admin <`logname`>

`cat $MAIL_FILE`
.
quit
_EOF

}

RetStat=0;

Log_Message () 
{
	# ^$ strips off the empty lines from the file. tr deletes the tabs

	grep -v '^$' $LOG_FILE | /usr/bin/logger -p mail.alert \
	-t "SUNWmail.Monitor.system" -i 
}

if [[ ($rflag -eq 1) && ($doAll -eq 0) ]]
then
	# The Recpts can be comma separated mail addresses.  So we use nawk to
	# break then up and put them in a file. This is required since each of
	# the recipients to mconnect need to be specified with "rpct to:"

	echo $Recpts | nawk '{
		i=1;
		split ($0, Arr,",");
		while (Arr[i])
			printf "rcpt to: %s\n", Arr[i++]	

	}' > $RCPTS_FILE
		
	# If AltHost is not specified, localhost is used by default.

	if [[ (-s $MAIL_FILE) && (-s $RCPTS_FILE) ]]
	then

# L10N: Dont translate "Subject: ......" string,
# "Content-Type:", "Content-Transfer-Encoding:" and  "charset="

		$gettext "Subject: ALERT: immonitor: System thresholds exceeded for: " > $SUBJ_FILE
		hostname >> $SUBJ_FILE
		echo "MIME-Version: 1.0" >> $SUBJ_FILE

# L10N: Look up the following table and insert the charset corresponding to
# your language
# --------------------------------------------------------------------------
# English 		:	US-ASCII
# Japanese      :   EUC-JP
# European languages:   ISO-8859-1
# Simplified Chinese:   EUC-CN
# Traditional Chinese:   EUC-TW
# Korean      :   EUC-KR
# --------------------------------------------------------------------------
#
		echo `$gettext "Content-Type: text/plain ; charset=US-ASCII"` >> $SUBJ_FILE
		echo `$gettext "Content-Transfer-Encoding: 8bit"` >> $SUBJ_FILE

		SendMail $AltHost
		if [[ $? -eq 0 ]]; then
			echo `$gettext "Mail sent to :-"` $Recpts
		else
			echo `$gettext "Could not send mail to :-"` $Recpts
		fi
		Log_Message ;
		RetStat=1 ;
	fi
else
	cat $MAIL_FILE

	# If the MAIL_FILE has some data AND the doAll == 0, then one/all of the
	# thresholds have been crossed, so we return a non-zero exit value.

	if [[ (-s $MAIL_FILE) && ($doAll -eq 0) ]] ; then
		Log_Message ;
		RetStat=1 ;
	fi
fi

exit $RetStat ;
