Resume/unlock gate for graphical systemd sessions.
This project provides:
- a systemd sleep hook that reacts to suspend/hibernate (pre) and resume (post)
- a oneshot gate service that waits until a graphical user session is present and unlocked
- a target that is started only after the gate condition is met
The result is a stable hook point for services that must not run immediately after resume, but only once a real, unlocked graphical session exists.
Some workloads should only start after resume and after the user has unlocked their session (for example: user-facing agents, mounts that depend on a logged-in session, desktop integrations, or services that misbehave while the screen is locked).
unlocked-graphical-target creates a dedicated target you can use as an ordering and activation boundary.
-
On suspend/hibernate (system-sleep
pre):systemctl stop unlocked-graphical.target(best-effort)
-
On resume (system-sleep
post):systemctl start --no-block unlocked-graphical-gate.service(best-effort)
-
The gate service (
Type=oneshot) runs/usr/sbin/wait-for-unlock.sh:- polls
loginctl list-sessionsand inspects each session’sLockedHint - ignores sessions without a real seat (
seat == "-") - exits successfully once it finds at least one unlocked session on a real seat
- polls
-
After the wait script exits 0, the gate service starts the target:
systemctl start unlocked-graphical.target
The sleep hook logs via logger with tag unlocked-graphical-gate.
- systemd with logind (
loginctl) - a graphical login on a real seat (the wait script ignores seatless sessions)
/bin/systemctland/usr/bin/loggeravailable (typical on systemd systems)
The Debian package declares Depends: systemd.
This installs the scripts and systemd units under prefix-aware paths (default prefix is /usr).
sudo make install
sudo systemctl daemon-reloadUninstall:
sudo make uninstall
sudo systemctl daemon-reloadNote on prefixes:
- The unit file
unlocked-graphical-gate.servicecurrently calls/usr/sbin/wait-for-unlock.sh. - If you change
PREFIX, ensure the unit’sExecStartmatches the installed location.
This repository contains Debian packaging metadata (debian/). CI uses debcrafter to build a .deb and uploads it as a release asset.
After installing the package, systemd is reloaded in postinst.
To start a service when unlocked-graphical.target is reached, add WantedBy=unlocked-graphical.target and enable it.
Example service drop-in or unit:
[Unit]
Description=Example service that should run only after resume and unlock
After=unlocked-graphical.target
Wants=unlocked-graphical.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my-service
[Install]
WantedBy=unlocked-graphical.targetThen enable it:
sudo systemctl enable my-service.serviceYou normally do not need to enable unlocked-graphical-gate.service or unlocked-graphical.target; the sleep hook triggers the gate automatically after resume.
For debugging, you can run the gate manually:
sudo systemctl start unlocked-graphical-gate.serviceCheck logs and status:
journalctl -t unlocked-graphical-gate -b
systemctl status unlocked-graphical-gate.service
systemctl status unlocked-graphical.targetIf the target never starts, validate that:
- at least one session shows
LockedHint=no(loginctl show-session <id> -p LockedHint) - the session has a real seat (not
-)
sbin/wait-for-unlock.sh– polls logind until an unlocked session existssystem-sleep/unlock-gate.sh– systemd sleep hook (pre/post)system/unlocked-graphical-gate.service– oneshot gate servicesystem/unlocked-graphical.target– target started after the gatedebian/– Debian packaging
MIT. See LICENSE and debian/copyright.