5

I have a service X that needs to connect to listening socket when it starts. That target socket is itself opened by another service Y started by systemd.

Is there any way to specify in the unit file (or otherwise) to only start service X after service Y has successfully started and opened the listening socket?

Note that I can not change service X to retry if the initial connect fails. Also fixed delays don't work well either because service Y takes varying amounts of time before it opens the listening socket.

  • 1
    Can Y be programmed to use the systemd interface and notify systemd when it is ready? – thrig Oct 30 '16 at 15:31
  • Y is open source, so I guess anything is possible but I was hoping to find a way to do this without modifying programs. Thx. – bigjosh Oct 30 '16 at 15:50
6

systemd tends to work slightly differently. You configure systemd to create and listen on the socket, and should anyone like X try to connect, then systemd launches Y to handle the connection, passing it the socket. So X can start notionally before Y, but it will not matter. Later connections will be handled by Y. (You can also configure it so Y is restarted for each connection, but I presume that is not your case).

The minimal changes to Y are to have it accept the pre-created socket as its stdin/stdout file descriptor, instead of doing the create/bind itself.

Here's a test setup you can try, not as root. You need 3 unit files. ~/.local/share/systemd/user/mysock.socket tells systemd to create the socket and how to pass it on:

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

~/.local/share/systemd/user/mysock.service (having the same name mysock) is the service that will be started should anyone connect to the socket. This is where you start Y, which I have replaced by some python.

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

Finally, your X service has a Unit saying it requires the socket. For X I am using a socat that writes the date to the socket. ~/.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

The python takes the socket on stdin, i.e. file descriptor 0, and wraps it into a suitable python socket object, does an accept() and can read/write to it: ~/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()

After a systemctl --user daemon-reload, you should be able to run X with

systemctl --user start mysockdepend

and see in the logs with journalctl that Y was started, and the debug output with the date.

Read about socket activation and 2nd from the man who invented it.

1

systemd handles this case with socket files.

A systemd unit file named some-socket.socket would be created to represent the socket.

Then your Service X service file can include a After= directive which references the socket.

The official systemd documentation on socket files should be helpful.

  • This does not mention the modifications to service Y that have to be made, given the behaviour of service Y that is described in the question. Apply socket units to an unmodified service Y, and one ends up with multiple listening sockets only one of which works. Ironically, the socket that doesn't work will be the one that service X will try to connect to, and the actual goal of the exercise will not be achieved. – JdeBP Oct 31 '16 at 9:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.