7

I'm currently trying to make a systemd service with two Fifo sockets. These sockets map to stdout and stdin of the application. I'm currently using the following configuration files.

foo.service

[Unit]
Description=foo Fifo test
After=network.target foo-in.socket foo-out.socket
Requires=foo-in.socket foo-out.socket

[Service]
Sockets=foo-out.socket
Sockets=foo-in.socket
StandardOutput=fd:foo-out.socket
StandardInput=fd:foo-in.socket
StandardError=journal
ExecStart=/path/to/foo/exec

foo-out.socket

[Unit]
Description=foo Task Writes to this

[Socket]
Service=foo.service
ListenFIFO=%t/foo/out

foo-in.socket

[Unit]
Description=foo Task reads commands from this

[Socket]
Service=foo.service
ListenFIFO=/run/user/1000/foo/in

I can start the service using the commands systemctl --user daemon-reload and systemctl --user start foo. The problem comes when I try stopping foo.service. I receive this message:

Warning: Stopping foo.service, but it can still be activated by:
  foo-in.socket
  foo-out.socket

Is there a way to stop the sockets automatically when the service is stopped?

Rene
  • 171
  • 1
  • 2
  • 1
    Generally speaking, I avoid directly starting and stopping any service that uses a socket. Instead, I start/stop the socket directly. In your case - with two sockets - you probably want a target that `Wants` both sockets, which are both `PartOf` the target. Then you can manage the whole thing using the target, rather than the service. – rich remer Aug 01 '19 at 17:08

4 Answers4

3

Here's a good example that can be manually shutdown. The example here is in attempt to shutdown the 'docker' service.

root@localhost:~# systemctl stop docker
Warning: Stopping docker.service, but it can still be activated by:
   docker.socket

root@llocalhost:~# systemctl stop docker.socket

root@logicaldoc:~# ps aux | grep docker
root      1519  0.0  0.0  14220   924 pts/0    S+   17:41   0:00 grep --color=auto docker
Archemar
  • 31,183
  • 18
  • 69
  • 104
John Klein
  • 31
  • 1
2

ExecStopPost Method

You can use the [Service] block's ExecStopPost= option to run arbitrary commands when the service stops either after successful completion or as a result of a failure.

Example

Only the last line has been updated.

[Unit]
Description=foo Fifo test
After=network.target foo-in.socket foo-out.socket
Requires=foo-in.socket foo-out.socket

[Service]
Sockets=foo-out.socket
Sockets=foo-in.socket
StandardOutput=fd:foo-out.socket
StandardInput=fd:foo-in.socket
StandardError=journal
ExecStart=/path/to/foo/exec
ExecStopPost=systemctl --user stop foo-out.socket ; systemctl --user stop foo-in.socket

Source: systemd.service (emphasis mine)

Additional commands that are executed after the service is stopped. This includes cases where the commands configured in ExecStop= were used, where the service does not have any ExecStop= defined, or where the service exited unexpectedly. This argument takes multiple command lines, following the same scheme as described for ExecStart=. Use of these settings is optional. Specifier and environment variable substitution is supported. Note that – unlike ExecStop= – commands specified with this setting are invoked when a service failed to start up correctly and is shut down again.

It is recommended to use this setting for clean-up operations that shall be executed even when the service failed to start up correctly. Commands configured with this setting need to be able to operate even if the service failed starting up half-way and left incompletely initialized data around. As the service's processes have been terminated already when the commands specified with this setting are executed they should not attempt to communicate with them.

Note that all commands that are configured with this setting are invoked with the result code of the service, as well as the main process' exit code and status, set in the $SERVICE_RESULT, $EXIT_CODE and $EXIT_STATUS environment variables, see systemd.exec(5) for details.

Note that the execution of ExecStopPost= is taken into account for the purpose of Before=/After= ordering constraints.

1

Add ExecStopPost=/rm (socket pathname) into the /etc/systemctl/system/(???).socket definition, which is a started by the (???).service via "Requires=???.socket".

To remove the socket, stop the service, then also stop the socket. If you do not stop the socket, any client writing to the socket will reactivate the service, as per it's message when you stop the service:

Warning: Stopping (???).service, but it can still be activated by: (???).socket

Stewart
  • 12,628
  • 1
  • 37
  • 80
1

Instead of adding ExecStopPost=systemctl ..., I would rely on systemd's predefined relationship PartOf=.

From the man page:

   PartOf=
       Configures dependencies similar to Requires=, but limited to stopping and
       restarting of units. When systemd stops or restarts the units listed
       here, the action is propagated to this unit. Note that this is a one-way
       dependency — changes to this unit do not affect the listed units.

       When PartOf=b.service is used on a.service, this dependency will show as
       ConsistsOf=a.service in property listing of b.service.  ConsistsOf=
       dependency cannot be specified directly.

In foo-out.socket and foo-in.socket, you can add PartOf=foo.service in your [Unit] section. Then, when foo.service is stopped, the sockets get stopped.


foo.service (untouched)

[Unit]
Description=foo Fifo test
After=network.target foo-in.socket foo-out.socket
Requires=foo-in.socket foo-out.socket

[Service]
Sockets=foo-out.socket
Sockets=foo-in.socket
StandardOutput=fd:foo-out.socket
StandardInput=fd:foo-in.socket
StandardError=journal
ExecStart=/path/to/foo/exec

foo-out.socket

[Unit]
Description=foo Task Writes to this
PartOf=foo.service

[Socket]
Service=foo.service
ListenFIFO=%t/foo/out

foo-in.socket

[Unit]
Description=foo Task reads commands from this
PartOf=foo.service

[Socket]
Service=foo.service
ListenFIFO=/run/user/1000/foo/in
Stewart
  • 12,628
  • 1
  • 37
  • 80