#!/bin/bash
#< Nagios Plugin - Check Solaris memory utilisation

BASENAME="/usr/bin/basename"
BC="/usr/bin/bc"
ECHO="/bin/echo"
EGREP="/usr/bin/egrep"
GREP="/usr/xpg4/bin/grep"
PRTCONF="/usr/sbin/prtconf"
SED="/usr/bin/sed"
TOP="/usr/local/bin/top"
UNAME="/usr/bin/uname"

# Variable initialistion
CRITICAL_FLAG="0"
WARNING_FLAG="0"
VERBOSE_FLAG="0"

THIS_PROG=$( ${BASENAME} $0 )

# Exit Status Codes
OK=0
WARNING=1
CRITICAL=2
UNKNOWN=3

function usage {
   {
      ${ECHO} "Usage: ${THIS_PROG} -w <warn> -c <crit> [-v]"
      ${ECHO} "       -c Critical threshold (integer or %)"
      ${ECHO} "       -w Warning threshold (integer or %)"
   } >&2
}

function print_error {
   ${ECHO} "Error: $@" >&2
}

function printv {
   (( VERBOSE_FLAG )) && ${ECHO} "$@"
}

function check_os {
   OS=$( ${UNAME} -s )
   case ${OS} in
      "SunOS") : ;;
      *      ) (( VERBOSE_FLAG )) && {
                  print_error "${OS} is not supported by this plugin"
               }
               ${ECHO} "UNKNOWN: Unsupported Operating System"
               exit ${UNKNOWN}
               ;;
   esac
}

function check_remaining_args {
   if [ "$1" -ne "0" ]; then
      (( VERBOSE_FLAG )) && {
         print_error "Remaining argument count not zero"
      }
      usage
      ${ECHO} "UNKNOWN: Extra arguments passed to plugin"
      exit ${UNKNOWN}
   fi
}

function parse_args {
   ERROR_COUNT=0
   if [ "${CRITICAL_FLAG}" -ne "1" ]; then
      (( VERBOSE_FLAG )) && {
         print_error "-c option not present"
      }
      (( ERROR_COUNT = ERROR_COUNT + 1 ))
   else
      # Let's check that the critical threshold contains a sane value
      ${ECHO} "${CRITICAL_THRESHOLD}" | ${EGREP} '^[0-9]+%?$' >/dev/null 2>&1
      if [ "$?" -ne "0" ]; then
         (( VERBOSE_FLAG )) && {
            print_error "Invalid critical threshold value passed"
         }
         (( ERROR_COUNT = ERROR_COUNT + 1 ))
      fi
      # This should never be true, a usage error would be thrown by the getopts while loop
      if [ -z "${CRITICAL_THRESHOLD}" ]; then
      (( VERBOSE_FLAG )) && {
         print_error "Critical threshold not set"
      }
      (( ERROR_COUNT = ERROR_COUNT + 1 ))
      fi
   fi

   if [ "${WARNING_FLAG}" -ne "1" ]; then
      (( VERBOSE_FLAG )) && {
         print_error "-w option not present"
      }
      (( ERROR_COUNT = ERROR_COUNT + 1 ))
   else
      # Let's check that the warning threshold contains a sane value
      ${ECHO} "${WARNING_THRESHOLD}" | ${EGREP} '^[0-9]+%?$' >/dev/null 2>&1
      if [ "$?" -ne "0" ]; then
         (( VERBOSE_FLAG )) && {
            print_error "Invalid warning threshold value passed"
         }
         (( ERROR_COUNT = ERROR_COUNT + 1 ))
      fi
      # This should never be true, a usage error would be thrown by the getopts while loop
      if [ -z "${WARNING_THRESHOLD}" ]; then
      (( VERBOSE_FLAG )) && {
         print_error "Warning threshold not set"
      }
      (( ERROR_COUNT = ERROR_COUNT + 1 ))
      fi
   fi
   if [ "${ERROR_COUNT}" -gt "0" ]; then
      (( VERBOSE_FLAG )) && {
         print_error "${ERROR_COUNT} error(s) found whilst parsing arguments"
      }
      ${ECHO} "UNKNOWN: Errors encountered passing plugin arguments"
      exit ${UNKNOWN}
   fi
}

function set_mode {
   ${ECHO} "${WARNING_THRESHOLD}" | ${GREP} -q '%$'
   WARN_PERCENT="$?"
   ${ECHO} "${CRITICAL_THRESHOLD}" | ${GREP} -q '%$'
   CRIT_PERCENT="$?"
   if [ "${CRIT_PERCENT}" -eq "0" -a "${WARN_PERCENT}" -eq "0" ]; then
      MODE="percent"
      CRITICAL_THRESHOLD=$( ${ECHO} "${CRITICAL_THRESHOLD}" | ${SED} 's/%$//' )
      WARNING_THRESHOLD=$( ${ECHO} "${WARNING_THRESHOLD}" | ${SED} 's/%$//' )
   elif [ "${CRIT_PERCENT}" -eq "1" -a "${WARN_PERCENT}" -eq "1" ]; then
      MODE="mb"
   else
      (( VERBOSE_FLAG )) && {
          print_error "Specify both -c and -w as a percentage OR integer value"
      }
      ${ECHO} "UNKNOWN: Bad arguments passed"
      exit ${UNKNOWN}
   fi
}

function check_logic {
   if [ "${CRITICAL_THRESHOLD}" -ge "${WARNING_THRESHOLD}" ]; then
      (( VERBOSE_FLAG )) && {
         print_error "Critical threshold must be < warning threshold"
      }
      ${ECHO} "UNKNOWN: Critical threshold >= warning threshold"
      exit ${UNKNOWN}
   fi
}

function do_check {
   PHYSICAL_MEMORY=$( ${PRTCONF} | ${GREP} 'Memory' | ${SED} 's/^.*: \([0-9]*\) M.*$/\1/' )
   FREE_MEMORY=$( ${TOP} -d 1 | ${GREP} '^Memory' | ${SED} 's/^[^,]*, \([0-9]*\)M free.*$/\1/' )
   case ${MODE} in
      "percent" ) PERCENT_FREE=$( ${ECHO} "scale=2; ( ${FREE_MEMORY} / ${PHYSICAL_MEMORY} ) * 100" | ${BC} | ${SED} 's/^\(.*\)\..*/\1/' )
                  if [ "${PERCENT_FREE}" -le "${CRITICAL_THRESHOLD}" ]; then
                     ${ECHO} "CRITICAL: ${PERCENT_FREE}% Free Memory|free=${PERCENT_FREE}%;${WARNING_THRESHOLD};${CRITICAL_THRESHOLD}"
                     exit ${CRITICAL}
                  elif [ "${PERCENT_FREE}" -le "${WARNING_THRESHOLD}" ]; then
                     ${ECHO} "WARNING: ${PERCENT_FREE}% Free Memory|free=${PERCENT_FREE}%;${WARNING_THRESHOLD};${CRITICAL_THRESHOLD}"
                     exit ${WARNING}
                  else
                     ${ECHO} "OK: ${PERCENT_FREE}% Free Memory|free=${PERCENT_FREE}%;${WARNING_THRESHOLD};${CRITICAL_THRESHOLD}"
                     exit ${OK}
                  fi
                  ;;  
      "mb"      ) if [ "${FREE_MEMORY}" -le "${CRITICAL_THRESHOLD}" ]; then
                     ${ECHO} "CRITICAL: ${FREE_MEMORY}Mb Free Memory|free=${FREE_MEMORY}Mb;${WARNING_THRESHOLD};${CRITICAL_THRESHOLD}"
                     exit ${CRITICAL}
                  elif [ "${FREE_MEMORY}" -le "${WARNING_THRESHOLD}" ]; then
                     ${ECHO} "WARNING: ${FREE_MEMORY}Mb Free Memory|free=${FREE_MEMORY}Mb;${WARNING_THRESHOLD};${CRITICAL_THRESHOLD}"
                     exit ${WARNING}
                  else
                     ${ECHO} "OK: ${FREE_MEMORY}Mb Free Memory|free=${FREE_MEMORY}Mb;${WARNING_THRESHOLD};${CRITICAL_THRESHOLD}"
                     exit ${OK}
                  fi
                  ;;
   esac
}

# Nagios recommends multiple level verbosity (e.g. -vvv)
# but we will not bother with that
while getopts ":c:w:v" OPTION; do
  case ${OPTION} in
    "c")   if [ "${CRITICAL_FLAG}" -ne "0" ]; then
              (( VERBOSE_FLAG )) && {
                 print_error "More than one -c option specified"
              }
              ${ECHO} "UNKNOWN: More than one -c option passed to plugin"
              exit "${UNKNOWN}"
           fi
           CRITICAL_FLAG="1"
           CRITICAL_THRESHOLD="${OPTARG}"
           ;;
    "w")   if [ "${WARNING_FLAG}" -ne "0" ]; then
              (( VERBOSE_FLAG )) && {
                 print_error "More than one -w option specified"
              }
              ${ECHO} "UNKNOWN: More than one -w option passed to plugin"
              exit "${UNKNOWN}"
           fi
           WARNING_FLAG="1"
           WARNING_THRESHOLD="${OPTARG}"
           ;;
    "v")   VERBOSE_FLAG="1"
           ;;
     * )   usage
           exit ${UNKNOWN}
           ;;
  esac
done

# Even though we're not expecting extra args, let's be thorough
shift $(( ${OPTIND} - 1 ))

check_os
check_remaining_args "$#"
parse_args
set_mode
check_logic
do_check

exit ${OK}