Como executar o serviço de usuário systemd para acionar no modo de suspensão (também conhecido como suspender, hibernar)?

17

Com base em várias fontes que eu juntei ~/.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

Eu o habilitei usando systemctl --user enable screenlock.service. Porém, após a reinicialização, o login, a suspensão e a continuação (testados com systemctl suspende fechando a tampa) a tela não está bloqueada e não há nadajournalctl --user-unit screenlock.service . O que estou fazendo de errado?

A execução DISPLAY=:0 /usr/bin/xautolock -locknowbloqueia a tela conforme o esperado.

$ 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

Se eu executar systemctl --user start screenlock.serviceos bloqueios de tela imediatamente e receber uma mensagem de logon journalctl --user-unit screenlock.service, isso ExecStartestará claramente correto.

.xinitrcSeção relevante :

xautolock -locker slock &

A criação de um serviço do sistema com o mesmo arquivo funciona (ou seja, slockfica ativa ao retomar):

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

Mas não quero adicionar um arquivo específico do usuário fora $HOMEpor vários motivos:

  • Os serviços do usuário devem ser claramente separados dos serviços do sistema
  • Os serviços do usuário devem ser controlados sem o uso de privilégios de superusuário
  • A configuração deve ser facilmente controlada por versão
l0b0
fonte
Estou usando o Awesome como gerenciador de janelas e o SLiM como gerenciador de login . Não estou usando um ambiente de desktop completo , como definido pelo Arch , e Linux / awesome como o ambiente de desktop, conforme definido pela Wikipedia . Não parece haver nada como um "gerenciador de desktop" para Linux.
L0b0
Os serviços do usuário são executados fora da sessão, portanto, os dados da sessão não estão disponíveis para eles; você pode ser melhor fora de usar um arquivo de serviço padrão para isso: pelo menos para teste de qualquer maneira ...
jasonwryan
@jasonwryan Certamente eu veria algum tipo de mensagem de erro no diário se o serviço tivesse sido acionado?
L0b0
Não sei: systemd-userainda é muito esquisito; fazê-lo funcionar como parte da sessão por meio da abordagem que descrevi ajudaria a diminuir o problema; é tudo o que posso sugerir.
jasonwryan
Embora não seja uma solução perfeita (ainda precisaria ser gerenciada com permissões de root), você pode simplesmente usar /etc/systemd/system/ou $HOME/.local/systemd/systemevitar colocar qualquer coisa /usrmanualmente. Como o @jasonwryan mencionou, as sessões do usuário ainda não são consideradas como qualidade de produção; mas eles estão se aproximando.
precisa saber é o seguinte

Respostas:

20

sleep.targeté específico para os serviços do sistema. O motivo é que sleep.targetnão é um alvo mágico que é ativado automaticamente quando você dorme. É apenas um alvo comum que coloca o sistema no modo de suspensão - para que as instâncias de 'usuário' obviamente não tenham um equivalente. (E, infelizmente, as instâncias de 'usuário' atualmente não têm como depender de serviços em todo o sistema.)

(Isso e existe toda a empresa "codificar $ DISPLAY". Toda vez que você codifica os parâmetros da sessão em um sistema operacional baseado no Unix altamente multiusuário / multi-banco, a raiz mata um gatinho.)

Portanto, existem duas boas maneiras de fazer isso (sugiro a segunda):

Método 1

Crie um serviço do sistema (ou um gancho systemd-sleep (8)) que faça o systemd-logind transmitir o sinal "bloquear todas as sessões" quando o sistema entrar em suspensão:

ExecStart=/usr/bin/loginctl lock-sessions

Então, dentro da sua sessão X11 (ou seja, de ~ / .xinitrc), execute algo que reaja ao sinal:

systemd-lock-handler slock &
xss-lock --ignore-sleep slock &

(GNOME, Canela, KDE, Iluminismo já suportam isso nativamente.)

Método 2

Na sua sessão do X11, execute algo que observe diretamente o sistema entrar em suspensão, por exemplo, conectando os "inibidores" do systemd-logind.

O xss-lock acima mencionado, na verdade, faz exatamente isso, mesmo sem o sinal explícito "bloquear tudo", portanto é suficiente fazê-lo funcionar:

xss-lock slock &

Ele será executado slockassim que o systemd-logind estiver se preparando para suspender o computador.

user1686
fonte
Você poderia elaborar um pouco sobre o Iluminismo e o suporte nativo de outras pessoas? Não está claro o que exatamente eles sustentam nativamente a partir da resposta.
Pavel Šimerda
@ PavelŠimerda: O sinal de "sessão de bloqueio" do systemd-logind (... toda a seção é sobre isso ...) Além disso, eu estava errado, o e19 na verdade não o suporta.
user1686
Obrigado pela informação sobre o E19. A resposta ainda carece de explicação sobre o que exatamente o Gnome e outros apoiam. Ouvir o sinal D-Bus do systemd (mesmo que não esteja escrito lá) é uma coisa, quais ações são realizadas em reação e quais ações e como o usuário pode configurar para ser executado é outra. Também não há informações sobre o que o systemd-lock-handler faz e de onde vem.
Pavel Šimerda
xss-lockestá no AUR, portanto não há necessidade de construí-lo manualmente.
L0b0 19/07/2015
Isso funciona muito bem nos testes do Debian. Obrigado por publicar. É muito decepcionante que systemd não permite serviços ao usuário a depender de serviços de sistemas de ...
cgogolin
-1

systemd-lock-handleré um script Python que pode fazer isso: 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()
yaerotugh
fonte