Einen systemd-Dienst erst starten, *nachdem* der Ziel-Socket lauscht?

Einen systemd-Dienst erst starten, *nachdem* der Ziel-Socket lauscht?

Ich habe einen Dienst X, der beim Start eine Verbindung zum Listening-Socket herstellen muss. Dieser Ziel-Socket wird selbst von einem anderen Dienst Y geöffnet, der von systemd gestartet wurde.

Gibt es eine Möglichkeit, in der Unit-Datei (oder auf andere Weise) anzugeben, dass Dienst X erst gestartet werden soll, nachdem Dienst Y erfolgreich gestartet wurde und den Listening-Socket geöffnet hat?

Beachten Sie, dass ich Dienst X nicht so ändern kann, dass er es erneut versucht, wenn die erste Verbindung fehlschlägt. Auch feste Verzögerungen funktionieren nicht gut, da Dienst Y unterschiedlich lange braucht, bevor er den Abhörsocket öffnet.

Antwort1

systemd funktioniert tendenziell etwas anders. Sie konfigurieren systemd so, dass es den Socket erstellt und abhört, und sollte jemand wie X versuchen, eine Verbindung herzustellen, startet systemd Y, um die Verbindung zu handhaben, und übergibt ihm den Socket. X kann also theoretisch vor Y starten, aber das spielt keine Rolle. Spätere Verbindungen werden von Y gehandhabt. (Sie können es auch so konfigurieren, dass Y für jede Verbindung neu gestartet wird, aber ich nehme an, das ist bei Ihnen nicht der Fall.)

Die minimalen Änderungen an Y bestehen darin, dass es den vorab erstellten Socket als seinen stdin/stdout-Dateideskriptor akzeptiert, anstatt das Erstellen/Binden selbst durchzuführen.

Hier ist ein Test-Setup, das Sie ausprobieren können, nicht als Root. Sie benötigen 3 Unit-Dateien. ~/.local/share/systemd/user/mysock.socketweist systemd an, den Socket zu erstellen und weiterzugeben:

# create listening socket. on 1st connect run mysock.service
[Socket]
ListenStream=5555
Accept=false

~/.local/share/systemd/user/mysock.service(mit dem gleichen Namen mysock) ist der Dienst, der gestartet wird, wenn sich jemand mit dem Socket verbindet. Hier starten Sie Y, das ich durch Python ersetzt habe.

[Unit]
Description=started from mysock.socket
[Service]
ExecStart=/home/meuh/bin/python/mysock.py
StandardInput=socket

Schließlich verfügt Ihr X-Dienst über eine Unit, die angibt, dass er den Socket benötigt. Für XI verwende ich ein Socat, das das Datum in den Socket schreibt. ~/.local/share/systemd/user/mysockdepend.service

[Unit]
Description=dependent on socket listening
Requires=mysock.socket
[Service]
ExecStart=/usr/bin/socat -U tcp:localhost:5555 exec:/usr/bin/date

Python nimmt den Socket auf stdin, also den Dateideskriptor 0, und verpackt ihn in ein geeignetes Python-Socket-Objekt, führt einen aus accept()und kann daraus lesen/schreiben: ~/bin/python/mysock.py

#!/usr/bin/python
# started from /home/meuh/.local/share/systemd/user/mysock.service
# with socket on stdin/stdout
import sys, time, socket, subprocess

def debug(msg):
#    time.sleep(3)
    subprocess.call(["/usr/bin/logger", msg])

debug("start\n")
s = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
while True:
    conn, addr = s.accept()
    conn.send("hello\n")
    while True:
        try:
            data = conn.recv(100)
            debug("got "+data+"\n")
            if len(data)<=0: break
        except:
            break
    conn.close()

Nach einem systemctl --user daemon-reloadsollten Sie in der Lage sein, X auszuführen mit

systemctl --user start mysockdepend

und sehen Sie in den Protokollen, mit journalctldenen Y gestartet wurde, und in der Debug-Ausgabe mit dem Datum.

Lesen überSocket-AktivierungUnd2. Platzvon dem Mann, der es erfunden hat.

Antwort2

systemd behandelt diesen Fall mitSocket-Dateien.

some-socket.socketZur Darstellung des Sockets würde eine Systemd-Unit-Datei mit dem Namen erstellt.

Dann kann Ihre Service XServicedatei eine After=Anweisung enthalten, die auf den Socket verweist.

Die offizielle Systemd-Dokumentation zu Socket-Dateien sollte hilfreich sein.

verwandte Informationen