2 # Check_MK Agent for Solaris
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2013 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
27 # Remove locale settings to eliminate localized outputs where possible
31 export MK_LIBDIR
=${MK_LIBDIR:-/usr/lib/check_mk_agent}
32 export MK_CONFDIR
=${MK_CONFDIR:-/etc/check_mk}
33 export MK_VARDIR
=${MK_VARDIR:-/var/lib/check_mk_agent}
35 # Optionally set a tempdir for all subsequent calls
38 # All executables in PLUGINSDIR will simply be executed and their
39 # ouput appended to the output of the agent. Plugins define their own
40 # sections and must output headers with '<<<' and '>>>'
41 PLUGINSDIR
=$MK_LIBDIR/plugins
43 # All executables in LOCALDIR will by executabled and their
44 # output inserted into the section <<<local>>>. Please refer
45 # to online documentation for details.
46 LOCALDIR
=$MK_LIBDIR/local
48 # close standard input (for security reasons) and stderr
57 /usr
/bin
/perl
-e 'if (! -f $ARGV[0]){die "0000000"};$mtime=(stat($ARGV[0]))[9];print ($^T-$mtime);' "$1"
68 OUTPUT
=$
(eval "$cmdline")
70 echo -n "$descr $? $OUTPUT" |
tr \\n
\\1
77 # Runs a command asynchronous by use of a cache file
78 function run_cached
() {
81 # TODO: this function is unable to handle mulitple args at once
82 # for example: -s -m won't work, it is read as single token "-s -m"
84 if [ "$1" = -s ] ; then local section
="echo '<<<$2>>>' ; " ; shift ; fi
85 if [ "$1" = -m ] ; then local mrpe
=1 ; shift ; fi
86 if [ "$1" = "-ma" ] ; then local mrpe
=1 ; local append_age
=1 ; shift ; fi
90 local CMDLINE
="$section$@"
92 if [ ! -d $MK_VARDIR/cache
]; then mkdir
-p $MK_VARDIR/cache
; fi
93 if [ "$mrpe" = 1 ] ; then
94 CACHEFILE
="$MK_VARDIR/cache/mrpe_$NAME.cache"
96 CACHEFILE
="$MK_VARDIR/cache/$NAME.cache"
99 # Check if the creation of the cache takes suspiciously long and return
100 # nothing if the age (access time) of $CACHEFILE.new is twice the MAXAGE
101 if [ -e "$CACHEFILE.new" ] ; then
102 AGE
=$
(file_age
"$CACHEFILE.new")
103 if [ $AGE -ge $
((MAXAGE
* 2)) ] ; then
104 fuser
-k "$CACHEFILE.new" >/dev
/null
2>&1
105 rm -f "$CACHEFILE.new"
109 # Check if cache file exists and is recent enough
110 if [ -s "$CACHEFILE" ] ; then
111 AGE
=$
(file_age
"$CACHEFILE")
112 if [ $AGE -le $MAXAGE ] ; then local USE_CACHEFILE
=1 ; fi
113 # Output the file in any case, even if it is
114 # outdated. The new file will not yet be available
115 if [ $append_age -eq 1 ] ; then
116 # insert the cached-string before the pipe (first -e)
117 # or, if no pipe found (-e t) append it (third -e),
118 # but only once and on the second line (2) (first line is section header,
119 # all further lines are long output)
120 cat "$CACHEFILE" |
sed -e "2s/|/ (Cached: ${AGE}\/${MAXAGE}s)|/" -e t -e "2s/$/ (Cached: ${AGE}\/${MAXAGE}s)/"
126 # Cache file outdated and new job not yet running? Start it
127 if [ -z "$USE_CACHEFILE" ] && [ ! -e "$CACHEFILE.new" ] ; then
128 if [ $mrpe -eq 1 ] ; then
129 echo "set -o noclobber ; exec > \"$CACHEFILE.new\" || exit 1 ; run_mrpe $NAME \"$CMDLINE\" && mv \"$CACHEFILE.new\" \"$CACHEFILE\" || rm -f \"$CACHEFILE\" \"$CACHEFILE.new\"" | nohup
/usr
/bin
/bash
>/dev
/null
2>&1 &
131 echo "set -o noclobber ; exec > \"$CACHEFILE.new\" || exit 1 ; $CMDLINE && mv \"$CACHEFILE.new\" \"$CACHEFILE\" || rm -f \"$CACHEFILE\" \"$CACHEFILE.new\"" | nohup
/usr
/bin
/bash
>/dev
/null
2>&1 &
137 echo "<<<check_mk>>>"
138 echo "Version: 1.6.0i1"
139 echo "AgentOS: solaris"
140 echo "Hostname: $(hostname)"
141 echo "AgentDirectory: $MK_CONFDIR"
142 echo "DataDirectory: $MK_VARDIR"
143 echo "SpoolDirectory: $SPOOLDIR"
144 echo "PluginsDirectory: $PLUGINSDIR"
145 echo "LocalDirectory: $LOCALDIR"
148 # Find out what zone we are running in
149 # Treat all pre-Solaris 10 systems as "global"
150 if type zonename
&>/dev
/null
153 pszone
="-z $zonename"
160 # Get statistics about monitored jobs. Below the job directory there
161 # is a sub directory per user that ran a job. That directory must be
162 # owned by the user so that a symlink or hardlink attack for reading
163 # arbitrary files can be avoided.
164 if pushd $MK_VARDIR/job
>/dev
/null
; then
168 if [ -d "$username" ] && cd "$username" ; then
169 count
=$
(su
-s "$SHELL" "$username" -c "ls -1 * | wc -l")
171 if [ "$count" -eq "1" ]; then
172 filename
=$
(su
-s "$SHELL" "$username" -c "ls -1 *")
173 echo "==> $filename <=="
176 su
-s "$SHELL" "$username" -c "head -n1000 *"
186 # Filesystem usage for UFS and VXFS
188 for fs
in ufs vxfs samfs lofs tmpfs
190 df
-l -k -F $fs 2>/dev
/null |
sed 1d |
grep -v "^[^ ]*/lib/[^ ]*\.so\.1 " | \
191 while read Filesystem kbytes used avail capacity Mountedon
193 kbytes
=$
(($used + $avail))
194 echo "$Filesystem $fs $kbytes $used $avail $capacity $Mountedon"
198 # Filesystem usage for ZFS
199 if type zfs
&>/dev
/null
202 zfs get
-Hp name
,usedbydataset
,avail
,mountpoint
,type |
sed 's/usedbydataset/used/g' 2>/dev
/null
203 if [ $?
-ne 0 ] ; then
204 zfs get
-Hp name
,referenced
,avail
,mountpoint
,type |
sed 's/referenced/used/g'
207 df
-l -k -F zfs
2>/dev
/null |
sed 1d
211 # newer Solaris (>=11.3) do not provide hits and misses via mdb -k
212 echo '<<<zfs_arc_cache>>>'
213 if type kstat
&>/dev
/null
215 kstat
-p zfs
:0:arcstats |
sed -e 's/.*arcstats://g' |
awk '{printf "%s = %s\n", $1, $2;}'
217 elif type mdb
&>/dev
/null
219 echo '::arc' | mdb
-k
224 # The default solaris ps command strips the command lines of the processes. But for good process
225 # matching on the server we really need to whole command line. On linux there are arguments to
226 # make ps output the whole command line, but on solaris this seems to be missing. We use the ucb
227 # ps command to get the full command line instead. What a hack.
228 if [ -x /usr
/ucb
/ps
]; then
229 UCB_PS
=$
(/usr
/ucb
/ps
-agwwwx)
230 PS
=$
(ps
-o user
,vsz
,rss
,pcpu
,etime
,pid
,args
$pszone | \
231 sed -e 1d
-e 's/ *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) */(\1,\2,\3,\4\/\5,\6) /')
232 while read -r LINE
; do
236 # Directly use ps output when line is too slow to be stripped
237 if [ ${#LINE} -lt 100 ]; then
242 CMD
=$
(echo "$UCB_PS" |
grep "^[ ]*$PID " |
head -n1 | \
243 awk '{ s = ""; for (i = 5; i <= NF; i++) s = s $i " "; print s }')
244 # Only use the ucb ps line when it's not empty (process might already been gone)
245 if [ -z "$CMD" ]; then
248 echo "${STATS}) ${CMD}"
252 ps
-o user
,vsz
,rss
,pcpu
,args
$pszone | \
253 sed -e 1d
-e 's/ *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) */(\1,\2,\3,\4) /'
258 # source: http://www.i-scream.org/libstatgrab/
259 # binary: http://www.opencsw.org/
260 if type statgrab
&>/dev
/null
262 statgrab_vars
="const. cpu. disk. general. mem. page. swap. user."
263 statgrab_sections
="cpu disk page"
265 # Collect net stats in the global zone and in local zones if dlstat is present.
266 if [ "$zonename" == "global" ] ||
type dlstat
&>/dev
/null
268 statgrab_vars
="$statgrab_vars net."
269 statgrab_sections
="$statgrab_sections net"
272 statgrab
$statgrab_vars |
grep -v md
1> /tmp
/statgrab.$$
273 for s
in $statgrab_sections
275 echo "<<<statgrab_$s>>>"
276 grep "^$s\." /tmp
/statgrab.$$ | cut
-d.
-f2-99 |
sed 's/ *= */ /'
279 # <<<statgrab_mem>>> info is preferred over <<<solaris_mem>>>
280 # since solaris_mem is under suspicion to be buggy.
281 echo '<<<statgrab_mem>>>'
282 egrep "^(swap|mem)\." /tmp
/statgrab.$$ |
sed 's/ *= */ /'
284 [ -f /tmp
/statgrab.$$
] && rm -f /tmp
/statgrab.$$
289 # Simulated Output of Linux /proc/cpu
291 load
=$
(uptime|
sed -e 's;.*average: \([0-9]\{1,\}\.[0-9]\{1,\}\), \([0-9]\{1,\}\.[0-9]\{1,\}\), \([0-9]\{1,\}\.[0-9]\{1,\}\).*;\1 \2 \3;')
292 nthreads
=$
(ps
-AL --no-headers |
wc -l)
293 procs
=$
(($
(psrinfo |
wc -l)))
294 echo $load 1/$nthreads $$
$procs
298 if [ -x /sbin
/zpool
]; then
299 run_cached
-s zpool_status
120 "/sbin/zpool status -x"
307 # Simulated output of Linux /proc/uptime
309 btime
=$
(kstat
'-p' 'unix:::boot_time' 2>&1|
grep 'boot_time'|
awk '{print $2}';)
314 ps
-o comm $pszone |
grep -w .
*ntpd
&>/dev
/null
318 ntpq
-np |
sed -e 1,2d
-e 's/^\(.\)/\1 /' -e 's/^ /%/'
323 # <<<solaris_mem>>> should be used if statgrab is missing and top is available.
324 if ! type statgrab
&>/dev
/null
;
326 if [ -x /usr
/bin
/top
] ||
[ -x /usr
/local
/top
]
328 echo "<<<solaris_mem>>>"
329 if [ -x /usr
/bin
/top
]; then /usr
/bin
/top |
grep '^Memory:'; fi
330 if [ -x /usr
/local
/bin
/top
]; then /usr
/bin
/top |
grep '^Memory:'; fi
334 if type prtdiag
>/dev
/null
2>&1
336 # prtdiag does not work in local zones
337 if [ "$zonename" == "global" ]
339 run_cached
-s solaris_prtdiag_status
300 '/usr/sbin/prtdiag 1>/dev/null 2>&1; echo $?'
343 # TCP Connection stats
344 echo '<<<tcp_conn_stats>>>'
345 netstat
-n -a -f inet
-P tcp |
tail +5 | \
346 nawk
'{ c[$7]++; } END { for (x in c) { print x, c[x]; } }'
350 if type mpathadm
&>/dev
/null
352 if [ "$zonename" == "global" ]
354 echo '<<<solaris_multipath>>>'
355 mpathadm list LU | nawk
'{if(NR%3==1){dev=$1}
357 if(NR%3==0){printf "%s %s %s\n",dev,tc,$NF}}'
362 # Fileinfo-Check: put patterns for files into $MK_CONFDIR/fileinfo.cfg
363 function replace_datevariable
()
365 # Replace the date variable of the input, e.g. $DATE:%Y%m%d$, by
366 # the current date. If there's no match just return the input.
368 local pattern
='(\$DATE:(.*)\$)'
370 if [[ ! $file_name =~
$pattern ]]; then
373 date_variable
="${BASH_REMATCH[1]}"
374 format_string
="${BASH_REMATCH[2]}"
375 echo "${file_name/$date_variable/$(date +$format_string)}"
380 # Fileinfo-Check: put patterns for files into /etc/check_mk/fileinfo.cfg
381 if [ -r "$MK_CONFDIR/fileinfo.cfg" ]; then
382 echo '<<<fileinfo:sep(124)>>>'
387 # let the shell do all the expansion, and pipe all files to perl
388 (cat "$MK_CONFDIR/fileinfo.cfg" "$MK_CONFDIR/fileinfo.d/*" 2>/dev
/null
) |
while read -r pattern
; do
390 /*) pattern
=$
(replace_datevariable
"$pattern")
391 for f
in $pattern; do echo $f; done
396 print "[[[header]]]\n";
397 print "name|status|size|time\n";
398 print "[[[content]]]\n";
403 print "$_|missing\n";
406 ($device, $inode, $mode, $nlink, $uid, $gid, $rdev, $size,
407 $atime, $mtime, $ctime, $blksize, $blocks) = stat($_);
409 print "$_|stat failed\n";
411 print "$_|ok|$size|$mtime\n";
414 set +vx
; eval "$old_state"
418 # Libelle Business Shadow
419 if type trd
>/dev
/null
2>&1
421 echo '<<<libelle_business_shadow:sep(58)>>>'
425 # Displaying Information About Faults or Defects
426 # If there are no faults the output of this command will be empty.
427 if type fmadm
>/dev
/null
2>&1
429 echo '<<<solaris_fmadm:sep(58)>>>'
433 # Getting Information About Services Running on Solaris
434 # We can get a list of all service instances, including disabled
435 # or incomplete ones by 'svcs -a'
436 if type svcs
> /dev
/null
2>&1
438 echo '<<<solaris_services>>>'
442 # MK's Remote Plugin Executor
443 if test -f "$MK_CONFDIR/mrpe.cfg"
446 grep -v '^ *#' "$MK_CONFDIR/mrpe.cfg" |
grep -v '^ *$' | \
447 while read descr cmdline
451 if [[ $cmdline =~ \
(([^\
)]*)\
)[[:space
:]](.
*) ]]
453 parameters
=${BASH_REMATCH[1]}
454 cmdline
=${BASH_REMATCH[2]}
456 # split multiple parameter assignments
457 for par
in $
(echo $parameters |
tr ":" "\n")
459 # split each assignment
460 key
=$
(echo $par | cut
-d= -f1)
461 value
=$
(echo $par | cut
-d= -f2)
463 if [ "$key" = "interval" ] ; then
465 elif [ "$key" = "appendage" ] ; then
471 if [ -z "$interval" ]
473 run_mrpe
$descr "$cmdline"
475 run_cached
$args $descr $interval "$cmdline"
481 if cd $LOCALDIR 2>/dev
/null
486 if [ -x "$skript" ] ; then
491 # Call some plugins only every X'th second
492 for skript
in [1-9]*/* ; do
493 if [ -x "$skript" ] ; then
494 run_cached local_
${skript//\//\#} ${skript%/*} "$skript"
501 if cd $PLUGINSDIR 2>/dev
/null
505 if [ -x "$skript" ] ; then
510 # Call some plugins only every X'th second
511 for skript
in [1-9]*/* ; do
512 if [ -x "$skript" ] ; then
513 run_cached plugins_
${skript//\//\#} ${skript%/*} "$skript"