#!/usr/bin/bash
#
# Nagios CPU Plugin
#
# Description: Check the cpu status and print performace data.
#              The plugin was written and testet on Solaris 8/10
#              and RedHat Enterprise Linux 3/4
#
# Author     : Peter Tuschy
# Version    : 1.20
# Date       : 2010-02-09
# 
# Change single bracket to double bracket for grep on Linux
# Version    : 1.21
# Date       : 2015-09-22
#
# Change removed Solaris support, require bash 4.2 to get rid of current_time
# Version    : 2.0.0
# Date       : 2025-07-28

# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2025-present Peter Tuschy (foss@bofh42.de)

# bash version 4 or later
[ ${BASH_VERSION%%.*} -ge 4 ] || exit 1
# bash version 4.2 or later
printf '%(%s)T' -1 >/dev/null 2>&1 || exit 1

export LANG=C

need_cmd() { hash "$1" 2>/dev/null; if [ $? -ne 0 ]; then echo "ERROR: script ${0##*/} needs command $1"; exit 1; fi; }
for i in readlink grep egrep tr uname awk tail vmstat ; do need_cmd $i ; done

SCRIPT=$(readlink -f $0)
WHERE=${SCRIPT%/*}

# setting default exit values
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
STATE_DEPENDENT=4

# import exit values from utils.sh if we found one
if   [ -f ${WHERE}/utils.sh ] ; then
  . ${WHERE}/utils.sh
elif [ -f ${WHERE}/../utils.sh ] ; then
  . ${WHERE}/../utils.sh
fi

usage() {
  echo "Usage: $0 [-w <warning>] [-c <critical>] [-s <seconds>]"
  exit $STATE_UNKNOWN
}

# make no warning or critical default
warning=-1
critical=-2
samples=15

# command line parsing
while [ "$1" != "" ]; do
  case $1 in
    -w)
      shift
      wpercent=`echo $1 | tr -d '[:digit:]' 2>/dev/null`
      warning=`echo $1 | tr -d '%' 2>/dev/null`
      ;;
    -c)
      shift
      cpercent=`echo $1 | tr -d '[:digit:]' 2>/dev/null`
      critical=`echo $1 | tr -d '%' 2>/dev/null`
      ;;
    -s)
      shift
      samples=$1
      ;;
    --help|-h)
      usage
      ;;
    *)
      usage
  esac
  shift
done

# CPU threshold values
CPU_WARNING=$warning
CPU_CRITICAL=$critical

if [ $CPU_CRITICAL -ge $CPU_WARNING ]; then
  echo "Critical value must be less than the warning value"
  exit $STATE_UNKNOWN
fi

OS=`uname`
samples2=`expr ${samples} + 2`

case "${OS}" in
  Linux)
    if [ -f /tmp/sar.txt ] ; then
      printf -v TCURRENT '%(%s)T'
      TCURRENT=$((TCURRENT-65))
      TSAMPLE=`awk /^SampleTime/'{ printf "%.0f", $2 }' /tmp/sar.txt`
      if [ $TCURRENT -lt $TSAMPLE ] ; then
        USESAR=/tmp/sar.txt
      fi
    fi
    if [ -z "${USESAR}" ] ; then
      # find what sar we have
      # this is the new one with steal
      if vmstat 1 1 | grep 'us sy id wa st$' >/dev/null ; then
        VMSTAT=`vmstat 1 ${samples2} | grep -v '[[:alpha:]]' | tail -${samples} | \
                awk '{ user += $(NF-4) ; kernel += $(NF-3) ; idle += $(NF-2) ; wait += $(NF-1) ; count +=1 }
                END { print ( user / count ) " " ( kernel / count ) " " ( idle / count ) " " ( wait / count ) }'`
        # here the old one without steal
      elif vmstat 1 1 | grep 'us sy id wa$' >/dev/null ; then
        VMSTAT=`vmstat 1 ${samples2} | grep -v '[[:alpha:]]' | tail -${samples} | \
                awk '{ user += $(NF-3) ; kernel += $(NF-2) ; idle += $(NF-1) ; wait += $(NF) ; count +=1 }
                END { print ( user / count ) " " ( kernel / count ) " " ( idle / count ) " " ( wait / count ) }'`
      else
        echo "No matching vmstat data found"
        exit $STATE_UNKNOWN
      fi
    else
      CPUUSER=`grep -E "^%user|^%nice" ${USESAR} | awk '{ user += $2 } END { printf "%.2f", user }'`
      CPUSYST=`awk /^%system/'{ printf "%.2f", $2 }' ${USESAR}`
      CPUIDLE=`awk /^%idle/'{ printf "%.2f", $2 }' ${USESAR}`
      CPUWAIT=`awk /^%iowait/'{ printf "%.2f", $2 }' ${USESAR}`
      OUTPUT=`echo $CPUIDLE | awk '{ printf "%.0f", $1 }'`
      samples=60
    fi
    ;;
  *)
    echo "OS ${OS} not supportet by this check"
    exit $STATE_UNKNOWN
esac

if [ -z "${USESAR}" ] ; then
  CPUUSER=`echo $VMSTAT | awk '{ printf "%.2f", $1 }'`
  CPUSYST=`echo $VMSTAT | awk '{ printf "%.2f", $2 }'`
  CPUIDLE=`echo $VMSTAT | awk '{ printf "%.2f", $3 }'`
  CPUWAIT=`echo $VMSTAT | awk '{ printf "%.2f", $4 }'`
  OUTPUT=`echo $VMSTAT | awk '{ printf "%.0f", $3 }'`
fi

PERFDATA="user=${CPUUSER};;;0;100  system=${CPUSYST};;;0;100  idle=${CPUIDLE};${CPU_WARNING};${CPU_CRITICAL};100;0  wait=${CPUWAIT};;;0;100"
PLUGDATA="CPU average for ${samples} seconds (idle ${CPUIDLE}% / user ${CPUUSER}% / system ${CPUSYST}% / iowait ${CPUWAIT}%)"

if [ $OUTPUT -le $CPU_CRITICAL ]; then
  echo "CPU CRITICAL - ${PLUGDATA} | ${PERFDATA}"
  exit $STATE_CRITICAL
elif [ $OUTPUT -le $CPU_WARNING ]; then
  echo "CPU WARNING - ${PLUGDATA} | ${PERFDATA}"
  exit $STATE_WARNING
elif [ $CPU_WARNING -lt $OUTPUT ]; then
  echo "CPU OK - ${PLUGDATA} | ${PERFDATA}"
  exit $STATE_OK
else
  echo "CPU STATUS UNKNOWN"
  exit $STATE_UNKNOWN
fi
