¿Cómo ejecutar el servicio de usuario systemd para activar el modo de suspensión (también conocido como suspender, hibernar)?


17

Basado en varias fuentes que he improvisado ~/.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

Lo he habilitado usando systemctl --user enable screenlock.service. Pero después de reiniciar, iniciar sesión, suspender y reanudar (probado con systemctl suspendy al cerrar la tapa) la pantalla no está bloqueada y no hay nada adentrojournalctl --user-unit screenlock.service . ¿Qué estoy haciendo mal?

La ejecución DISPLAY=:0 /usr/bin/xautolock -locknowbloquea la pantalla como se esperaba.

$ 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 (nobody@)
 • Compiled against Lua 5.2.3 (running with Lua 5.2)
 • D-Bus support: ✔
$ slim -v
slim version 1.3.6

Si ejecuto systemctl --user start screenlock.servicelos bloqueos de pantalla inmediatamente y recibo un mensaje de registro journalctl --user-unit screenlock.service, entonces ExecStartclaramente es correcto.

.xinitrcSección relevante :

xautolock -locker slock &

Crear un servicio del sistema con el mismo archivo funciona (es decir, slockestá activo cuando se reanuda):

# ln -s "${HOME}/.config/systemd/user/screenlock.service" /usr/lib/systemd/system/screenlock.service
# systemctl enable screenlock.service
$ systemctl suspend

Pero no quiero agregar un archivo específico de usuario fuera $HOMEpor varias razones:

  • Los servicios de usuario deben estar claramente separados de los servicios del sistema.
  • Los servicios de usuario deben controlarse sin usar privilegios de superusuario
  • La configuración debe ser fácilmente controlada por la versión

Estoy usando awesome como administrador de ventanas y SLiM como administrador de inicio de sesión . No estoy usando un entorno de escritorio completo como lo define Arch , y Linux / awesome como el entorno de escritorio como lo define Wikipedia . No parece haber nada como un "administrador de escritorio" para Linux.
l0b0

Los servicios de usuario se ejecutan fuera de la sesión, por lo que sus datos de sesión no están disponibles para ellos; podría ser mejor usar un archivo de servicio estándar para esto: al menos para probar de todos modos ...
jasonwryan

@jasonwryan ¿Seguramente vería algún tipo de mensaje de error en el diario si el servicio se hubiera activado?
l0b0

No lo sé: systemd-usersigue siendo muy escamoso; lograr que funcione como parte de la sesión a través del enfoque que describí ayudaría a reducir el problema; Eso es todo lo que puedo sugerir.
jasonwryan

Aunque no es una solución perfecta (aún necesitaría ser administrado con permisos de root), simplemente puede usar /etc/systemd/system/o $HOME/.local/systemd/systemevitar poner algo /usrmanualmente. Como @jasonwryan mencionó, las sesiones de usuario todavía no se consideran de calidad de producción; Pero se están acercando.
HalosGhost

Respuestas:


20

sleep.targetEs específico de los servicios del sistema. La razón es sleep.targetque no es un objetivo mágico que se activa automáticamente cuando se va a dormir. Es solo un objetivo regular que pone el sistema en suspensión, por lo que las instancias de 'usuario', por supuesto, no tendrán un equivalente. (Y desafortunadamente, las instancias de 'usuario' actualmente no tienen forma de depender de los servicios de todo el sistema).

(Eso, y está todo el negocio del "hardcoding $ DISPLAY". Cada vez que codifica los parámetros de sesión en un sistema operativo que se basa en Unix multiusuario / multiusuario, la raíz mata a un gatito).

Entonces, hay dos buenas maneras de hacer esto (sugiero la segunda):

Método 1

Cree un servicio del sistema (o un enlace systemd-sleep (8)) que haga que systemd-logind difunda la señal de "bloquear todas las sesiones" cuando el sistema se suspenda:

ExecStart=/usr/bin/loginctl lock-sessions

Luego, dentro de su sesión X11 (es decir, desde ~ / .xinitrc), ejecute algo que reaccione a la señal:

systemd-lock-handler slock &
xss-lock - bloqueo de sueño y sueño

(GNOME, Cinnamon, KDE, Enlightenment ya lo admiten de forma nativa).

Método 2

Dentro de su sesión X11, ejecute algo que vigile directamente que el sistema se vaya a dormir, por ejemplo, conectándose a los "inhibidores" de systemd-logind.

El mencionado xss-lock en realidad hace exactamente eso, incluso sin la señal explícita de "bloquear todo", por lo que es suficiente para que se ejecute:

xss-lock slock &

Se ejecutará slocktan pronto como vea systemd-logind preparándose para suspender la computadora.


¿Podría por favor explicar un poco sobre la Ilustración y el apoyo nativo de otros? No está claro qué es exactamente lo soportan de forma nativa a partir de la respuesta.
Pavel Šimerda

@ PavelŠimerda: La señal de "bloqueo de sesión" de systemd-logind (... toda la sección trata sobre eso ...) Además, me equivoqué, e19 en realidad no lo admite.
user1686

Gracias por la información sobre E19. La respuesta aún carece de explicación sobre qué es exactamente lo que Gnome y otros apoyan. Escuchar una señal D-Bus de systemd (incluso si no está escrita allí) es una cosa, qué acciones se realizan en reacción y qué acciones puede configurar el usuario para hacer, y otra. Tampoco hay información sobre qué hace systemd-lock-handler y de dónde viene.
Pavel Šimerda

xss-lockestá en el AUR, por lo que no hay necesidad de construirlo manualmente.
l0b0

Esto funciona muy bien bajo las pruebas de Debian. Gracias por publicar. Es bastante decepcionante que systemd no permita que los servicios del usuario dependan de los servicios del sistema ...
cgogolin

-1

systemd-lock-handleres un script de Python que puede lograr esto: 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()
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.