‹ back home

Systemd, locking and sleeping

2022-10-26

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:

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.targets 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.

— § —