--- /dev/null
+sgmon -- Simple stargrave's monitoring system
+Intended for my own personal use, as a replacement for Monit.
+All of that is just an ordinary plain POSIX shell scripts.
+
+Create some directory hierarchy for convenience:
+
+ $ mkdir -p probes/example.com/{ping4,ping6,http}
+ $ mkdir -p probes/jails/foobar
+
+Make probes from them:
+
+ $ cat > probes/example.com/ping4/run <<EOF
+ #!/bin/sh -e
+ "$SGMONDIR"/helper/http http://example.com/ Some.Expected.Title
+ EOF
+ # only 3 failures will generated notification email
+ $ echo 3 > probes/example.com/http/max-attempts
+
+ $ cat > probes/example.com/ping4/run <<EOF
+ #!/bin/sh -e
+ "$SGMONDIR"/helper/ping 4 example.com
+ EOF
+
+ $ cat > probes/example.com/ping6/run <<EOF
+ #!/bin/sh -e
+ "$SGMONDIR"/helper/ping 6 example.com
+ EOF
+ $ echo 5 > probes/example.com/ping4/period # every 5 seconds
+
+ $ cat > probes/jails/foobar/run <<EOF
+ #!/bin/sh -e
+ jls -j foobar-jail-name
+ EOF
+
+Set default configuration values:
+
+ $ cat > rc <<EOF
+ NOTIFY_EMAIL=user+sgmon@example.com
+ PERIOD_DEFAULT=60
+ EOF
+
+You can run probe individually for testing:
+
+ $ cd probes/example.com/ping6
+ $ /path/to/sgmon/run.sh
+ [...]
+
+Or start all of them in background:
+
+ $ cd probes
+ $ /path/to/sgmon/start.sh
+
+Send signal to that script and it will exit when all probes will die.
+It is advisable to run it under daemontools'es supervise utility for
+convenience.
+
+run.sh creates temporary directory for each probe and links it to
+$probe/state. Also $probe/pid file is created there. run-script expects
+that it is run from the $probe directory, there is state/, where it can
+store its temporary files. $SGMONDIR are also always set $SGMONSRV.
+
+You can remove all pid-files and state/-directories with clear.sh.
+
+Generate HTML page with information about state of all services and
+their output:
+
+ $ cd probes
+ $ /path/to/sgmon/status.sh > /path/to/index.html
+
+If some probe is failed, then email message with its stdout/stderr will
+be sent. If it succeeds after the fail, then another message is sent.
+Each startup and shutdown are also notified by email.
--- /dev/null
+#!/bin/sh
+
+find . \( -name state -or -name -pid \) -delete -print
--- /dev/null
+#!/bin/sh -e
+
+proto=$1
+srv=$2
+shift
+shift
+
+drill -${proto} @$srv $@ >state/drill-out
+grep -q "rcode: NOERROR" state/drill-out || {
+ cat state/drill-out
+ exit 1
+}
--- /dev/null
+#!/bin/sh -e
+
+bad=0
+curl --max-time 60 --verbose "$1" >state/curl-out 2>&1 || bad=1
+grep -q "$togrep" state/curl-out || bad=1
+if [ "$bad" -eq 1 ] ; then
+ cat state/curl-out
+ exit 1
+fi
--- /dev/null
+#!/bin/sh -e
+
+bad=0
+curl --max-time 60 --verbose "$1" >/dev/null 2>state/curl-out || bad=1
+grep -q "subject: CN=$2" state/curl-out || bad=1
+if [ "$bad" -eq 1 ] ; then
+ cat state/curl-out
+ exit 1
+fi
--- /dev/null
+#!/bin/sh -e
+
+ifconfig $1 > state/ifconfig-out
+cat state/ifconfig-out
+grep -q "flags=.*UP" state/ifconfig-out
--- /dev/null
+#!/bin/sh -e
+
+netstat -I $1 -w 1 -q 1 -h
--- /dev/null
+#!/bin/sh -e
+
+read pid < "$1"
+ps $pid > state/ps-out
+tail -1 state/ps-out
--- /dev/null
+#!/bin/sh -e
+
+proto=$1
+shift
+[ "$proto" = "6" ] || proto=""
+if ping${proto} -c 1 $@ > state/ping-out ; then
+ tail -1 < state/ping-out
+else
+ cat state/ping-out
+ exit 1
+fi
--- /dev/null
+#!/bin/sh -e
+
+proto=$1
+host=$2
+printf "QUIT\r\n" | nc -${proto} -w 30 $host 25 | grep -q "220 $host ESMTP"
--- /dev/null
+#!/bin/sh -e
+
+. "$SGMONDIR"/rc
+(
+ echo stdout:
+ cat state/stdout
+ if [ -s state/stderr ] ; then
+ echo stderr:
+ cat state/stderr
+ fi
+) |
+mailx -s "Failed $SGMONSRV" "$NOTIFY_EMAIL"
--- /dev/null
+#!/bin/sh -e
+
+. "$SGMONDIR"/rc
+echo OK | mailx -s "Succeeded $SGMONSRV" "$NOTIFY_EMAIL"
--- /dev/null
+#!/bin/sh -e
+
+find . -mindepth 1 -type d -not -name ".*" | while read probe ; do
+ [ -x $probe/run ] || continue
+ echo ${probe#./}
+done | sort
--- /dev/null
+#!/bin/sh -e
+
+export SGMONDIR="$(dirname "$(realpath -- "$0")")"
+. "$SGMONDIR"/rc
+[ -z "$1" ] && SGMONSRV="$(basename "$PWD")" || SGMONSRV="$1"
+export SGMONSRV
+export SGMONTMP=`mktemp -d -t sgmon`
+trap "rm -fr $SGMONTMP" HUP PIPE INT QUIT TERM EXIT
+rm -f state
+ln -s $SGMONTMP state
+
+while : ; do
+ [ -s max-attempts ] && read max_attempts < max-attempts || max_attempts=1
+ [ -s state/attempts ] && read attempts < state/attempts || attempts=0
+ if ./run >state/stdout 2>state/stderr ; then
+ if [ "$attempts" -ge "$max_attempts" ] ; then
+ "$SGMONDIR"/notify-ok.sh
+ fi
+ rm -f state/attempts
+ touch state/ok
+ echo OK
+ else
+ rm -f state/ok
+ attempts=$(( $attempts + 1 ))
+ echo $attempts > state/attempts
+ echo BAD ${attempts}/${max_attempts}
+ if [ $attempts -eq $max_attempts ] ; then
+ "$SGMONDIR"/notify-bad.sh
+ fi
+ fi
+ [ -s period ] && read period < period || period=$PERIOD_DEFAULT
+ sleep $period
+done
--- /dev/null
+#!/bin/sh -e
+
+stop() {
+ trap - HUP PIPE INT QUIT TERM EXIT
+ echo ... | mailx -s "Stopping" "$NOTIFY_EMAIL"
+ for probe in $probes ; do
+ [ -s $probe/pid ] || continue
+ echo killing ${probe}...
+ pkill -F $probe/pid 2>/dev/null || :
+ done
+ for probe in $probes ; do
+ read pid < $probe/pid || :
+ [ -n "$pid" ] || continue
+ echo waiting ${probe}...
+ wait $pid || :
+ done
+ echo finished
+ exit
+}
+
+root="`pwd`"
+SGMONDIR="$(dirname "$(realpath -- "$0")")"
+. "$SGMONDIR"/rc
+probes="$("$SGMONDIR"/probelist.sh)"
+echo $(echo "$probes" | wc -l) probes | mailx -s "Starting" "$NOTIFY_EMAIL"
+for probe in $probes ; do
+ cd $probe
+ echo starting ${probe}...
+ "$SGMONDIR"/run.sh $probe >/dev/null 2>&1 &
+ echo $! > pid || :
+ cd "$root"
+done
+echo OK
+trap stop HUP PIPE INT QUIT TERM EXIT
+while : ; do sleep 1 ; done
--- /dev/null
+#!/bin/sh -e
+
+catfiles() {
+ for f in $@ ; do
+ [ -s $f ] || continue
+ sed 's/&/\&/g ; s/</\</g ; s/>/\>/g' < $f
+ done
+}
+
+SGMONDIR="$(dirname "$(realpath -- "$0")")"
+. "$SGMONDIR"/rc
+probes="$("$SGMONDIR"/probelist.sh)"
+cat <<EOF
+<!DOCTYPE html>
+<html><head><title>sgmon status $(date -Iseconds)</title></head><body>
+<table border=1>
+<tr><th>Status</th><th>probe</th><th>Last</th><th>Message</th></tr>
+EOF
+for probe in $probes ; do
+ if [ -s $probe/state/attempts ] ; then
+ read attempts < $probe/state/attempts || attempts=1
+ mtime=$(stat -t "%F %T" -f %Sm $probe/state/attempts)
+ [ -s max-attempts ] && read max_attempts < max-attempts || max_attempts=1
+ [ "$attempts" -lt "$max_attempts" ] &&
+ status="<b>Prefail</b>" || status="<b>FAIL</b>"
+ status="$status (${attempts}/${max_attempts})"
+ else
+ if [ -r $probe/state/ok ] ; then
+ mtime=$(stat -t "%F %T" -f %Sm $probe/state/ok)
+ status=OK
+ else
+ mtime=
+ status="???"
+ fi
+ fi
+ cat <<EOF
+<tr>
+ <td>$status</td>
+ <td><tt><a name="$probe">$probe</a></tt></td>
+ <td><tt>$mtime</tt></td>
+ <td><pre>$(catfiles $probe/state/stdout $probe/state/stderr)</pre></td>
+</tr>
+EOF
+done
+echo "</table></body></html>"