Starten von systemd-Diensten, die einen Sitzungs-D-Bus auf einem kopflosen System gemeinsam nutzen

12

Ich benötige Hilfe beim Starten von Diensten, die über einen Sitzungs-D-Bus (kein System) auf einem kopflosen Linux-System kommunizieren. Der Schlüssel ist, dass niemand am Headless-System angemeldet ist.

Bisher konnte ich einen D-Bus-Daemon starten und die D-Bus-Kommunikation im Auftrag eines nicht angemeldeten Benutzers ("otheruser") in drei verschiedenen Terminals testen:

Im ersten Terminal starte ich einen D-Bus-Daemon für den "otheruser":

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

Im zweiten Terminal starte ich die D-Bus-Serveranwendung mit der obigen Antwort DBUS_SESSION_BUS_ADDRESS:

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

Dann kann ich im dritten Terminal die Verbindung testen:

$ 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

Ich möchte jedoch die D-Bus-Serveranwendung sowie einige Client-D-Bus-Dienste über systemd starten. Wie starte ich eine D-Bus-Sitzung über systemd, damit die Umgebungsvariable DBUS_SESSION_BUS_ADDRESS für "otheruser" an den D-Bus-Server und die Client-Dienste weitergegeben wird?

Eine mögliche Lösung könnte darin bestehen, die Ausgabe von dbus-daemon in eine "somefile" zu leiten und dann DBUS_SESSION_BUS_ADDRESS = $ (cat somefile) zu setzen, bevor der D-Bus-Server und die Clients gestartet werden. Das kommt mir einfach etwas zu umständlich vor; vor allem, weil mir bewusst ist, dass die Direktive "Busname" in der systemd-Dienstdatei für System- D-Bus-Verbindungen etwas Magisches enthält . Wie starte ich systemd-Dienste für "otheruser" ordnungsgemäß, damit diese systemd-Dienste mit einer Sitzungs-D-Bus-Schnittstelle kommunizieren können?

Ole Wolf
quelle

Antworten:

14

Sie benötigen mehrere Dinge, damit dies funktioniert.

  1. Aktivieren Sie die Ausführung von Benutzerdiensten zum Startzeitpunkt ohne Benutzeranmeldung (systemd verweilen).
  2. Eine systemd-Socket-Datei, um den D-Bus-Socket anzugeben, den systemd zuweisen soll.
  3. Ein systemd-Dienst zum Starten des gestarteten D-Bus-Sitzungsbusses, der dann die Umgebungsvariable DBUS_SESSION_BUS_ADDRESS für andere systemd-Dienste festlegt.
  4. Stellen Sie sicher, dass Ihre systemd- my-dbus-client.serviceDateien Type=dbusvom dbus.socketGerät stammen oder von diesem abhängen , um sicherzustellen, dass sie den dbus-Sitzungsbus-Socket zuweisen, und starten Sie den dbus-Sitzungsdienst, falls dieser noch nicht gestartet wurde.

Damit Systemd-Dienste für einen bestimmten Benutzer zum Startzeitpunkt ohne Anmeldung gestartet werden können, müssen Sie zunächst das Verweilen des Systemd-Benutzers aktivieren. Dies muss bei der Konfiguration nur einmal als Root erfolgen, um es für einen Benutzer zu aktivieren:

# loginctl enable-linger otheruser

Wenn Sie sich auf einem Debian-basierten System befinden, können Sie für die nächsten beiden Schritte einfach das Paket dbus-user-session packen:

# apt-get install dbus-user-session

Wenn Sie eine andere Distribution verwenden, möchten Sie dies manuell tun oder einfach nur verstehen, wie es weitergeht. Andernfalls überspringen Sie die Erstellung von dbus.serviceund dbus.socket.

Erstellen Sie die Datei /usr/lib/systemd/user/dbus.socket(beachten Sie, bei einigen Distributionen befindet sich möglicherweise das Benutzerverzeichnis /libanstelle von /usr/lib) mit dem folgenden Inhalt:

[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

Die Weitergabe DBUS_SESSION_BUS_ADDRESSan alle Dienste, die Ihr Hauptanliegen war, wird in der folgenden ExecPostStartZeile behandelt. Für alle folgenden Dienste wird diese Einstellung festgelegt.

%twird ersetzt durch XDG_RUNTIME_DIR- ein vorübergehendes Verzeichnis, /rundas von systemd erstellt wurde und für die Benutzersitzung spezifisch ist, in die Sie Dateien einfügen können. Wenn Sie diesen Socket an einem anderen Ort erstellen möchten, gibt es keinen Grund, warum Sie dies nicht können. Stellen Sie einfach sicher, dass es irgendwo vorübergehend ist, oder es wird beim Neustart / Herunterfahren der Sitzung bereinigt.

Ich hatte einige Probleme beim Versuch, den dbus-Unix-Socket zu einem abstrakten zu machen - systemd schien die Form unix:abstract=oder das @Präfix aus irgendeinem Grund nicht zu mögen .

Erstellen Sie nun die Datei /usr/lib/systemd/user/dbus.servicemit folgendem Inhalt:

[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

Es gibt ein bisschen Magie, die hier hinter den Kulissen von systemd vor sich geht, um den bereits erstellten Unix-Socket an den dbus-Daemon zu übergeben. Systemd verwendet die Informationen von dbus.socket, um den Socket zu erstellen, und sein Dateideskriptor wird in der Umgebungsvariablen festgelegt LISTEN_FDS, die an die übergeben wird dbus-daemon. Die oben aufgeführten speziellen Optionen veranlassen dbus-daemon, den übergebenen Dateideskriptor zu verwenden, anstatt einen neuen zu erstellen. Auf diese Weise können dbus-Clients parallel zum dbus-Daemon gestartet werden, ohne dass der Socket vorhanden sein muss.

Erstellen Sie abschließend Ihre eigenen systemd-Benutzerdienste, und stellen Sie sicher, dass Sie entweder den Typ auf Type=dbus, BusName=den Namen eines der von diesem Dienst registrierten dbus-Dienstnamen oder Requires=dbus.socketden Abschnitt "Einheit" festlegen. Hier ist ein Beispiel:

[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

Sie können sie an einer von mehreren Stellen platzieren: - $HOME/.config/systemd/user -/usr/lib/systemd/user

Aktivieren Sie Ihre Dienste mit systemctl --user enable <service name>und starten Sie neu, und alles sollte funktionieren.


Verweise:

  • man loginctl zum Verweilen
  • man pam_systemd für XDG_RUNTIME_DIR info
  • man systemd.service für Typ = dbus, Busname = und implizite Abhängigkeit von dbus.socket
  • man sd_listen_fds Informationen zur Umgebungsvariablen LISTEN_FDS
  • https://wiki.archlinux.org/index.php/Systemd/User - Allgemeine Informationen zu Systemd-Benutzersitzungen
Keithel
quelle
Vielen Dank so für diese viel. Es hat mir so viel Arbeit gespart!
Bernardo Meurer
2
Die Dokumentation ist ein wenig spärlich und es ist schwierig, alles zusammenzusetzen. Ich bin froh, dass es jemandem geholfen hat! Vielleicht hilft es mir in Zukunft wieder. : D
Keithel
Eine der besten Antworten, die ich seit langer Zeit gesehen habe. Gute Arbeit!
Norman Ramsey