6

(systemd version 229, fwiw)

I have a primary service A, and a secondary service B. The primary A can run by itself. But service B cannot run correctly by itself: it needs A to be running (technically B can run, but this is what I want systemd to prevent). My goal: If A is not running, B should not run. Given that A and B are running, when A stops or dies/crashes, then B should be stopped.

How do I achieve this?

I get close by adding [Unit] items to b.service, using

Requisite=A.service
After=A.service

The result of the above is that

1) B won't start unless A is running (good).
2) B is stopped when A is stopped (good).
3) However, if I kill A, service B continues to run (bad).

How can I fix this last behavior #3? I tried using BindsTo instead of Requisite, like this in B's service file:

BindsTo=A.service
After=A.service

and I get:

1) If A is not running and I start B, then A is also started
    (I want an error, I don't want A started)
2) B is stopped when A is stopped (good).
3) B is stopped when A is killed (good)

So now #3 is good but #1 is not the desired behaviour. Neither PartOf nor BindsTo seems to do the trick, but perhaps I don't have the right incantation of combinations of options? Its not clear to me from the man pages what options can be combined.

With BindsTo, I also tried failing B's start using ExecStartPre, but of course this didn't work because the start of B had already determined that it needed A to run (and started it), before it fired up B.

Is there some way to get a behaviour inbetween Requisite and BindsTo? The 1-2-3 I've listed above?

Thanx in advance!

1
  • Have you had any progress on this issue?
    – ThreeFx
    Apr 4, 2019 at 21:37

3 Answers 3

2

I'm also looking for a more elegant solution. There is an open systemd ticket for Request for enhancement here: https://github.com/systemd/systemd/issues/5966

Currently my workaround is as follow: B.service

Requisite=A.service
After=A.service

A.service

OnFailure=Stop-B.service

Stop-B.service

[Service]
Type=oneshot
TimeoutSec=0
ExecStart=/bin/systemctl stop B.service
1

I had a similar situation to solve. The approach I came up with involve creating a target unit as barrier between the two services.

a.service:

[Unit]
Wants=a.target

a.target:

[Unit]
Requisite=a.service
After=a.service
PartOf=a.service
RefuseManualStart=true
RefuseManualStop=true

b.service:

[Unit]
BindsTo=a.target
After=a.target

This setup produces the behavior the OP desires, since a.target is always active with a.service (PartOf stops a.target when a.service is stopped, RefuseManual* avoid systemctl start/stop commands on a.target), and b.service cannot start a.service via a.target.

1
  • This didn't do exactly what I wanted, because the PartOf=a.service line in a.target does not cause a.target (and therefor b.service) to be stopped if a.service goes inactive (as opposed to being explicitly stopped). And using BindsTo=a.service instead of PartOf=a.service doesn't work because it overrides the Requisite=a.service line and starts a.service when b.service is started. May 24, 2020 at 12:48
0

I went with a similar approach to Chris's, but without needing to create a separate service for stopping B:

B.service:

[Unit]
Requisite=A.service
After=A.service

A.service:

[Service]
ExecStopPost=/bin/systemctl stop B.service

The Requisite line in B.service ensures that B.service cannot be started if A.service is not already running, while the ExecStopPost line in A.service ensures that B.service gets stopped when A.service is stopped or goes inactive.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .