Protecting My Home Server’s Power Button with molly-guard and acpid
A while ago I took one of the old desktop computers in the house and turned it into a basic home server. Right now its main functions are printing and file storage, and most of the time it can be turned off. To make this easy, I’ve kept the default behavior for the power button to shut down the system.
But just shutting down a server like this is risky. What if someone is still logged in and working on something? That work would be lost if the server was shut down unexpectedly.
Guarding Shutdown Commands with molly-guard
The molly-guard package is available in Debian (and derivative distributions such as Ubuntu). It offers some protection against accidental shutdown by replacing the shutdown
, poweroff
, and similar commands with “guarded” versions. The “guards” are a series of scripts located in /etc/molly-guard/run.d/
, and if one of them fails (exits with a nonzero exit code), the operation is canceled.
By default, there are only two basic scripts:
- The
10-print-message
script displays a customizable message. - The
30-query-hostname
script tries to detect an SSH session and prompts for the hostname as confirmation.
Note that the numeric prefixes control execution order. The scripts are run in order of filename, and if one fails the rest are not run. I wanted my guards to run after the message is displayed but before the hostname prompt, so I named my scripts starting with 20-
.
First, I wrote a script to check for active terminal sessions:
#!/bin/sh
ACTIVE_TERMINALS=$(who | wc -l)
CURRENT_TERMINALS=$(who -m | wc -l) # 1 if run from a terminal, 0 otherwise
if [ $ACTIVE_TERMINALS -gt $CURRENT_TERMINALS ]; then
echo 'W: there are other active terminal sessions.' >&2
exit 1
fi
Then, I wrote a script to check for active print jobs:
#!/bin/sh
# lpstat -u without a user argument lists jobs from all users
# (although this does not appear to be documented)
ACTIVE_PRINT_JOBS=$(lpstat -u | wc -l)
if [ $ACTIVE_PRINT_JOBS -gt 0 ]; then
echo 'W: there are active print jobs.' >&2
exit 1
fi
I placed these into /etc/molly-guard/run.d/
, making sure to set execute permissions so they would actually run.
Guarding the Power Button with acpid
There’s a problem, though: While molly-guard
guards the shutdown commands, it doesn’t do anything with the power button, which was my original goal. This is because the power button is handled (at least on this system) by systemd-logind
, which is a separate service.
Fixing this involves two steps:
- Stop
systemd-logind
from handling the power button - Set up acpid to handle the power button by running the (guarded)
shutdown
command
First, to stop systemd-logind
from handling the power button, I edited /etc/systemd/logind.conf
. It had some lines that look like this:
...
#HandlePowerKey=poweroff
#HandleSuspendKey=suspend
#HandleHibernateKey=hibernate
...
These commented-out lines show the default behavior. As you might expect, the default behavior for the power button (or “power key” as it’s called here) is to power off the computer.
I uncommented the line and changed the behavior to “ignore”:
HandlePowerKey=ignore
Then, to set up acpid
, I first installed the package:
apt install --no-install-recommends acpid
I included the --no-install-recommends
option to prevent the acpi-support-base
package from being installed. That package includes some default configuration for handling the power button, but the problem with that configuration is that it will defer to systemd-logind
if it detects that it’s running. It’s the exact opposite of what I’m trying to do: I specifically want to bypass systemd-logind
and have acpid
handle it instead.
Without the acpi-support-base
package, acpid
isn’t configured to do anything. Fortunately, the example configuration does exactly what I need. I simply copied two files from /usr/share/doc/acpid/examples/
:
cp /usr/share/doc/acpid/examples/powerbtn /etc/acpi/events/powerbtn
cp /usr/share/doc/acpid/examples/powerbtn.sh /etc/acpi/powerbtn.sh
chmod a+x /etc/acpid/powerbtn.sh # Make the handler script executable
After this, the power button is now guarded.
More Guards?
There are a few other things I haven’t been able to guard yet:
- File transfers using Samba (I’m not sure how to check open connections to Samba)
- ZFS scrubs and resilvers (The
zpool status
command doesn’t offer a machine-readable output option, and it seems I’m not the only one struggling with this)