web-dev-qa-db-fra.com

Démarrage des services systemd partageant une session D-Bus sur un système sans tête

J'ai besoin d'aide pour démarrer des services qui communiquent via une session (pas un système) D-Bus sur un système Linux sans tête. La clé est que personne ne sera connecté sur le système sans tête.

Jusqu'à présent, j'ai pu démarrer un démon D-Bus et tester la communication D-Bus au nom d'un utilisateur ("autre utilisateur") qui n'est pas connecté, dans trois terminaux différents:

Dans le premier terminal, je lance un démon D-Bus pour "autre utilisateur":

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

Dans le deuxième terminal, je démarre l'application serveur D-Bus en utilisant la réponse DBUS_SESSION_BUS_ADDRESS ci-dessus:

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

Ensuite, dans le troisième terminal, je peux tester la connexion:

$ 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

Mais, je veux démarrer l'application serveur D-Bus ainsi que quelques services client D-Bus via systemd. Comment démarrer une session D-Bus via systemd afin que sa variable d'environnement DBUS_SESSION_BUS_ADDRESS soit propagée au serveur D-Bus et aux services clients pour "autre utilisateur"?

Une solution possible pourrait consister à diriger la sortie de dbus-daemon dans un "certains fichiers", puis à définir DBUS_SESSION_BUS_ADDRESS = $ (cat certains fichiers) avant de démarrer le serveur et les clients D-Bus. Cela me semble un peu trop gênant; surtout parce que je suis conscient qu'il y a de la magie avec une directive "Busname" dans le fichier de service systemd pour les connexions system D-Bus. Comment démarrer correctement les services systemd pour "autre utilisateur" afin que ces services systemd puissent communiquer avec une interface D-Bus de session?

12
Ole Wolf

Vous avez besoin de plusieurs choses pour que cela fonctionne.

  1. Activer les services utilisateur pour s'exécuter au démarrage sans connexion utilisateur (persistance systemd).
  2. Un fichier de socket systemd pour spécifier le socket D-Bus à allouer à systemd.
  3. Un service systemd pour lancer le bus de session D-Bus qui se lance, puis définit la variable d'environnement DBUS_SESSION_BUS_ADDRESS pour d'autres services systemd.
  4. Assurez-vous que votre système est my-dbus-client.service les fichiers sont de Type=dbus ou dépendent du dbus.socket unité pour s'assurer qu'ils allouent la prise de bus de session dbus et démarrent le service de session dbus s'il n'a pas déjà été démarré.

Tout d'abord, pour que les services Systemd pour un utilisateur donné démarrent au démarrage sans connexion, vous devez activer la persistance de l'utilisateur systemd - cela ne doit être effectué qu'une seule fois en tant que root lors de la configuration pour l'activer pour un utilisateur:

# loginctl enable-linger otheruser

Ensuite, si vous êtes sur un système basé sur Debian, pour les deux étapes suivantes, vous pouvez simplement installer le paquet dbus-user-session:

# apt-get install dbus-user-session

Si vous utilisez une autre distribution, souhaitez le faire manuellement ou voulez simplement comprendre comment cela fonctionne. Sinon, ignorez la création de dbus.service et dbus.socket.

Créez le fichier /usr/lib/systemd/user/dbus.socket (notez que sur certaines distributions, le répertoire utilisateur peut se trouver sous /lib au lieu de /usr/lib) avec le contenu suivant:

[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

Propagation de DBUS_SESSION_BUS_ADDRESS à tous les services, ce qui était votre principale préoccupation, est adressé par la ligne ExecPostStart ci-dessous - tous les services suivants auront cet ensemble.

%t est remplacé par le XDG_RUNTIME_DIR - un répertoire transitoire sous /run créé par systemd spécifique à la session utilisateur que vous pouvez bourrer de fichiers. Si vous souhaitez créer ce socket ailleurs, il n'y a aucune raison que vous ne le puissiez pas. Assurez-vous simplement qu'il est quelque part transitoire, ou qu'il est nettoyé lors du redémarrage/démontage de la session.

J'ai eu quelques problèmes en essayant de faire du socket dbus unix un abstrait - systemd ne semblait pas aimer la forme unix:abstract= ou @ préfixe pour une raison quelconque.

Créez maintenant le fichier /usr/lib/systemd/user/dbus.service avec le contenu suivant:

[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

Il y a un peu de magie qui se passe ici dans les coulisses de systemd pour passer le socket unix déjà créé au dbus-daemon. Systemd utilise les informations de dbus.socket pour créer le socket, et son descripteur de fichier est défini dans la variable d'environnement LISTEN_FDS, qui est passé dans le dbus-daemon. Les options spéciales répertoriées ci-dessus font que dbus-daemon utilise le descripteur de fichier transmis au lieu d'en créer un nouveau. Cela permet aux clients dbus de démarrer parallèlement au démarrage du dbus-daemon sans se soucier de l'absence de socket.

Enfin, créez vos propres services utilisateur systemd, en vous assurant de définir le type sur Type=dbus, ensemble BusName= au nom de l'un des noms de service dbus qui seront enregistrés par ce service, ou en vous assurant que Requires=dbus.socket est spécifié dans la section Unité. Voici un exemple:

[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

Vous pouvez les placer à plusieurs endroits: - $HOME/.config/systemd/user - /usr/lib/systemd/user

Activez vos services avec systemctl --user enable <service name> et redémarrez, et tout devrait fonctionner.


Références:

  • man loginctl pour s'attarder
  • man pam_systemd pour XDG_RUNTIME_DIR info
  • man systemd.service pour Type = dbus, BusName = et dépendance implicite sur dbus.socket
  • man sd_listen_fds pour plus d'informations sur la variable d'environnement LISTEN_FDS
  • https://wiki.archlinux.org/index.php/Systemd/User - informations générales sur les sessions utilisateur systemd
14
Keithel