1

I'm trying to launch a meta-service remotely through a socket. Requirements:

  1. The socket should start the meta-service when a connection is established
  2. The meta-service should start all child (Wants=) services upon start
  3. When the connection is closed, the meta-service should stop
  4. When the meta-service stops, it should stop all child (ConsistsOf=) services

I'm not expecting multiple connections, so my requirements are undefined if multiple connections are made.

Here's one attempt:

# simple.socket [Unit] Description=Socket [Socket] ListenStream=11111 Accept=no # simple.service [Unit] Description=Meta-service Wants=simple-child.service # Will be a full tree of dependencies [Service] ExecStart=-cat - # cat will fail to start because it doesn't accept connections StandardInput=socket # simple-child.service [Unit] Description=Child1 PartOf=simple.service # Puts a ConsistsOf= relationship in simple.service [Service] ExecStart=sleep infinity # Business goes here 

The problem here is that when Accept=no, the ExecStart= is responsible for handling the incoming connections. cat - doesn't call accept() and so simple.service will fail to start. Is there another basic tool I could use in ExecStart= which would accept() the connections, but close when the a connection is stopped? It could be the first or last connection, I'm agnostic to that. That would be the easiest solution which also solves the rest of the question. Is there an example of a C-application which uses accept() so I could figure out how systemd passes sockfd for our first argument accept(int sockfd, ...)? Then I could write something myself. I tried running this, but I kept getting failures when using bind().

Here's another attempt using Accept=yes:

# simple.socket [Unit] Description=Socket [Socket] ListenStream=11111 Accept=yes # [email protected] # Note the template here [Unit] Description=Meta-service Wants=simple-child.service [Service] ExecStart=-cat - # now cat will work! StandardInput=socket # simple-child.service [Unit] Description=Child1 [email protected] # This fails to connect to the instance [Service] ExecStart=sleep infinity # Business goes here 

In this case, everything starts great. When the connection is closed, [email protected] stops nicely, but simple-child.service keeps running. That's because [email protected] does not refer to the correct instance. I'd REALLY prefer to avoid templating simple-child.service, but let's try it:

# simple.socket [Unit] Description=Socket [Socket] ListenStream=11111 Accept=yes # [email protected] [Unit] Description=Meta-service Wants=simple-child@%i.service % Starts simple-child as a template [Service] ExecStart=-cat - StandardInput=socket # [email protected] # Newly templated [Unit] Description=Child1 PartOf=simple@%i.service # Using %i [Service] ExecStart=sleep infinity # Business goes here 

In this case [email protected] is templated as [email protected]:11111-127.0.0.1:49276.service, but %i is only 6. It spawns [email protected] which is only [email protected] and so it fails to stop when [email protected]:11111-127.0.0.1:49276.service stops.

1 Answer 1

0

I have a solution, but it's in python which is not ideal for dependency reasons.

# simple.socket [Unit] Description=Socket [Socket] ListenStream=11111 Accept=no # simple.service [Unit] Description=Meta-service Wants=simple-child.service [Service] ExecStart=/usr/local/bin/listen.py # simple-child.service [Unit] Description=Child1 PartOf=simple.service [Service] ExecStart=sleep infinity 

And the python script is:

#!/usr/bin/env python3 from socketserver import TCPServer, StreamRequestHandler import socket class Handler(StreamRequestHandler): def handle(self): while True: c = self.rfile.read(1) if len(c) == 0: exit() class Server(TCPServer): SYSTEMD_FIRST_SOCKET_FD = 3 def __init__(self, server_address, handler_cls): TCPServer.__init__(self, server_address, handler_cls, bind_and_activate=False) self.socket = socket.fromfd(self.SYSTEMD_FIRST_SOCKET_FD, self.address_family, self.socket_type) if __name__ == "__main__": HOST, PORT = "12.34.56.78", 12345 # This gets overridden by systemd server = Server((HOST, PORT), Handler) server.serve_forever() 

Now when the connection is established, simple.service and simple-child.service both launch. When the connection is killed simple.service stops, but somehow simple-child.service doesn't stop despite the PartOf= relationship. Adding ExecStop=systemctl stop simple-child.service to simple.service does work, but that's not a great solution.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.