#!/bin/bash
# ======================================================================
#
# NAGIOS CLIENT CHECK :: check requirements
#
# Check if a list requirements regarding processes and network 
# connections do exist 
# ----------------------------------------------------------------------
#
# ah=axel.hahn@unibe.ch
#
# 2023-02-13  v1.0  ah  initial version with check for processes, tcp/ udp
# 2023-02-15  v1.1  ah  add label
# 2024-06-07  v1.2  ah  use iml help and add link to docs
# 2025-02-11  v1.3  ah  add ping check; add timeout in connection check
# ======================================================================


. "$( dirname $0 )/inc_pluginfunctions"

self_APPVERSION=1.3

self=$( basename $0 )

typeset -i VERBOSE=0

OUT=

typeset -i ADDHEADER=1
typeset -i COUNT=0
typeset -i COUNTOK=0
typeset -i COUNTERR=0

PROCESSES=

# ----------------------------------------------------------------------
# functions
# ----------------------------------------------------------------------

# show help
function showHelp(){
        cat <<EOH
$( ph.showImlHelpHeader )

Check if a list requirements regarding processes and network 
connections do exist.

The check returns OK if all given requirements match.

SYNTAX:
$self [-h] [PARAMETERS]

OPTIONS:

    -h|--help            show this help.
    -v|--verbose         show more data: processes and port numbers from
                         /etc/services; add it as first param

PARAMETERS:

    -c|--connect TYPE TARGET PORT        
                         check if connect to a server to a given port
                         is reachable
                         TYPE    string  one of tcp|udp
                         TARGET  string  target host
                         PORT    int     port number
    -i|--ping TARGET     ping a target system
    -l|--label STRING    add label to divide the output in multiple sections
    -p|--process STRING  check if a process with given regex exists in the
                         output of 'ps aux'
    -t|--tcp PORT        check if connect on local tcp port is reachable;
                         This is a shortcut for '-c tcp localhost PORT'

The parameters can be repeated multiple times. Checks will be executed in
the given order.

EXAMPLES

$self -p httpd -p mysqld -t 22 -t 80 -t 443 -t 3306
     Check if
     - a process httpd and a process mysqld exist
     - localhost listens to ports 22, 80, 443 and 3306

$self -l "webservice" -p httpd -t 80 -t 443 -l "database" -p mysqld -t 3306
     Use output with multiple sections

EOH
}

# show a label to divide the output in multiple sections
function showLabel(){
        ADDHEADER=1
        newline
        OUT+="========== $1"
        newline;
        
}

# show a line with a result
# param  string  status, eg. OK|ERROR
# param  string  type, eg. process|connection
# param  string  result
function addOutput(){
        if [ $ADDHEADER -ne 0 ]; then
                ADDHEADER=0
                newline
                addOutput STATUS TYPE RESULT
                newline
                # addOutput ------ ---- ------
                # newline
        fi
        OUT+=$(printf "%-7s %-11s %s\n" "$1" "$2" "$3")
}

function addVerbose(){
        if [ $VERBOSE -gt 0 ]; then
                newline
                OUT+="$(echo """$*""" | sed 's#^#        > #g')"
                newline
        fi
}

# helper function: add new line in $OUT
function newline(){
        OUT+="
"
}

# ----------------------------------------------------------------------
# check functions
# ----------------------------------------------------------------------

# check in process list
# param  string  regex to search for in output of "ps aux"
function chkProcess(){
        local label="$1"
        local found;

        test -z "$PROCESSES" && PROCESSES=$( ps aux )
        COUNT+=1
        found=$( echo "$PROCESSES" | grep -v "grep" | grep -v "$self" | grep -E "$label" )
        if [ -z "$found" ]; then
                COUNTERR+=1
                ph.setStatus critical
                addOutput "ERROR" "process" "$label was not found"
        else
                COUNTOK+=1
                addOutput "OK" "process" "$label ($( echo """$found""" | wc -l ) x)"
                addVerbose "$found"
        fi
        newline
}

# Check connection
# param  string  type: one of tcp|udp
# param  string  target: a servername eg. localhost or www.example.com
# param  integer port number to connect
function chkConnection(){
        local type="$1"     # tcp | udp
        local target="$2"   # servername
        local port="$3"     # port number
        
        COUNT+=1
        if ! echo "$type/$target/$port" | grep -Eq "^(tcp|udp)/[a-z][a-z0-9\.\-]*/[1-9][0-9]*$"; then
                COUNTERR+=1
                OUT+="ERROR  syntax error detected - verify params for port check; type=$type (one of tcp|udp) target=$target (servername to connect) port=$port"
        else
                if timeout 1 bash -c ">/dev/$type/$target/$port" 2>/dev/null; then
                        COUNTOK+=1
                        addOutput "OK" "connection" "$type to $target on port $port"
                else
                        COUNTERR+=1
                        ph.setStatus critical
                        addOutput "ERROR" "connection" "$type to $target on port $port FAILED"
                fi
                addVerbose "$( grep """ $port/$type""" /etc/services )"
        fi
        newline
}

# Check ping to a target
# param  string  target: a servername eg. localhost or www.example.com
function chkPing(){
        local myHost="$1"
        local cmdout

        COUNT+=1
        if cmdout="$( ping -c 1 $myHost 2>&1 )"; then
                COUNTOK+=1
                addOutput "OK" "ping" "ping to $myHost $( echo "$cmdout" | head -2 | tail -1 | rev | cut -f -1,2 -d ' ' | rev)"
        else
                COUNTERR+=1
                ph.setStatus critical
                addOutput "ERROR" "ping" "ping to host $myHost FAILED"
        fi
        addVerbose "$cmdout"
        newline
}

# ----------------------------------------------------------------------
# MAIN
# ----------------------------------------------------------------------

if [ "$#" -eq 0 ]; then
        showHelp;
        exit 1
fi

# parse params
while [[ "$#" -gt 0 ]]; do case $1 in
    -c|--connect)   chkConnection "$2" "$3" "$4"; shift;shift;shift;shift ;; 
    -i|--ping)      chkPing "$2"; shift;shift ;; 
    -l|--label)     showLabel "$2"; shift;shift ;; 
    -p|--process)   chkProcess "$2";shift 2;;
    -h|--help)      showHelp; exit 0;;
    -t|--tcp)       chkConnection tcp localhost "$2"; shift;shift ;; 
    -v|--verbose)   VERBOSE=1; shift;;
    *) echo "ERROR: Unknown parameter: $1"; showHelp; exit 1;
esac; done

ph.status "$COUNT Requirement checks - errors: $COUNTERR"
echo "$OUT"
ph.exit

# ----------------------------------------------------------------------