]> Sergey Matveev's repositories - sgmon.git/commitdiff
Initial commit
authorSergey Matveev <stargrave@stargrave.org>
Sat, 26 Mar 2022 19:35:09 +0000 (22:35 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 26 Mar 2022 19:35:09 +0000 (22:35 +0300)
16 files changed:
README [new file with mode: 0644]
clear.sh [new file with mode: 0755]
helper/dns [new file with mode: 0755]
helper/http [new file with mode: 0755]
helper/https-cert [new file with mode: 0755]
helper/iface [new file with mode: 0755]
helper/netstat [new file with mode: 0755]
helper/pid-alive [new file with mode: 0755]
helper/ping [new file with mode: 0755]
helper/smtp [new file with mode: 0755]
notify-bad.sh [new file with mode: 0755]
notify-ok.sh [new file with mode: 0755]
probelist.sh [new file with mode: 0755]
run.sh [new file with mode: 0755]
start.sh [new file with mode: 0755]
status.sh [new file with mode: 0755]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..8454efe
--- /dev/null
+++ b/README
@@ -0,0 +1,72 @@
+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.
diff --git a/clear.sh b/clear.sh
new file mode 100755 (executable)
index 0000000..38ad620
--- /dev/null
+++ b/clear.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+find . \( -name state -or -name -pid \) -delete -print
diff --git a/helper/dns b/helper/dns
new file mode 100755 (executable)
index 0000000..af23f10
--- /dev/null
@@ -0,0 +1,12 @@
+#!/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
+}
diff --git a/helper/http b/helper/http
new file mode 100755 (executable)
index 0000000..a917d21
--- /dev/null
@@ -0,0 +1,9 @@
+#!/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
diff --git a/helper/https-cert b/helper/https-cert
new file mode 100755 (executable)
index 0000000..3053837
--- /dev/null
@@ -0,0 +1,9 @@
+#!/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
diff --git a/helper/iface b/helper/iface
new file mode 100755 (executable)
index 0000000..e227581
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh -e
+
+ifconfig $1 > state/ifconfig-out
+cat state/ifconfig-out
+grep -q "flags=.*UP" state/ifconfig-out
diff --git a/helper/netstat b/helper/netstat
new file mode 100755 (executable)
index 0000000..54cb36b
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+
+netstat -I $1 -w 1 -q 1 -h
diff --git a/helper/pid-alive b/helper/pid-alive
new file mode 100755 (executable)
index 0000000..2c8447b
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh -e
+
+read pid < "$1"
+ps $pid > state/ps-out
+tail -1 state/ps-out
diff --git a/helper/ping b/helper/ping
new file mode 100755 (executable)
index 0000000..180f487
--- /dev/null
@@ -0,0 +1,11 @@
+#!/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
diff --git a/helper/smtp b/helper/smtp
new file mode 100755 (executable)
index 0000000..6d7002f
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh -e
+
+proto=$1
+host=$2
+printf "QUIT\r\n" | nc -${proto} -w 30 $host 25 | grep -q "220 $host ESMTP"
diff --git a/notify-bad.sh b/notify-bad.sh
new file mode 100755 (executable)
index 0000000..ca3769a
--- /dev/null
@@ -0,0 +1,12 @@
+#!/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"
diff --git a/notify-ok.sh b/notify-ok.sh
new file mode 100755 (executable)
index 0000000..b265cb6
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+. "$SGMONDIR"/rc
+echo OK | mailx -s "Succeeded $SGMONSRV" "$NOTIFY_EMAIL"
diff --git a/probelist.sh b/probelist.sh
new file mode 100755 (executable)
index 0000000..6cafcb2
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh -e
+
+find . -mindepth 1 -type d -not -name ".*" | while read probe ; do
+    [ -x $probe/run ] || continue
+    echo ${probe#./}
+done | sort
diff --git a/run.sh b/run.sh
new file mode 100755 (executable)
index 0000000..13ff28e
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,33 @@
+#!/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
diff --git a/start.sh b/start.sh
new file mode 100755 (executable)
index 0000000..30315dd
--- /dev/null
+++ b/start.sh
@@ -0,0 +1,35 @@
+#!/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
diff --git a/status.sh b/status.sh
new file mode 100755 (executable)
index 0000000..d3eec5b
--- /dev/null
+++ b/status.sh
@@ -0,0 +1,45 @@
+#!/bin/sh -e
+
+catfiles() {
+    for f in $@ ; do
+        [ -s $f ] || continue
+        sed 's/&/\&amp;/g ; s/</\&lt;/g ; s/>/\&gt;/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>"