#!/bin/bash
# Nagios Plugin to check openfiles by a program and it's child processes
# nasim.ansari at gmail.com : 30 Sep 2011
# Version 1.0
#
# Valid Options:
# -p ProgramName        : program name or string listed in ps -ef
# -W ProgramWarlevel    : Number of FD for Program Warning Level
# -C ProgramCriticlevel : Number of FD for Program Critical level
# -w ProgramWarlevel    : Number of FD for Program Warning Level
# -c ProgramCriticlevel : Number of FD for Program Critical level
#

# Exit Status
okExit=0
warningExit=1
criticalExit=2
unknownExit=3

MESSAGE=""

# Command Variable
AWK=/bin/awk
LSOF=/usr/sbin/lsof

## Check lsof path and suid bit
if [ -f $LSOF ]; then
        if [ -u $LSOF ]; then
                :
        else
                echo "$LSOF need suid bit"
                exit $unknownExit
        fi
else
        echo "$LSOF doesnot exist"
        exit $unknownExit
fi


# Syntax for this script
function printhelp {
echo 'This script is to check open file by a program and its Child process.'
echo "Usages: $0 -p ProgramName -W ProgramWarningLevel -C ProgramCriticalLevel -w ChildProgramWarningLevel -c ChildProgramCriticalLevel"
echo 'Where:
        -p ProgramName                  : Program Name for which check need to be performed. Mandatory
        -W ProgramWarningLevel          : Programs Warning Level (def : 400)
        -C ProgramCriticalLevel         : Programs Critical Level (def : 512)
         -w ChildProgramWarningLevel    : Warning Level for any child of program (def : 400)
        -c ChildProgramCriticalLevel    : Critical Level for any child of program (def : 512)'
exit $unknownExit
}

# Let's validate command line parameters

while getopts "hp:W:C:w:c:" optionName; do
   case "$optionName" in
        p) ProgName="$OPTARG";;
        W) ProgWlevel="$OPTARG";;
        C) ProgClevel="$OPTARG";;
        w) SubProgWlevel="$OPTARG";;
        c) SubProgClevel="$OPTARG";;
        *) printhelp;;
   esac
done

# Programe name is mandatory option
[ -z "$ProgName" ] && printhelp

# Set default values
[ -z "$ProgWlevel" ] && ProgWlevel=400
[ -z "$ProgClevel" ] && ProgClevel=512
[ -z "$SubProgWlevel" ] && SubProgWlevel=400
[ -z "$SubProgClevel" ] && SubProgClevel=512

# Enable this if you are debugging
#echo -n "Running ...: "
#echo $0 -p $ProgName -W $ProgWlevel -C $ProgClevel -w $SubProgWlevel -c $SubProgClevel


# Get the PID of program(s)
PidOfProg="`ps -ef|grep $ProgName|grep -Ewv "grep|$0"|awk '{print $2}'`"
[ -z "$PidOfProg" ] && echo "UNKNOWN: Not able to determine PID of $ProgName" && exit $unknownExit

# Collect lsof output in /var/tmp
lsoffile=/var/tmp/lsoffile.$$
$LSOF >$lsoffile

# Let's check how many files are opened by ParentsPid
for ParentPid in $PidOfProg ; do
   ChildProcPIDs="`ps -ef| awk -v pid=$ParentPid '$3 == pid {print $2}'`"
   NoOfOpenFiles="`awk -v pid=$ParentPid '$2 == pid ' $lsoffile|wc -l`"
       if [ $NoOfOpenFiles -ge $ProgClevel ] ;then
          MESSAGE="pid $ParentPid has $NoOfOpenFiles open files. "
          CRITICAL=yes
       elif [ $NoOfOpenFiles -ge $ProgWlevel ] ;then
          MESSAGE="$MESSAGE pid $ParentPid has $NoOfOpenFiles open files. "
          WARNING=yes
       else
          MESSAGE="Open FD = $NoOfOpenFiles of $ProgName PID=$PidOfProg is less than critical limits."
          OK=yes
       fi
done

# Let's check for Child processes
if [ ! -z "$ChildProcPIDs" ];then
    for ChildPid in $ChildProcPIDs ; do
     NoOfOpenFiles="`awk -v pid=$ChildPid '$2 == pid ' $lsoffile|wc -l`"
       if [ $NoOfOpenFiles -ge $SubProgClevel ] ;then
          MESSAGE="$MESSAGE ChildPid $ChildPid : $NoOfOpenFiles "
          CRITICAL=yes
       elif [ $NoOfOpenFiles -ge $SubProgWlevel ] ;then
          MESSAGE="$MESSAGE ChildPid $ChildPid : $NoOfOpenFiles "
          WARNING=yes
       fi
   done
fi

# Remove lsof on each run
[ -f $lsoffile ] && rm -f $lsoffile

# Display Message and exit with correct exit status
[ "$CRITICAL" = yes ] && echo "CRITICAL: $MESSAGE"  && exit $criticalExit
[ "$WARNING" = yes  ] && echo "WARNING: $MESSAGE"  && exit $warningExit
[ "$OK" = yes  ] && echo "OK: $MESSAGE"  && exit $okExit
