9

From systemd.unit(5), the option Requires= means:

Configures requirement dependencies on other units. If this unit gets activated, the units listed here will be activated as well. If one of the other units gets deactivated or its activation fails, this unit will be deactivated.

I did a little experiment on that. I created 2 services: a.service and b.service:

# cat a.service
[Service]
ExecStart=/bin/false

# cat b.service
[Unit]
Requires=a.service

[Service]
ExecStart=/bin/sleep 1000

After I do

systemctl start b.service

I expected to see both a.service and b.service fail because a.service fails with /bin/false and b.service fails with a.service failure.

However, b.service is running:

root@john-ubuntu:/etc/systemd/system# systemctl status a.service b.service
● a.service
   Loaded: loaded (/etc/systemd/system/a.service; static; vendor preset: enabled)
   Active: failed (Result: exit-code) since Thu 2017-09-07 16:38:39 CST; 2s ago
  Process: 1245 ExecStart=/bin/false (code=exited, status=1/FAILURE)
 Main PID: 1245 (code=exited, status=1/FAILURE)

Sep 07 16:38:39 john-ubuntu systemd[1]: Started a.service.
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Main process exited, code=exited, status=1/FAILURE
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Unit entered failed state.
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Failed with result 'exit-code'.

● b.service
   Loaded: loaded (/etc/systemd/system/b.service; static; vendor preset: enabled)
   Active: active (running) since Thu 2017-09-07 16:38:39 CST; 2s ago
 Main PID: 1244 (sleep)
    Tasks: 1
   Memory: 88.0K
      CPU: 696us
   CGroup: /system.slice/b.service
           └─1244 /bin/sleep 1000

Sep 07 16:38:39 john-ubuntu systemd[1]: Started b.service.

Did I miss something? Thanks.

2 Answers 2

3

This is tricky. The main thing to understand is that systemd starts everything in parallel, unless told not to. This is actually stated in the man page in the "Requires" section:

requirement dependencies do not influence the order in which services are started or stopped

What you want to achieve is "wait for a.service to be started before starting b.service". For this you need both the "Requires" and "After" options in your b.service file:

[Unit]
Requires=a.service
After=a.service

[Service]
ExecStart=/bin/sleep 1000

= UPDATE =

OK, I see what's wrong: in your a.service file you put the ExecStart command and didn't specify the Type. This means that the Type will default to 'Simple'. You need the type 'Forking' for this to work. From systemd.service man page:

If set to simple (the default if neither Type= nor BusName=, but ExecStart= are specified), it is expected that the process configured with ExecStart= is the main process of the service. In this mode, if the process offers functionality to other processes on the system, its communication channels should be installed before the daemon is started up (e.g. sockets set up by systemd, via socket activation), as systemd will immediately proceed starting follow-up units.

If set to forking, it is expected that the process configured with ExecStart= will call fork() as part of its start-up. The parent process is expected to exit when start-up is complete and all communication channels are set up. The child continues to run as the main daemon process. This is the behavior of traditional UNIX daemons. If this setting is used, it is recommended to also use the PIDFile= option, so that systemd can identify the main process of the daemon. systemd will proceed with starting follow-up units as soon as the parent process exits.

Therefore, you should update your a.service file to include 'Type=Forking':

[Service]
Type=forking
ExecStart=/bin/false

This will work. :)

4
  • Your suggestion doesn't work on my systemd. b.service is still active and running when a.service failed. Did you try it on your systemd?
    – johnlinp
    Commented Sep 8, 2017 at 5:54
  • @林自均 is your comment here ^^ still relevant, or does this answer now work for you? I'm trying to understand why you've accepted it but you say it doesn't work. Thanks Commented Jul 25, 2018 at 7:21
  • Since false does not fork, it would be easier to understand as a Type=oneshot service IMO. That's also useful, if you write a test service with /bin/true instead, you could make it RemainAfterExit=yes if you didn't want the service to be considered as deactivated immediately after starting.
    – sourcejedi
    Commented Jul 25, 2018 at 7:37
  • I reported this problem and the maintainer said that it is a documentation issue. I updated the documentation by the way.
    – johnlinp
    Commented Aug 17, 2018 at 3:50
1

If you are using CentOS 7 with systemd-219 (which has a systemd.unit(5) man page matching the section quoted in the question), this would appear to be partly due to a documentation error. Perhaps the same applies to some other distributions and systemd versions.

This sentence quoted in the comment:

If one of the other units gets deactivated or its activation fails, this unit will be deactivated.

would suggest to me that systemctl start b.service would cause both services to be activated, but then after a.service failed on return from /bin/false, b.service would be automatically deactivated. Just as the question states that this was not the observed behavior, I have not observed that behavior on CentOS 7.

The quoted sentence was replaced with these sentences at https://www.freedesktop.org/software/systemd/man/systemd.unit.html:

If one of the other units fails to activate, and an ordering dependency After= on the failing unit is set, this unit will not be started. Besides, with or without specifying After=, this unit will be stopped if one of the other units is explicitly stopped.

The updated documentation then agrees with @gerard's statement that you need an After= setting in b.service, and matches the behavior I have observed on CentOS 7.

Then, as @gerard says, when systemd starts a Type=simple service, it "will immediately proceed starting follow-up units". Note that Type=forking isn't the only setting that can be used to address this, you could also set Type=notify or one of the other types described in the systemd.service(5) man page (other than Type=idle). When you are past the stage of testing failure handling, ensure that the service actually behaves as required given its Type, e.g. it calls fork(), sd_notify(), etc.

Also be aware that there are a lot of edge cases in failure handling in systemd-219, e.g. https://github.com/systemd/systemd/issues/8398

1

You must log in to answer this question.

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