How to systemd targets to stop services from other targets

systemd

I would like to have several systemd targets, call them TargetA, TargetB, and TargetC, that control a set of services, call them Service01 through Service7. The configuration would look something like this:

  • TargetA
    • Service1
    • Service2
    • Service3
  • TargetB wants
    • Service3
    • Service4
    • Service5
  • TargetC wants
    • Service5
    • Service6
    • Service7

Goals:

  • Only one target should be allowed to be enabled at a time
  • The system administrator should be able to manually start and services at will

My initial setup involved:

  • Each target conflicted with the other two targets
  • Every service had StopWhenUnneeded=true enabled

This met the first requirement. Starting TargetX stopped the other two targets. Unfortunately, this setup restricted the sysadmin. If the sysadmin ran sudo systemctl start ServiceX the service would launch and then immediately die because it wasn't needed.

My second setup involved:

  • Every target conflicted with the other two targets
  • Every target also conflicted with the services that it didn't need
  • No service has StopWhenUnneeded=true

For example:

  • TargetA
    • Wants
      • Service1
      • Service2
      • Service3
    • Conflicts
      • Service4
      • Service5
      • Service6
      • Service7
      • TargetB
      • TargetC

This doesn't have the desired effect.
If I run:

sudo systemctl start TargetA.target

followed by:

sudo systemctl start TargetB.target

then TargetA.target is stopped (yay!) but Service1 and Service2 are still running.

What am I not getting? How can I get the systemd behavior that I'm looking for?

Best Answer

Suggested approach:

  1. Use systemctl isolate to start each of the 3 targets. As documented in man systemctl, this is a powerful command: "The isolate command will immediately stop processes that are not enabled in the new unit, possibly including the graphical environment or terminal you are currently using." Structure your target dependencies carefully! Possibly include multi-user.target as dependency.
  2. Make sure that that the first service the target starts is a special one that checks that the other targets are not active. This defends against an admin accidentally "starting" one of your custom targets instead of "isolating" it. You use /bin/systemctl is-active your.target to quickly check if it returns "active" or not.