Home Chipmonk with NUT to event script power outages
Post
Cancel

Chipmonk with NUT to event script power outages

coard

Ah, shit. The power went out.

So you just found the key, almost have the exploit at a PoC state where it fin… Wait what? The power went out! You just lost your last 10 minutes in between commits. This doesn’t happen if you have a UPS. Even when you do have a UPS, a device that keeps the power on to your most important devices during a power outage for a short while (mine, around 45 minutes) using a battery, you probably want your system to be able to back up it’s most important work and then send a shutdown signal, to shut everything down cleanly and avoid data loss.

Chipmonk

Chipmonk on GitHub

Chipmonk
Chipmonk

Watch what Chipmonk does by default!

The chipmonk code is pretty short and to the point:

#!/bin/bash

# # # Chipmonk
# # # Author: Marshall Whittaker / oxagast
# # # Version: 2.0a
# # # NUT (Network UPS Tools) Visual Power Dialog

# Here you can change the location where Chipmonk will log to.
logloc=/var/log/nut

# The below three functions are for adding user defined scripts!
# I recommend leaving ther placeholders where they are, they are
# innoculous, but the script doesn't work correctly without them
# if nothing else is in the brackets.

function line_power()
{
  :
}

function on_battery()
{
  :
}

function low_battery()
{
  :
}

# Primary functions start here

# Called by trap on a shutdown signal
function clean_up()
{
  echo "Running cleanup, killing upslog..."
  pkill upslog
  rm /tmp/ups.status.$pid
}

# This is for starting and logging things to our log location (default /var/log/nut/)
function log_ups()
{
  long_format='%host% %upshost% %time @Y-@M-@d @H:@M% STA:%VAR ups.status% CRG:%VAR battery.charge% INP:%VAR input.voltage% LO
D:%VAR ups.load% TMP:%VAR ups.temperature%'
  state='%VAR ups.status%'
  if [[ -d $logloc ]]; then
    touch "$logloc/ups.log"
    if [[ -f "$logloc/ups.log" ]]; then
      upslog -l "$logloc/ups.log" -s $model -f "$long_format"
    else
      echo "Your $logloc is not writable by you."
    fi
  fi
  upslog -l /tmp/ups.status.$pid -s $model -f "$state"
}

# Calls kdialog on UPS state changes
function popup()
{
  trap clean_up EXIT
  f="/tmp/ups.status.$pid"
  powerline=$(<"$f")
  inotifywait -m -e modify "$f" --format "%e" | while read -r event 2>/dev/null; do
    if [ "$event" == "MODIFY" ]; then
      prev="$powerline"
      powerline=$(cat "$f" | tail -n 1)
      if [[ "$powerline" != "$prev" ]]; then
        if [[ $last != $powerline ]]; then
          if [[ $powerline == "OL" ]]; then
            kdialog --icon "battery-full-charging" --passivepopup "Notice: UPS $model now on line power." 30
            line_power
          fi
          if [[ $powerline == "OB" ]]; then
            kdialog --icon "battery-low" --passivepopup "Warning: UPS $model now on battery power." 30
            on_battery
          fi
          if [[ $powerline == "LB" ]]; then
            kdialog --icon "battery-caution" --passivepopup "Critical: UPS $model now has a low battery!" 30
            low_battery
          fi
        fi
        last=$powerline
      fi
    fi
  done
}

# startup and config initilization
echo "# # # Chipmonk # # #"
echo "NUT (Network UPS Tools) Visual Power Dialogs"
echo "Author: Marshall Whittaker / oxagast"
logperm=2
pid=$$
if [[ "$#" -gt 1 ]]; then
  echo "You need to specify the NUT configured UPS model@hostname: "
  echo "    chipmonk SK600@gotham"
  exit 1
fi
if [[ "$#" -eq 0 ]]; then
  model=$(grep "[MODEL]" -A 1 ~/.chipmonk | tail -n 1 | tr -d '\n')
  if [ $(echo $model | grep ".@." | wc -l) -eq 1 ]; then
    log_ups
    popup
  fi
else
  if [[ "$#" -eq 1 ]]; then
    model=$1
    if [ $(echo $model | grep ".@." | wc -l) -eq 1 ]; then
      echo "[MODEL]" >~/.chipmonk
      echo $model >>~/.chipmonk
      log_ups
      popup
    fi
  fi
  echo "You need to specify the NUT configured UPS model@hostname: "
  echo "    chipmonk SK600@gotham"
  exit 1
fi

Here I use NUT (Network UPS Tools), in combination with some code I wrote called Chipmonk that monitors UPS power events. Chipmonk monitors NUT logs and then notifies the user that an event is in play foremost. We do this using kdialog.

Logs

Chipmonk uses one log to monitor events only, and another log is used for the user to review. The user review log looks something like this:

jerkon SK600@jerkon 2022-17-15 13:17 STA:OL CRG:NA INP:121.8 LOD:25 TMP:NA
jerkon SK600@jerkon 2022-17-15 13:17 STA:OL CRG:NA INP:121.8 LOD:28 TMP:NA
jerkon SK600@jerkon 2022-18-15 13:18 STA:OB CRG:NA INP:5.2 LOD:30 TMP:NA
jerkon SK600@jerkon 2022-18-15 13:18 STA:OB CRG:NA INP:5.2 LOD:30 TMP:NA
jerkon SK600@jerkon 2022-26-15 13:26 STA:OB CRG:NA INP:5.2 LOD:30 TMP:NA
jerkon SK600@jerkon 2022-26-15 13:26 STA:OL CRG:NA INP:121.8 LOD:25 TMP:NA

Which gives you your monitoring machine’s hostname, the name of the UPS, and what machine it is currently monitoring (NUT can monitor multiple machines), date, time, UPS status, charge left, input voltage, current system load, and battery temperature, in that order.

The status can be:

1
2
3
OL - On line power
OB - On battery power
LB - Low battery power

I believe there are a few other more obscure statuses that I will include io chipmonk eventually.

User Defined

In Chipmonk, near the top of the code, you will see three functions corresponding to the UPS status. You are free to script within these brackets, and it should do what you want around the system (like backup/save files, etc). Fun Fact!: You can do this on lines before or after the : token, however, I suggest not removing the token, as without a NOOP the function breaks the entire script if there is nothing else within the brackets.

Conclusion

coard

Hope you now have a safer hacking experience in a power outage-prone area. You can email me with any questions or comments about this code. Feature requests are also welcome!

Bitcoin Donation Address:
3Ht1soLAdcBXrxbZLDJ53vry819E3rw49d

This post is licensed under CC BY 4.0 by the author.