Based on various sources I have cobbled together ~/.config/systemd/user/screenlock.service:
[Unit] Description=Lock X session Before=sleep.target [Service] Environment=DISPLAY=:0 ExecStart=/usr/bin/xautolock -locknow [Install] WantedBy=sleep.target
I’ve enabled it using systemctl --user enable screenlock.service. But after rebooting, logging in, suspending and resuming (tested both with systemctl suspend and by closing the lid) the screen is not locked and there is nothing in journalctl --user-unit screenlock.service. What am I doing wrong?
Running DISPLAY=:0 /usr/bin/xautolock -locknow locks the screen as expected.
$ systemctl --version systemd 215 +PAM -AUDIT -SELINUX -IMA -SYSVINIT +LIBCRYPTSETUP +GCRYPT +ACL +XZ +SECCOMP -APPARMOR $ awesome --version awesome v3.5.5 (Kansas City Shuffle) • Build: Apr 11 2014 09:36:33 for x86_64 by gcc version 4.8.2 (<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="036d6c616c677a43">[email protected]</a>) • Compiled against Lua 5.2.3 (running with Lua 5.2) • D-Bus support: ✔ $ slim -v slim version 1.3.6
If I run systemctl --user start screenlock.service the screen locks immediately and I get a log message in journalctl --user-unit screenlock.service, so ExecStart clearly is correct.
xautolock -locker slock &
Creating a system service with the same file works (that is, slock is active when resuming):
# ln -s "${HOME}/.config/systemd/user/screenlock.service" /usr/lib/systemd/system/screenlock.service
# systemctl enable screenlock.service
$ systemctl suspend
But I do not want to add a user-specific file outside $HOME for several reasons:
- User services should be clearly separated from system services
- User services should be controlled without using superuser privileges
- Configuration should be easily version controlled
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
sleep.target is specific to system services. The reason is, sleep.target is not a magic target that automatically gets activated when going to sleep. It’s just a regular target that puts the system to sleep – so the ‘user’ instances of course won’t have an equivalent. (And unfortunately the ‘user’ instances currently have no way to depend on systemwide services.)
(That, and there’s the whole “hardcoding $DISPLAY” business. Every time you hardcode session parameters in an OS that’s based on the heavily multi-user/multi-seat Unix, root kills a kitten.)
So there are two good ways to do this (I suggest the 2nd one):
Method 1
Create a system service (or a systemd-sleep(8) hook) that makes systemd-logind broadcast the “lock all sessions” signal when the system goes to sleep:
ExecStart=/usr/bin/loginctl lock-sessions
Then, within your X11 session (i.e. from ~/.xinitrc), run something that reacts to the signal:
systemd-lock-handler slock &
xss-lock --ignore-sleep slock &
(GNOME, Cinnamon, KDE, Enlightenment already support this natively.)
Method 2
Within your X11 session, run something that directly watches for the system going to sleep, e.g. by hooking into systemd-logind’s “inhibitors”.
The aforementioned xss-lock actually does exactly that, even without the explicit “lock all” signal, so it is enough to have it running:
xss-lock slock &
It will run slock as soon as it sees systemd-logind preparing to suspend the computer.
Method 2
systemd-lock-handler is a Python script which can accomplish this:
https://github.com/grawity/code/blob/master/desktop/systemd-lock-handler.
#!/usr/bin/env python
# systemd-lock-handler -- proxy between systemd-logind's "Lock" signal and your
# favourite screen lock command
from __future__ import print_function
import os, sys, dbus, dbus.mainloop.glib
from gi.repository import GLib
def trace(*args):
global arg0
print("%s:" % arg0, *args)
def setup_signal(signal_handler):
global session_id
bus = dbus.SystemBus()
manager = bus.get_object("org.freedesktop.login1", "/org/freedesktop/login1")
# yecch
manager = dbus.Interface(manager, "org.freedesktop.login1.Manager")
session_path = manager.GetSession(session_id)
session = bus.get_object("org.freedesktop.login1", session_path)
session.connect_to_signal("Lock", signal_handler)
def handler_dbus_fdo():
trace("locking session using DBus")
bus = dbus.SessionBus()
screensaver = bus.get_object("org.freedesktop.ScreenSaver", "/ScreenSaver")
screensaver.Lock()
def handler_external():
global lock_command
trace("locking session using %r" % lock_command[0])
os.spawnvp(os.P_NOWAIT, lock_command[0], lock_command)
def main():
global arg0, lock_command, session_id
arg0 = sys.argv[0].split("/")[-1]
lock_command = sys.argv[1:] or ["--dbus"]
try:
session_id = os.environ["XDG_SESSION_ID"]
except KeyError:
print("error: $XDG_SESSION_ID not set; are you using pam_systemd?",
file=sys.stderr)
sys.exit(1)
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
if lock_command == ["--dbus"]:
trace("using freedesktop.org DBus API")
setup_signal(handler_dbus_fdo)
else:
trace("using external command %r" % lock_command[0])
setup_signal(handler_external)
trace("waiting for lock signals on session %s" % session_id)
try:
loop = GLib.MainLoop()
loop.run()
except KeyboardInterrupt:
sys.exit(0)
main()
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0