Locking the system
logind
(part of systemd) emits events when the system is about to be locked
or go into sleep. Typically, one can configure logind
so that closing the
laptop lid triggers a “lock the session” event. However, this just emits a
D-Bus signal and doesn’t provide facilities for running any screen locker. One
needs a separate process listening for this event, and this process itself
needs to run a screen-locker and any other similar actions. This is the role
that a tools of mine, systemd-lock-handler
, has been fulfilling. When one
such event is emitted by systemd/logind, the systemd-lock-handler
starts a
custom systemd user target (sleep.target
). Users can associate services that
should run when this target starts (like a screen locker, or screen dimming,
etc).
It’s hard to see any value being added by logind
for this scenario; the
service that listens to the D-Bus signal could just as well be monitoring the
laptop lid or power button itself. The difference in complexity is immediately
obvious, no obvious added benefits.
Upon re-reading the docs for sway(5), I found bindswitch
. It turns
out I can just tell logind
to just ignore when I close the lid or press the
power button, and have sway
execute a script when that happens instead.
That removes a lot of unnecessary abstractions. One could simply bind this to a script that locks or dims the screen. Stupid simple.
Sleeping
logind
also emits a PrepareForSleep
event before the system goes to sleep
(either suspend or hibernate), and systemd-lock-handler
handles this as well.
This is handy to lock the screen before the system goes into sleep mode, so the
system is already locked when it later resumes.
The documented approach for this kind of scenario is to inhibit suspension permanently. Where there is an attempt to suspend the system, the inhibitor should first run the screen locker, and then allow suspension to continue. This is a terrible API, with too high a level of complexity for something rather trivial.
Let’s summarise how it’s meant to be used:
- A dedicated service runs permanently, inhibiting suspension.
- The service also needs to listen for
logind
’sPreparingForSleep
events. - When the system is about to suspend, a
PreparingForSleep
signal is emitted. - On receiving a
PreparingForSleep
event, the dedicated service runs the screen locker, and then release the inhibition.
The most obvious smell in this interface is the fact that a service needs to run permanently, in order to do something before the system suspends. Instead, the tool requesting the suspension could do this instead.
A solution for this is again simple; instead of using systemd suspend
directly, simply use a script:
screen_locker --fork
systemctl suspend
This tricky part is if Alice suspends the system via ssh
and Bob using that
system interactively (e.g.: sitting in from of it). To be honest, this is a
rather unrealistic scenario anyway, and IIRC logind
would not allow Alice to
suspend the system if she is only logged in remotely.
Issues with systemd-lock-handler
An issue with the current implementation is that, upon receiving a signal, it starts a target, which will then trigger starting of the screen locker asynchronously. This means that it’s still possible for the locker to start too late (e.g.: after the system comes back from sleep).
I’m considering a v3.0.0 that will change how the tool works and simply run a set
of scripts synchronously, so it’s simple to just run something like swaylock -f
in it. It would also be possible to just run a systemd and wait for it to
be ready. The contents of the script are up to the user, but the general
approach reduces some pointless complexity and custom systemd.target
s while
also ensuring that actions are executed before the system sleeps.
In hindsight, this approach is the obvious one, much simpler, and has far less issues.
Conclusion
systemd
is infamous for its complexity. I really appreciate a lot of the
features it bring (dependency resolution, cgroups, socket activation, user
services, and a few others). But when it comes to handling the system power
state and session locking, it is over-engineered, too complex, and does too
little. It requires dedicated services permanently running to listen for
specific events just to do something basic like “run the screen locker before
suspending the system”.
The only real benefit of having logind
handle locking/sleeping when the lid
is closed or when the power button is pressed, is that the system will also go
to sleep if no user is logged in. This is an odd case, but, admittedly a valid
one; if I close my laptop lid without logging in, I’d expect it to go to sleep
as usual. Ideally, however, the display manager to do this for me, but AFAIK,
no display manager actually does.