Iniciando serviços do systemd compartilhando uma sessão D-Bus no sistema headless

2

Preciso de ajuda para iniciar serviços que se comunicam por meio de uma sessão (não do sistema) D-Bus em um sistema Linux sem cabeça. A chave é que ninguém estará logado no sistema sem cabeça.

Até agora eu consegui iniciar um daemon D-Bus e testar a comunicação D-Bus em nome de um usuário ("otheruser") que não está logado, em três terminais diferentes:

No primeiro terminal, inicio um daemon D-Bus para o "otheruser":

$ sudo -u otheruser dbus-daemon --session --print-address 1
unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48

No segundo terminal, inicio o aplicativo do servidor D-Bus usando a resposta DBUS_SESSION_BUS_ADDRESS acima:

$ sudo -u otheruser DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48" /usr/bin/my-dbus-service

Então, no terceiro terminal, posso testar a conexão:

$ sudo -u otheruser DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48" gdbus introspect --session --dest com.mycompany.myappname --object-path /com/mycompany/interface

Mas, quero iniciar o aplicativo do servidor D-Bus, bem como alguns serviços cliente D-Bus via systemd. Como faço para iniciar uma sessão do D-Bus via systemd para que sua variável de ambiente DBUS_SESSION_BUS_ADDRESS seja propagada para o servidor D-Bus e serviços do cliente para "otheruser"?

Uma possível solução pode ser canalizar a saída do dbus-daemon para um "algum arquivo" e, em seguida, definir DBUS_SESSION_BUS_ADDRESS = $ (cat somefile) antes de iniciar o servidor e os clientes do D-Bus. Isso parece um pouco estranho para mim; especialmente porque estou ciente de que há alguma mágica com uma diretiva "Busname" no arquivo de serviço systemd para as conexões D-Bus do system . Como faço para iniciar corretamente os serviços systemd para "otheruser" para que esses serviços systemd possam se comunicar com uma interface D-Bus de sessão?

    
por Ole Wolf 16.01.2018 / 15:53

1 resposta

2

Você precisa de várias coisas para fazer isso funcionar.

  1. Permitir que os serviços do usuário sejam executados no momento da inicialização sem o login do usuário (systemd linger).
  2. Um arquivo de soquete systemd para especificar o soquete D-Bus para o systemd alocar.
  3. Um serviço do systemd para iniciar o barramento de sessão do D-Bus que é iniciado e, em seguida, define a variável de env DBUS_SESSION_BUS_ADDRESS para outros serviços do systemd.
  4. Assegure-se de que seus arquivos my-dbus-client.service do systemd% sejam Type=dbus ou dependam da unidade dbus.socket para garantir que eles aloquem o soquete do barramento da sessão dbus e iniciem o serviço da sessão dbus se ele ainda não tiver sido iniciado.

Primeiro, para fazer com que os serviços do Systemd para um determinado usuário iniciem no tempo de inicialização sem login, é necessário ativar o usuário systemd remanescente - isso só precisa ser feito uma vez como root ao configurá-lo para um usuário:

# loginctl enable-linger otheruser

Em seguida, se você estiver em um sistema baseado no Debian, para as próximas duas etapas, você pode simplesmente instalar o pacote pacote dbus-user-session:

# apt-get install dbus-user-session

Se você estiver usando alguma outra distribuição, faça isso manualmente ou apenas queira entender como ela funciona. Caso contrário, pule a criação de dbus.service e dbus.socket .

Crie o arquivo /usr/lib/systemd/user/dbus.socket (note que em algumas distribuições o diretório do usuário pode estar em /lib em vez de /usr/lib ) com o seguinte conteúdo:

[Unit]
Description=D-Bus User Message Bus Socket

[Socket]
ListenStream=%t/bus
ExecStartPost=-/bin/systemctl --user set-environment DBUS_SESSION_BUS_ADDRESS=unix:path=%t/bus

[Install]
WantedBy=sockets.target
Also=dbus.service

A propagação de DBUS_SESSION_BUS_ADDRESS para todos os serviços, que era sua principal preocupação, é tratada pela linha ExecPostStart abaixo - todos os serviços a seguir terão esse conjunto.

%t é substituído pelo XDG_RUNTIME_DIR - um diretório transitório em /run criado pelo systemd específico da sessão do usuário que você pode preencher os arquivos. Se você deseja criar este soquete em outro lugar, não há motivo para você não poder. Apenas certifique-se de que esteja em algum lugar transitório ou que seja limpo na reinicialização / desmontagem da sessão.

Eu tive alguns problemas tentando tornar o soquete unix dbus um resumo - o systemd parecia não gostar do prefixo unix:abstract= or @ por algum motivo.

Agora crie o arquivo /usr/lib/systemd/user/dbus.service com o seguinte conteúdo:

[Unit]
Description=D-Bus User Message Bus
Requires=dbus.socket

[Service]
ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation
ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig

[Install]
Also=dbus.socket

Existe um pouco de mágica que acontece aqui nos bastidores pelo systemd para passar o socket unix já criado para o dbus-daemon. O Systemd usa as informações de dbus.socket para criar o soquete e seu descritor de arquivo é definido na variável de ambiente LISTEN_FDS , que é passada para o dbus-daemon . Opções especiais listadas acima fazem o dbus-daemon usar o descritor de arquivo passado em vez de criar um novo. Isso permite que os clientes dbus iniciem paralelamente ao dbus-daemon, iniciando sem preocupações de que o socket não exista.

Por fim, crie seus próprios serviços de usuário do systemd, certificando-se de definir o tipo como Type=dbus , definir BusName= como o nome de um dos nomes de serviço dbus que serão registrados por esse serviço ou Certifique-se de que Requires=dbus.socket esteja especificado na seção Unidade. Aqui está um exemplo:

[Unit]
Description=Config Server Startup

[Service]
Type=dbus
BusName=com.example.app.configuree
ExecStart=/opt/example/app/configuration_server
Restart=on-failure

[Install]
WantedBy=default.target

Você pode colocá-los em um dos vários lugares:  - $HOME/.config/systemd/user  - /usr/lib/systemd/user

Ative seus serviços com systemctl --user enable <service name> e reinicialize e tudo deve funcionar.

Referências:

  • man loginctl para atrasar
  • man pam_systemd para informações de XDG_RUNTIME_DIR
  • man systemd.service para Type = dbus, BusName = e dependência implícita em dbus.socket
  • man sd_listen_fds para informações sobre a variável de ambiente LISTEN_FDS
  • link - informações gerais sobre sessões de usuários do systemd
por 05.04.2018 / 19:21