Preventing systemd start conditionally based on configuration

systemd

I've been struggling with this for the last hours, and as far as I understand the documents, there isn't nearly any straight way around it.
I have a configuration / state called IS_PAIRED, which is stored inside some .conf file. If this configuration is set to false, I want to prevent the service from starting, as well as running it's preliminary steps.

Further context: The main service is a long running service. It is part of a bigger overall application. This service needs to run only after the main application has been paired with a different application. The IS_PAIRED configuration is controlled by another service in the same application. It's impossible to say when it will be set – it is the user's choice. All other services in the application can only be started after IS_PAIRED is set. Otherwise, there is no point in starting them. The IS_PAIRED param is persisted. After it is set, the service needs to load on boot.

I have started with this:

[Service]
Restart=on-failure
ExecStartPre=/bin/grep -q '^IS_PAIRED=true$' /etc/p.conf
ExecStartPre=/bin/sh -c "echo 'aaa' > /vagrant/b.sh"
ExecStart=/vagrant/a.sh
ExecStopPost=/vagrant/utils/bin/delay_service_restart.sh test

[Install]
WantedBy=multi-user.target

What happens when IS_PAIRED isn't true, is that the systemd fails, runs the PostStop, and then keeps on restarting. I don't want this to happen. If this configuration isn't set, the service should just go inactive.
I don't want to switch the Restart value to on-abort, because sometimes the long-running process (redubbed as a.sh here) will catch an exception and close with an error rather than be killed by a signal.

Next, I tried this (the service is named test):

[Service]
Restart=on-failure
ExecStartPre=/bin/bash -c "grep -q '^IS_PAIRED=true$' /etc/p.conf || systemctl stop test"
ExecStartPre=/bin/sh -c "echo 'aaa' > /vagrant/b.sh"
ExecStart=/vagrant/a.sh
ExecStopPost=/vagrant/utils/bin/delay_service_restart.sh test

[Install]
WantedBy=multi-user.target

What happens now is that the second ExecStartPre is executed before the service is stopped. This is not good for me, as in the real code there are initializations there I do not want to permit with the configuration not set.

On my last attempt, it really gets weird:

[Service]
Restart=on-failure
ExecStartPre=/bin/bash -c "grep -q '^IS_PAIRED=true$' /etc/p.conf || { systemctl stop test; /bin/false; }"
ExecStartPre=/bin/sh -c "echo 'aaa' > /vagrant/b.sh"
ExecStart=/vagrant/a.sh
ExecStopPost=/vagrant/utils/bin/delay_service_restart.sh test

[Install]
WantedBy=multi-user.target

I thought I'm being tricky here, but no, the second ExecStartPre still ran.
I thought I'm going crazy, so I verified that the first one does evaluate to an error:

vagrant@ubuntu-bionic:~$ sudo /bin/bash -c "grep -q '^IS_PAIRED=true$' /etc/p.conf || { systemctl stop test; /bin/false; }"
vagrant@ubuntu-bionic:~$ sudo echo $?
1

So overall, I'm pretty lost, and I have two questions:

  1. How am I supposed to achieve the logic I want? I can see two
    possible solutions I don't like:

    • Making the configuration check a service. This is wrong. There is nothing service-like about it.
    • Creating a wrapper runner where the check is the first thing that happens. Well, I am currently migrating a logically organized upstart into this systemd. In the upstart this check is in the pre-start, and rightly so. It belongs this logically. Otherwise I have to start creating separate wrappers for each type of service – some of them are in java, some of them in C++, some have additional pre-start actions, and all have this precondition.
    • Having the software that sets IS_PAIRED in p.conf create a file, and then use the ConditionPathExists directive. This is the only one I am willing to consider, but it really is quite horrible.
  2. In case there is no right way – why isn't my last hack working? 🙁

Best Answer

Maybe ExecCondition= can help?

[Service]
ExecCondition=/usr/bin/myapp-exec-condition

The application will only start if myapp-exec-condition exits with status 0.