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.socket
weist 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-reload
sollten Sie in der Lage sein, X auszuführen mit
systemctl --user start mysockdepend
und sehen Sie in den Protokollen, mit journalctl
denen 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.socket
Zur Darstellung des Sockets würde eine Systemd-Unit-Datei mit dem Namen erstellt.
Dann kann Ihre Service X
Servicedatei eine After=
Anweisung enthalten, die auf den Socket verweist.
Die offizielle Systemd-Dokumentation zu Socket-Dateien sollte hilfreich sein.