#!/bin/sh # # This script was written by erdgeist@erdgeist.org # The code is released under the beer ware license, this means do whatever you # want with it, as long as you leave this notice along with the code. # Should we meet some day and you find the code is worth it, let's enjoy a beer # together. PLUGIN_DIR=/usr/local/etc/minimunin-plugins CONFIG_DIR=/usr/local/etc/minimunin-configs BUILTIN="cpu processes load swap systat df iostat uptime memory open_files" SYSCTL=/sbin/sysctl [ -f ${SYSCTL} ] || SYSCTL=/usr/sbin/sysctl # list plugins, read configs PLUGINS=$(/usr/bin/find ${PLUGIN_DIR} \( -type l -or -type f \) -not -name '*_' -perm +111 -exec basename {} \; 2> /dev/null) CONFIGS=$(/usr/bin/find ${CONFIG_DIR} -type f -exec /usr/bin/grep -v -e ^\# -e ^$ {} \; -exec /bin/echo [] \; 2> /dev/null) main() { # print banner printf "# munin node at %s\n" "$(/bin/hostname)" # read commands in loop while read -r command arg; do # chomp and sanitize variables command=$(printf %s "${command}" | /usr/bin/tr -cd '[:alnum:]_-. ') arg=$(printf %s "${arg}" | /usr/bin/tr -cd '[:alnum:]_-. ') # printf "%s %s\n" "$command" "$arg" >> /var/log/minimunin.log # dispatch commands case ${command} in list) printf "%s " "${BUILTIN}" ${PLUGINS}; printf '\n' ;; fetch) print_fetch ${arg}; printf ".\n" ;; config) print_config ${arg}; printf ".\n" ;; quit) exit 0 ;; *) printf "# Unknown command %s.\n" "${command}" # printf %s ${command} | hexdump -C >> /var/log/minimunin.log ;; esac done } print_fetch() { # see if we're serving a plugin _plugin=$1 call_plugin ${_plugin} fetch && return 0 # if not, let our builtins answer case ${_plugin} in open_files) printf "max.value %d\n" $(get_sys kern.maxfiles) printf "used.value %d\n" $(get_sys kern.openfiles) ;; load) printf "load.value %s\n" $(get_sys vm.loadavg | /usr/bin/cut -wf3) ;; swap) printf "swap_in.value %d\n" $(get_sys vm.stats.vm.v_swappgsin) printf "swap_out.value %d\n" $(get_sys vm.stats.vm.v_swappgsout) ;; uptime) boot=$(get_sys kern.boottime); boot=${boot#*sec =}; boot=${boot%%,*} printf "uptime.value %d\n" $(( ( `/bin/date +%s` - boot ) / 86400 )) ;; memory) pagesize=$(get_sys vm.stats.vm.v_page_size) printf "active.value %d\n" $(( pagesize * `get_sys vm.stats.vm.v_active_count` )) printf "inactive.value %d\n" $(( pagesize * `get_sys vm.stats.vm.v_inactive_count` )) printf "laundry.value %d\n" $(( pagesize * `get_sys vm.stats.vm.v_laundry_count` )) printf "wired.value %d\n" $(( pagesize * `get_sys vm.stats.vm.v_wire_count` )) printf "cached.value %d\n" $(( pagesize * `get_sys vm.stats.vm.v_cache_count` )) printf "free.value %d\n" $(( pagesize * `get_sys vm.stats.vm.v_free_count` )) printf "buffers.value %d\n" $(( `get_sys vfs.bufspace` )) printf "swap.value %d\n" $(( `/usr/sbin/swapinfo -k | /usr/bin/tail -n 1 | /usr/bin/cut -wf 3` * 1024 )) ;; cpu) set -- $(get_sys kern.cp_time) printf "user.value %d\nnice.value %d\nsystem.value %d\ninterrupt.value %d\nidle.value %d\n" "$1" "$2" "$3" "$4" "$5" ;; processes) printf "processes.value %d\n" $(/bin/pgrep -aS .* | /usr/bin/wc -l) printf "threads.value %d\n" $(( `ps auxwH | wc -l` - 1 )) printf "maxprocesses.value %d\n" $(get_sys kern.maxproc) ;; iostat) for d in $(/usr/sbin/iostat -Id | /usr/bin/head -n 1); do set -- $(/usr/sbin/iostat -Idx ${d} | /usr/bin/tail -n 1) printf "${d}_read.value %d\n${d}_write.value %d\n" "${4%.*}" "${5%.*}" done ;; systat) set -- $(get_sys vm.stats.sys.v_soft vm.stats.sys.v_intr vm.stats.sys.v_syscall vm.stats.sys.v_swtch vm.stats.vm.v_forks vm.stats.vm.v_rforks vm.stats.vm.v_vforks) printf "softint.value %d\nhardint.value %d\nsyscall.value %d\ncs.value %d\nforks.value %d\n" "$1" "$2" "$3" "$4" $(( ${5}+${6}+${7} )) ;; df) /bin/df -P -t noprocfs,devfs,fdescfs,linprocfs,linsysfs,nfs,nullfs | /usr/bin/tail -n +2 | while read -r fs blocks used avail cap mount; do fs=$(printf "%s" ${fs} | /usr/bin/tr -c '[:alnum:]' _) printf "%s %d\n" "${fs}.value" $(( used * 512 )) done ;; *) printf "# Unknown plugin %s.\n" "${_plugin}" ;; esac } print_config() { _plugin=$1 # see if we're configuring a plugin call_plugin ${_plugin} config && return 0 # if not, execute built in commands case ${_plugin} in load) printf %s \ "graph_title Load average graph_info The load average of the machine describes how many processes are in the run-queue (scheduled to run \"immediately\"). graph_category system graph_args --base 1000 -l 0 graph_vlabel load graph_scale no load.label load load.info Average load for the last five minutes. load.warning 10 load.critical 120 " ;; swap) printf %s \ "graph_title Swap in/out graph_args --base 1000 -l 0 graph_info This graph shows the swap activity of the system. graph_category system graph_vlabel pages per second in (-) / out (+) swap_in.label swap swap_in.type DERIVE swap_in.min 0 swap_in.max 100000 swap_in.graph no swap_out.label swap swap_out.type DERIVE swap_out.min 0 swap_out.max 100000 swap_out.negative swap_in " ;; iostat) printf %s \ "graph_title IOstat by bytes graph_args --base 1024 -l 0 graph_vlabel MB per second read+written graph_category disk graph_info This graph shows the I/O to and from block devices graph_order" set -- $(/usr/sbin/iostat -Id | /usr/bin/head -n 1) for d; do printf " %s_read %s_write" "$d" "$d"; done printf "\n" for d; do printf %s \ "${d}_read.label ${d} ${d}_read.type DERIVE ${d}_read.max 2000 ${d}_read.min 0 ${d}_read.graph no ${d}_write.label ${d} ${d}_write.info I/O on device ${d} ${d}_write.type DERIVE ${d}_write.max 2000 ${d}_write.min 0 ${d}_write.negative ${d}_read " done ;; uptime) printf %s \ "graph_title Uptime graph_args --base 1000 -l 0 graph_vlabel uptime in days graph_scale no graph_category system uptime.label uptime uptime.draw AREA " ;; memory) printf %s \ "graph_title Memory usage graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit $(( `get_sys vm.stats.vm.v_page_size` * `get_sys vm.stats.vm.v_page_count`)) graph_category system graph_info This graph shows what the machine uses its memory for. graph_order active inactive wired buffers cached free swap active.label active active.info pages recently statistically used active.draw AREA inactive.label inactive inactive.info pages recently statistically unused inactive.draw STACK laundry.label laundry laundry.info pages eligible for laundering laundry.draw STACK wired.label wired wired.info pages that are fixed into memory, usually for kernel purposes, but also sometimes for special use in processes wired.draw STACK buffers.label buffers buffers.info pages used for filesystem buffers buffers.draw STACK cached.label cache cached.info pages that have percolated from inactive to a status where they maintain their data, but can often be immediately reused cached.draw STACK free.label free free.info pages without data content free.draw STACK swap.label swap swap.info Swap space used swap.draw STACK " ;; open_files) printf %s \ "graph_title File table usage graph_args --base 1000 -l 0 graph_vlabel number of open files graph_category system graph_info This graph monitors the number of open files. used.label open files used.info The number of currently open files. used.warning $(( `get_sys kern.maxfiles` * 92 / 100 )) used.critical $(( `get_sys kern.maxfiles` * 98 / 100 )) max.label max open files max.info The maximum supported number of open files. " ;; cpu) cdef=$(get_sys kern.clockrate) ; cdef=${cdef#*stathz = }; cdef=${cdef% *}",/,100,*" printf %s \ "graph_title CPU usage graph_order system interrupt user nice idle graph_args --base 1000 -r --lower-limit 0 --upper-limit $(( `get_sys hw.ncpu` * 100 )) graph_vlabel % graph_scale no graph_info This graph shows how CPU time is spent. graph_category system graph_period second system.label system system.draw AREA system.max 5000 system.type DERIVE system.min 0 system.info CPU time spent by the kernel in system activities system.cdef system,${cdef} interrupt.label interrupt interrupt.draw STACK interrupt.max 5000 interrupt.type DERIVE interrupt.min 0 interrupt.info CPU time spent by the kernel processing interrupts interrupt.cdef interrupt,${cdef} user.label user user.draw STACK user.max 5000 user.type DERIVE user.info CPU time spent by normal programs and daemons user.min 0 user.cdef user,${cdef} nice.label nice nice.draw STACK nice.max 5000 nice.type DERIVE nice.info CPU time spent by nice(1)d programs nice.min 0 nice.cdef nice,${cdef} idle.label idle idle.draw STACK idle.max 5000 idle.type DERIVE idle.info Idle CPU time idle.min 0 idle.cdef idle,${cdef} " ;; systat) printf %s \ "graph_title System Statistics graph_vlabel per second graph_scale no graph_category system graph_info FreeBSD systat plugin softint.label Software interrupts softint.type DERIVE softint.min 0 hardint.label Hardware interrupts hardint.type DERIVE hardint.min 0 syscall.label System calls syscall.type DERIVE syscall.min 0 cs.label Context switches cs.type DERIVE cs.min 0 forks.label Fork rate forks.type DERIVE forks.min 0 " ;; processes) printf %s \ "graph_title Process Statistics graph_args --base 1000 -l 0 graph_vlabel number of processes graph_category system graph_info This graph monitors the number of running processes and threads. processes.label The number of processes in the system threads.label The number of threads in the system maxprocesses.label The maximum number of processes in the system processes.warning $(( `get_sys kern.maxproc` * 92 / 100 )) processes.critical $(( `get_sys kern.maxproc` * 98 / 100 )) " ;; df) printf %s \ "graph_title Filesystem usage (in bytes) graph_args --base 1024 --lower-limit 0 graph_vlabel bytes graph_category disk " /bin/df -P -t noprocfs,devfs,fdescfs,linprocfs,linsysfs,nfs,nullfs | /usr/bin/tail -n +2 | while read -r fs blocks used avail cap mount; do fs=$(printf "%s" ${fs} | /usr/bin/tr -c '[:alnum:]' _) printf "%s %s\n" "${fs}.label" "${mount}" done ;; *) printf "# Unknown plugin %s.\n" "${_plugin}" ;; esac } get_sys() { LANG=C LC_ALL=C ${SYSCTL} -n $@ } call_plugin() { _param=$2 _plug=$(/usr/bin/find ${PLUGIN_DIR} \( -type l -or -type f \) -perm +111 -name "$1" 2>/dev/null | /usr/bin/head -n 1) [ -z "${_plug}" ] && return 1 unset in_sect _env _user _command while read -r line; do case ${line## } in \[*\]) case [$1 in ${line%%]*}) in_sect=true;; *) unset in_sect ;; esac ;; env.*) [ "${in_sect}" ] || continue _pref=${line#*env.} _env="${_env} ${_pref%% *}='${_pref#* }'" ;; user\ *) _user=${line#*user } ;; command\ *) _command=${line#*command } ;; esac done <