I'm trying to launch a meta-service remotely through a socket. Requirements:
- The socket should start the meta-service when a connection is established
- The meta-service should start all child (
Wants=) services upon start - When the connection is closed, the meta-service should stop
- 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.