#!/bin/ksh # Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2 # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. # Author: Lars Michelsen # Florian Heigl # Christian Zigotzky # NOTE: This agent has been adapted from the Checkmk FreeBSD agent. inpath() { # replace "if type [somecmd]" idiom # 'command -v' tends to be more robust vs 'which' and 'type' based tests command -v "${1:?No command to test}" >/dev/null 2>&1 } get_epoch() { date +%s 2>/dev/null || perl -e 'print($^T."\n");' } # Remove locale settings to eliminate localized outputs where possible export LC_ALL=C unset LANG export MK_LIBDIR="/usr/lib/check_mk_agent" export MK_CONFDIR="/etc" # Optionally set a tempdir for all subsequent calls #export TMPDIR= # Make sure, locally installed binaries are found PATH=$PATH:/usr/local/bin # All executables in PLUGINSDIR will simply be executed and their # ouput appended to the output of the agent. Plugins define their own # sections and must output headers with '<<<' and '>>>' PLUGINSDIR=$MK_LIBDIR/plugins # All executables in LOCALDIR will by executabled and their # output inserted into the section <<>>. Please refer # to online documentation for details. LOCALDIR=$MK_LIBDIR/local if inpath sudo && [ "$(whoami)" != "root" ]; then ROOT_OR_SUDO="sudo --non-interactive" else ROOT_OR_SUDO="" fi export ROOT_OR_SUDO # close standard input (for security reasons) and stderr if [ "$1" = -d ]; then set -xv else exec /dev/null fi _free_mem_index_vmstat() { # The number of output columns in the vmstat command changed in newer versions. We want the # entry under 'fre' (not a typo). # https://man.openbsd.org/OpenBSD-6.0/vmstat.8 # https://man.openbsd.org/vmstat.8 idx=1 for col in "$@"; do if [ "$col" = "fre" ]; then printf "%s" "$idx" return 0 fi idx=$((idx + 1)) done return 1 } _free_mem_with_unit_vmstat() { # Newer vmstat versions print "1M" instead of "1024" as free memory # SUP-11138 # https://github.com/openbsd/src/blob/1c702c4a4395025332d4340f326500c590811dc8/usr.bin/vmstat/vmstat.c#L368 case "${1}" in *M) mem_free_mb_with_unit="${1}" printf "%s" "${mem_free_mb_with_unit%?} MB" ;; *) printf "%s" "${1} kB" ;; esac } _free_mem_with_unit() { vmstat_output=$(vmstat) # shellcheck disable=SC2046 # we want word splitting here if ! mem_free_idx="$(_free_mem_index_vmstat $(echo "$vmstat_output" | tail -n2 | head -1))"; then return 1 fi _free_mem_with_unit_vmstat "$(echo "$vmstat_output" | tail -n1 | awk -v idx="$mem_free_idx" '{ print $idx }')" return 0 } section_openbsd_mem() { if ! mem_free_with_unit=$(_free_mem_with_unit); then return 1 fi mem_total=$(sysctl hw.usermem | cut -d= -f2) mem_total=$(echo "$mem_total/1024" | bc) swapctl_output=$(swapctl -k -s) swap_free=$(echo "$swapctl_output" | awk '{ print $7 }') swap_total=$(echo "$swapctl_output" | awk '{ print $2 }') # if there is no swap space swap values are 0 if [ -z "$swapctl_output" ]; then swap_free=0 swap_total=0 fi printf "<<>>\n" printf "MemTotal:\t %s kB\n" "$mem_total" printf "MemFree:\t %s\n" "$mem_free_with_unit" printf "SwapTotal:\t %s kB\n" "$swap_total" printf "SwapFree:\t %s kB\n" "$swap_free" return 0 } section_misc_sections() { echo "<<>>" echo "Version: 2.4.0p13" echo "AgentOS: openbsd" echo "Hostname: $(hostname)" echo "AgentDirectory: $MK_CONFDIR" echo "DataDirectory: $MK_VARDIR" echo "SpoolDirectory: $SPOOLDIR" echo "PluginsDirectory: $PLUGINSDIR" echo "LocalDirectory: $LOCALDIR" echo "OSType: unix" echo "OSName: $(uname -s)" echo "OSVersion: $(uname -r)" echo '<<>>' df -kPt ffs | sed -e 's/^\([^ ][^ ]*\) \(.*\)$/\1 ffs \2/' | sed 1d # processes including username, without kernel processes echo '<<>>' echo "[time]" get_epoch echo "[processes]" ps ax -ww -o user,vsz,rss,pcpu,command | sed -e 1d -e 's/ *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) */(\1,\2,\3,\4) /' echo '<<>>' echo "$(sysctl -n vm.loadavg | tr -d '{}') $(top -b -n 1 | grep -E '^[0-9]+ processes' | awk '{ print $3"/"$1 }') $(sysctl -n hw.ncpu)" echo '<<>>' echo "$(get_epoch) - $(sysctl -n kern.boottime | cut -d' ' -f 4,7 | tr ',' '.' | tr -d ' ')" | bc section_openbsd_mem echo '<<>>' # MC= MAC address # BI= Bytes in # PI= Packets in # EI= Errors in # EO= Errors out # BO= Bytes out # PO= Packets out # CO= Colls # NI= Number of interfaces # INTERFACES= Array of interfaces set -A INTERFACES set -A MC set -A BI set -A BO set -A PI set -A PO set -A EI set -A EO set -A CO NI=0 # special (lo/pfsync/pflog/enc) and inactive (*) interfaces are not needed NETSTAT_OUTPUT=$(netstat -in | grep '' | grep -v -E "\*|lo|pfsync|enc") NETSTAT_OUTPUT_BYTES=$(netstat -inb | grep '' | grep -v -E "\*|lo|pfsync|enc") # adjust internal field separator to get lines from netstat and backup it before OFS=$IFS IFS='' # collect netstat values and interface number for NS in $NETSTAT_OUTPUT; do NI=$((NI + 1)) INTERFACES[NI]=$(echo "$NS" | awk '{ print $1 }') MC[NI]=$(echo "$NS" | awk '{ print $4 }') PI[NI]=$(echo "$NS" | awk '{ print $5 }') EI[NI]=$(echo "$NS" | awk '{ print $6 }') PO[NI]=$(echo "$NS" | awk '{ print $7 }') EO[NI]=$(echo "$NS" | awk '{ print $8 }') CO[NI]=$(echo "$NS" | awk '{ print $9 }') done # need NIC counter again for byte values - reset it NI=0 for NS in $NETSTAT_OUTPUT_BYTES; do NI=$((NI + 1)) BI[NI]=$(echo "$NS" | awk '{ print $5 }') BO[NI]=$(echo "$NS" | awk '{ print $6 }') done # what is this for? [ "${NI}" -ge 1 ] || NI=15 # jot is OpenBSD "range" for i in $(jot $NI); do echo "${INTERFACES[$i]}:${BI[$i]} ${PI[$i]} ${EI[$i]} 0 0 0 0 0 ${BO[$i]} ${PO[$i]} ${EO[$i]} 0 0 ${CO[$i]} 0 0" done for IF in $(jot $NI); do echo "[${INTERFACES[$IF]}]" IFCONFIG_OUTPUT=$(ifconfig "${INTERFACES[$IF]}") for IO in $IFCONFIG_OUTPUT; do # Speed SP=$(echo "$IO" | grep -E "media:.*base" | cut -d\( -f2 | cut -db -f1) if [ "$SP" ]; then case "$SP" in *G*) SP=$((${SP%G} * 1000)) ;; esac printf "\tSpeed: %sMb/s\n" "${SP}" fi # Detect duplexity - in reality only available for physical devices but # virtual ones like CARP devices will get at least a half duplex if echo "$IO" | grep -q -E "media:.*full-duplex"; then printf "\tDuplex: Full\n" elif echo "$IO" | grep "media:" | grep -q -v "full-duplex"; then printf "\tDuplex: Half\n" fi # Auto-negotiation if echo "$IO" | grep -q -E "media:.*autoselect"; then printf "\tAuto-negotiation: on\n" elif echo "$IO" | grep "media:" | grep -q -v "autoselect"; then printf "\tAuto-negotiation: off\n" fi # Detect detected link if echo "$IO" | grep "status:" | grep -q -E "active|backup|master"; then printf "\tLink detected: yes\n" fi done printf "\tAddress: %s\n" "${MC[$IF]}" done # reset IFS to default IFS=$OFS # IPMI-Data (Fans, CPU, temperature, etc) # needs the sysutils/ipmitool and kldload ipmi.ko if inpath ipmitool; then echo '<<>>' ipmitool sensor list | grep -v 'command failed' | sed -e 's/ *| */|/g' -e "s/ /_/g" -e 's/_*$//' -e 's/|/ /g' | grep -v -E '^[^ ]+ na ' | grep -v ' discrete ' fi if inpath mailq && getent passwd postfix >/dev/null 2>&1; then echo '<<>>' ${ROOT_OR_SUDO} mailq | tail -n 6 fi } run_plugins() { ( cd "${PLUGINSDIR}" || return for script in "./"*; do [ -x "${script}" ] && "${script}" done ) } run_local_checks() { ( cd "${LOCALDIR}" || return echo '<<>>' for script in "./"*; do [ -x "${script}" ] && "${script}" done ) } # TODO: sync with linux agent run_mrpe_plugins() { if [ -e "$MK_CONFDIR/mrpe.cfg" ]; then echo '<<>>' # SC2162: read without -r will mangle backslashes. # We suppress it here for compatibility (curretly backslashes e.g. before spaces are dropped). # Since escaping of field seperators is not relevant when reading into one variable, we probably # would have wanted "read -r". # shellcheck disable=SC2162 grep -Ev '^[[:space:]]*($|#)' "$MK_CONFDIR/mrpe.cfg" | while read descr cmdline; do PLUGIN=${cmdline%% *} OUTPUT=$(eval "$cmdline") printf "(%s) %s %s %s" "${PLUGIN##*/}" "$descr" "$?" "$OUTPUT" | tr \\n \\1 echo done fi } main() { section_misc_sections run_plugins run_local_checks run_mrpe_plugins return 0 } main