Hibernation with apcupsd

Now that most Linuxen have actually gotten hibernation to work reasonably, all that remains is to hook it up with apcupsd. Apcupsd has a simple hook for actions to take when the maximum time on battery is over, and there are standard instructions on the Web about how to use this hook to issue a killpower to the UPS. But there is one subtlety with hibernation that may trip you up. It probably does not show up with a USB-connected UPS but does strike on a UPS connected by APC's custom serial cable.

During a normal shutdown induced by the UPS running too long on battery during an outage, the halt sequence shuts down apcupsd relatively early on. Then, very late in the halt sequence, apcupsd is called again, not as a daemon, but to send the killpower signal to the UPS. After a grace delay, the UPS pulls down the output power line.

In contrast, during hibernation, the apcupsd process is not terminated, but frozen. It continues to hold a lock on the serial port (e.g., /dev/ttyS0) used to communicate with the UPS. As a result, the second apcupsd invocation that tries to send a killpower may fail. In my experience it does so once every 3--4 trials.

The solution is to terminate the daemon in the custom hibernate script, and send the killpower some time later, as shown in the following script /etc/pm/sleep.d/99_apcupsd.

#!/bin/bash

case $1 in
    hibernate)
        # See if this is a powerfail situation.
        if [ -f /etc/apcupsd/powerfail ]; then
        /etc/init.d/apcupsd stop
        killall -TERM apcupsd
        echo
        echo "!!! Killpower !!!"
        sleep 5
        /etc/apcupsd/apccontrol killpower
        sleep 5
        fi
        ;;
    thaw)
        # Restart apcupsd to hopefully forget hibernate episode
        echo
        echo "Restarting apcupsd after thaw."
        echo
        killall -TERM apcupsd
        /etc/init.d/apcupsd start
        ;;
esac