fixes and alert improvements
[distro-setup] / system-status
old mode 100644 (file)
new mode 100755 (executable)
index 28c0035..3ccde7c
@@ -7,6 +7,11 @@
 
 if [ -z "$BASH_VERSION" ]; then echo "error: shell is not bash" >&2; exit 1; fi
 
+if [[ $EUID != 1000 ]]; then
+  echo "$0: error, expected to be user 1000"
+  exit 1
+fi
+
 source /a/bin/errhandle/err
 status_file=/dev/shm/iank-status
 
@@ -28,36 +33,152 @@ v() {
     printf "%s\n" "$*"
   fi
 }
+p() { printf "%s\n" "$*"; }
 # log-once COUNT NAME [MESSAGE]
 lo() {
-  /usr/local/bin/log-once "$@" | ifne mail -s "$HOSTNAME: system-status $2" root@localhost
+  if type -p ifne &>/dev/null; then
+    /usr/local/bin/log-once "$@" | ifne mail -s "$HOSTNAME: system-status $2" root@localhost
+  fi
 }
 
 loday() {
-  /usr/local/bin/log-once "$@" | ifne mail -s "$HOSTNAME: system-status $2" daylerts@iankelling.org
+  if type -p ifne &>/dev/null; then
+    /usr/local/bin/log-once "$@" | ifne mail -s "$HOSTNAME: system-status $2" daylert@iankelling.org
+  fi
 }
 
-
+# todo, consider migrating some of these alerts into prometheus
 write-status() {
   chars=("${first_chars[@]}")
 
-  # clock us out in timetrap if are idle too long
-  if [[ -e /p/.timetrap.db ]]; then
-    export DISPLAY=:0
-    if type -p xprintidle &>/dev/null && xidle=$(xprintidle 2>/dev/null); then
-      if [[ $xidle == [0-9]* ]]; then
-        sheet=$(sqlite3 /p/.timetrap.db "select sheet from entries where end is NULL;")
-        idle=300000
-        if [[ $sheet == w ]]; then
-          idle=900000
-        fi
-        if [[ $sheet && $xidle -gt $idle ]]; then
-          timetrap out
-        fi
+  services=( epanicclean )
+  case $HOSTNAME in
+    bk|je|li) : ;;
+    *)
+      services+=(
+        systemstatus
+        btrfsmaintstop
+        dynamicipupdate
+      )
+      bads=()
+      if systemctl show -p SubState --value ${services[@]} | egrep -v '^(running|)$' &>/dev/null; then
+        for s in ${services[@]}; do
+          if [[ $(systemctl show -p SubState --value $s 2>&1) != running ]]; then
+            bads+=($s)
+          fi
+        done
+        chars+=(MYSERS)
       fi
-    fi
+      p ${bads[*]} | lo -240 mysers
+      ;;
+  esac
+
+  case $HOSTNAME in
+    kd)
+      services=(
+        prometheus-node-exporter
+        prometheus-alertmanager
+        prometheus
+      )
+      bads=()
+      if systemctl show -p SubState --value ${services[@]} | egrep -v '^(running|)$' &>/dev/null; then
+        for s in ${services[@]}; do
+          if [[ $(systemctl show -p SubState --value $s 2>&1) != running ]]; then
+            bads+=($s)
+          fi
+        done
+        chars+=(PROM)
+      fi
+      p ${bads[*]} | lo -240 prom
+      ;;
+  esac
+
+
+  if [[ -e /a/bin/bash_unpublished/source-state ]]; then
+    # /a gets remounted due to btrbk, ignore error code for file doesnt exist
+    source /a/bin/bash_unpublished/source-state || [[ $? == 1 ]]
   fi
+  if [[ $MAIL_HOST == "$HOSTNAME" ]]; then
+
+    bouncemsg=
+    glob=(/m/md/bounces/new/*)
+    if [[ -e ${glob[0]} ]]; then
+      chars+=(BOUNCE)
+      bouncemsg="message in /m/md/bounces/new"
+    fi
+    p $bouncemsg | loday -1 bounce
+    # emails without the S (seen) flag. this only checks the last flag,
+    # but its good enough for me.
+    glob=(/m/md/alerts/{new,cur}/!(*,S))
+    if [[ -e ${glob[0]} ]]; then
+      chars+=(A)
+    fi
+
+    glob=(/m/md/daylert/{new,cur}/!(*,S))
+    if [[ -e ${glob[0]} ]]; then
+      chars+=(DAY)
+    fi
+
+    bbkmsg=
+    if [[ $(systemctl is-active btrbk.timer) != active ]]; then
+      chars+=(BTRBK.TIMER)
+      bbkmsg="not enabled"
+    fi
+    p "$bbkmsg" | lo -480 btrbk.timer
+
+    ## check if last snapshot was within an hour
+    vol=o
+    # this section generally copied from btrbk scripts, but
+    # this part modified to speed things up by about half a second.
+    # I'm not sure if its quite as reliable, but it looks pretty safe.
+    # Profiled it using time and also adding to the top of the file:
+    # set -x
+    # PS4='+ $(date "+%2N") '
+    # allow failure in case there are no snapshots yet.
+    # shellcheck disable=SC2012
+    shopt -u nullglob
+    files=(/mnt/root/btrbk/$vol.20*)
+    shopt -s nullglob
+    snaps=()
+    if (( ${#files[@]} )); then
+      snaps=($(ls -1avdr "${files[@]}" 2>/dev/null |head -n1 || : ))
+    fi
+    now=$EPOCHSECONDS
+    maxtime=0
+    for s in ${snaps[@]}; do
+      file=${s##*/}
+      t=$(date -d $(sed -r  's/(.{4})(..)(.{5})(..)(.*)/\1-\2-\3:\4:\5/' <<<${file#$vol.}) +%s)
+      if (( t > maxtime )); then
+        maxtime=$t
+      fi
+    done
+    snapshotmsg=
+    if (( maxtime < now - 4*60*60 )); then
+      chars+=(OLD-SNAP)
+      snapshotmsg="/o snapshot older than 4 hours"
+    fi
+    p "$snapshotmsg" | lo -1 old-snapshot
+
+
+    # commented out, only using timetrap retrospectively.
+    # # clock us out in timetrap if are idle too long
+    # if [[ -e /p/.timetrap.db ]]; then
+    #   export DISPLAY=:0
+    #   if type -p xprintidle &>/dev/null && xidle=$(xprintidle 2>/dev/null); then
+    #     if [[ $xidle == [0-9]* ]]; then
+    #       sheet=$(sqlite3 /p/.timetrap.db "select sheet from entries where end is NULL;")
+    #       idle=300000
+    #       if [[ $sheet == w ]]; then
+    #         idle=900000
+    #       fi
+    #       if [[ $sheet && $xidle -gt $idle ]]; then
+    #         timetrap out
+    #       fi
+    #     fi
+    #   fi
+    # fi
 
+  fi
 
   if ip l show tunfsf &>/dev/null; then
     # this is for tracking dns over tls issue, which
@@ -73,53 +194,35 @@ write-status() {
     esac
   fi
 
-
-  if pgrep -G iank -u iank -f 'emacs --daemon' &>/dev/null; then
-    emacsfiles="$(emacsclient --eval "$(cat /usr/local/bin/unsaved-buffers.el)"| sed '/^"nil"$/d;s/^"(/E: /;s/)"$//')"
-    if [[ $emacsfiles ]]; then
-      chars+=("$emacsfiles")
+  # We do this once every 5 minutes, since this is not a grave problem.
+  # For formatted elisp, see /b/ds/unsaved-buffers.el
+  elisp='(format "%s" (-reduce-from (lambda (acc buf) (let ((bpath (buffer-file-name buf))) (if (and bpath (buffer-modified-p buf)) (cons bpath acc ) acc))) nil (buffer-list)))'
+  if [[ ! $last_emacs_check || $emacsfiles ]] || (( last_emacs_check < EPOCHSECONDS - 300 )); then
+    if pgrep -G iank -u iank -f 'emacs --daemon' &>/dev/null; then
+      # i dun care if this fails
+      emacsfiles="$(timeout 1 emacsclient --eval "$elisp"| sed '/^"nil"$/d;s/^"(/E: /;s/)"$//' ||:)"
+      if [[ $emacsfiles ]]; then
+        chars+=("$emacsfiles")
+      fi
     fi
+    last_emacs_check=$EPOCHSECONDS
   fi
 
+
   glob=(/nocow/btrfs-stale/*)
   if [[ -e ${glob[0]} ]]; then
-    chars+=("STALE")
+    chars+=(STALE)
   fi
+  var_mail_msg=
   if [[ $(find /var/mail -type f \! -empty -print -quit) ]]; then
     var_mail_msg="message in /var/mail"
   fi
-  loday -1 var_mail $var_mail_msg
-  glob=(/m/md/bounces/new/*)
-  if [[ -e ${glob[0]} ]]; then
-    chars+=("BOUNCE")
-    bouncemsg="message in /m/md/bounces/new"
-  fi
-  loday -1 bounce $bouncemsg
-  # emails without the S (seen) flag. this only checks the last flag,
-  # but its good enough for me.
-  glob=(/m/md/alerts/{new,cur}/!(*,S))
-  if [[ -e ${glob[0]} ]]; then
-    chars+=("A")
-  fi
-
-  glob=(/m/md/daylerts/{new,cur}/!(*,S))
-  if [[ -e ${glob[0]} ]]; then
-    chars+=("L")
-  fi
-
-
-  tmp=(/var/local/cron-errors/mailtest-check*)
-  if (( ${#tmp[@]} )); then
-    chars+=("MAILPING")
-  fi
-  tmp=(/var/local/cron-errors/mailtest-slow*)
-  if (( ${#tmp[@]} )); then
-    chars+=("SPAMD")
-  fi
+  p $var_mail_msg | loday -1 var_mail
 
   # early in install process, we dont have permission yet for exiqgrep.
   # 1100 helps allow for system restarts
   qlen=$(/usr/sbin/exiqgrep -o 1100 -c -b | awk '{print $1}') ||:
+  qmsg=
   if ((qlen)); then
     qmsg="queue length $qlen"
     chars+=("q $qlen")
@@ -127,34 +230,35 @@ write-status() {
   case $HOSTNAME in
     # No point in emailing about the mailq on a host where we don't
     # check email.
-    $MAIL_HOST|bk)
-      loday -120 qlen $qmsg
+    $MAIL_HOST)
+      p $qmsg | loday -120 qlen
       ;;
   esac
 
   begin=false
-  if ! make -C /b/ds -q ~/.local/distro-begin || [[ $(<~/.local/distro-begin) != 0 ]]; then
+
+  if ! make -C /b/ds -q ~/.local/distro-begin 2>/dev/null || [[ $(<~/.local/distro-begin) != 0 ]]; then
     begin=true
   fi
 
   end=false
-  if ! make -C /b/ds -q ~/.local/distro-end || [[ $(<~/.local/distro-end) != 0 ]]; then
+  if ! make -C /b/ds -q ~/.local/distro-end 2>/dev/null || [[ $(<~/.local/distro-end) != 0 ]]; then
     end=true
   fi
 
   # these conditions are so we dont have an overly verbose prompt
   if $begin && $end; then
-    chars+=("D")
+    chars+=(D)
   elif $begin; then
-    chars+=("DB")
+    chars+=(DB)
   elif $end; then
-    chars+=("DE")
+    chars+=(DE)
   else
     f=~/.local/conflink
     # shellcheck disable=SC2043
     for _ in 1; do
       if [[ -e $f ]]; then
-        now=$(date +%s)
+        now=$EPOCHSECONDS
         fsec=$(stat -c%Y $f)
         # the / 60 makes it 0-59 seconds less strict, +1 to help make sure we
         # dont have any false positives.
@@ -175,7 +279,7 @@ write-status() {
         # Just because i forget a lot, -mmin -NUM means files modified <= NUM minutes ago
         if (( fmin < 0 )) && [[ $(find ${all_dirs[@]} -mmin $fmin -type f -print -quit 2>/dev/null) ]]; then
           v conflink newer filesystem files
-          chars+=("CONFLINK")
+          chars+=(CONFLINK)
           break
         fi
 
@@ -188,7 +292,7 @@ write-status() {
           fi
           if (( $(date -d "$(git log --diff-filter=ACR --format=%aD -1)" +%s) > fsec )); then
             v conflink: newer files checked in to git
-            chars+=("CONFLINK")
+            chars+=(CONFLINK)
             break
           fi
 
@@ -198,7 +302,7 @@ write-status() {
           done < <(git ls-files -o --exclude-standard)
           if [[ ${untracked[0]} && $(find "${untracked[@]}" -mmin $fminplus -type f -print -quit) ]]; then
             v conflink: untracked in $d
-            chars+=("CONFLINK")
+            chars+=(CONFLINK)
             break
           fi
         done
@@ -207,66 +311,27 @@ write-status() {
       fi
       if [[ ! -e $f || $(<$f) != 0 ]]; then
         v conflink: last run not found or failed
-        chars+=("CONFLINK")
+        chars+=(CONFLINK)
         break
       fi
     done
   fi
 
-#  if [[ $(grep -v "exim user lost privilege for using -C option" /var/log/exim4/paniclog 2>/dev/null ||:) ]]; then
+  #  if [[ $(grep -v "exim user lost privilege for using -C option" /var/log/exim4/paniclog 2>/dev/null ||:) ]]; then
   if [[ -s /var/log/exim4/paniclog ]]; then
     chars+=("PANIC!")
     # leave it up to epanic-clean to send email notification
   fi
 
-  source /a/bin/bash_unpublished/source-state
-  if [[ $MAIL_HOST == "$HOSTNAME" ]]; then
-    bbkmsg=
-    if [[ $(systemctl is-active btrbk.timer) != active ]]; then
-      chars+=("BTRBK.TIMER")
-      bbkmsg="btrbk.timer not enabled"
+  if [[ ! -e $status_file || -w $status_file ]]; then
+    if [[ -e /a/bin/bash_unpublished/source-state ]]; then
+      cat /a/bin/bash_unpublished/source-state >$status_file
     fi
-    lo -48 btrbk.timer $bbkmsg
 
-    ## check if last snapshot was within an hour
-    vol=o
-    # this section generally copied from btrbk scripts, but
-    # this part modified to speed things up by about half a second.
-    # I'm not sure if its quite as reliable, but it looks pretty safe.
-    # Profiled it using time and also adding to the top of the file:
-    # set -x
-    # PS4='+ $(date "+%2N") '
-    # allow failure in case there are no snapshots yet.
-    # shellcheck disable=SC2012
-    shopt -u nullglob
-    files=(/mnt/root/btrbk/$vol.20*)
-    shopt -s nullglob
-    snaps=()
-    if (( ${#files[@]} )); then
-      snaps=($(ls -1avdr "${files[@]}" 2>/dev/null |head -n1 || : ))
+    if [[ ${chars[*]} ]]; then
+      echo "ps_char=\"${chars[*]} \$ps_char\"" >>$status_file
     fi
-    now=$(date +%s)
-    maxtime=0
-    for s in ${snaps[@]}; do
-      file=${s##*/}
-      t=$(date -d $(sed -r  's/(.{4})(..)(.{5})(..)(.*)/\1-\2-\3:\4:\5/' <<<${file#$vol.}) +%s)
-      if (( t > maxtime )); then
-        maxtime=$t
-      fi
-    done
-    if (( maxtime < now - 4*60*60 )); then
-      chars+=("OLD-SNAP")
-      snapshotmsg="/o snapshot older than 4 hours"
-    fi
-    lo -1 old-snapshot $snapshotmsg
-  fi
-
-  cat /a/bin/bash_unpublished/source-state >$status_file
-
-  if [[ ${chars[*]} ]]; then
-    echo "ps_char=\"${chars[*]} \$ps_char\"" >>$status_file
   fi
-
 }
 # use this if we want to do something just once per minute
 first_chars=()
@@ -279,19 +344,19 @@ if [[ $1 ]]; then
 fi
 
 main-loop() {
-while true; do
-  power=true
-  if [[ -e /sys/class/power_supply/AC/online && $(</sys/class/power_supply/AC/online) == 0 ]]; then
-    power=false
-  fi
-  wait=15
-  if ! $power; then
-    wait=60
-  fi
+  while true; do
+    power=true
+    if [[ -e /sys/class/power_supply/AC/online && $(</sys/class/power_supply/AC/online) == 0 ]]; then
+      power=false
+    fi
+    wait=15
+    if ! $power; then
+      wait=60
+    fi
 
-  sleep $wait
-  write-status
-done
+    sleep $wait
+    write-status
+  done
 }
 
 # ensure our long operations are one line so we are not prone errors