5

(On Stock Debian testing aka stretch aka 9, so I have a regular systemd+logind+NetworkManager+GNOME stack)

I have a pair of script that I want to run on startup/shutdown and resume/suspend. This script requires networking to be present when it runs. I have attempt this with the following script:

[Unit]
Description=Yamaha Reciever power
Requires=network-online.target
After=network-online.target
Before=sleep.target
Conflicts=sleep.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/av-up
ExecStop=/usr/local/bin/av-down
RemainAfterExit=yes

[Install]
WantedBy=graphical.target

This works correctly on startup/shutdown, however during suspend it runs after the network has shutdown and therefore fails.

I have determined that the reason for this is how suspend proceeds under systemd:

  1. Suspend is initiated by a program (e.g. systemctl suspend) sending a dbus call to logind.
  2. Logind then sends a PrepareToShutdown dbus signal to anyone listening.
  3. Logind then sends a StartUnit dbus call to systemd to run the suspend.target unit.

NetworkManager listens to PrepareToShutdown, so removes the network at (2), while my unit is triggered when systemd actually starts suspending at (3). NetworkManager keeps an "inhibit" lock with logind to ensure it shuts the network down before (3). (Side note: it seems crazy to have something like systemd control ordering of suspend/resume, only to subvert it with logind making stuff circumvent this)

What is the right way to trigger a program to run on suspend/resume while networking is still running?

Should I use NetworkManager pre-down scripts? If so how do I stop it triggering if the network goes down but I'm not suspending?

Is there a way to hook into the suspend process earlier?

Is there a way to make NetworkManager keep the network up longer?

NB: this is distinct from How to write a Systemd unit that will fire before networking goes down as I am talking about suspend/resume.

  • So many layers of inhibitions, redirections and signalling yet there is no way to make user-defined scripts run before everything else..... – Michael Yoo May 3 '17 at 16:37
2

Acccording to the systemd-suspend documentation, as well as the systemctl man page systemctl suspend activates the suspend.target.

systemctl list-dependencies suspend.target --after --all shows that suspend.target calls systemctl-suspend.service then sleep.target. This means when you call systemctl suspend the default order of operations are:

suspend.target
|-systemd-suspend.service
  |-sleep.target

If you placed Before=sleep.target, then your order of operations is likely:

suspend.target
|-systemd-suspend.service
  |-[custom service]
    |-sleep.target

So you're service runs after systemd-suspend.service does its thing, which is likely your issue.


You can add to your service file to get the correct results:

Before=systemd-suspend.service

After calling systemctl daemon-reload you should be able to use systemctl list-dependencies suspend.target --after --all to see your service appear between suspend.target and systemd-suspend.service. Your final order of operations should be:

suspend.target
|-[custom service]
  |-systemd-suspend.service
    |-sleep.target
  • I tried this but no dice. I tried with "Before=sleep.service systemd-suspend.service" and got the same behaviour (my script runs before sleep, but after network is down.). With just "Before=systemd-suspend.service" the script isn't called at all during suspend. – David Jan 27 '17 at 6:50
  • When you changed your Before what did you get when running systemctl daemon-reload ; systemctl list-dependencies suspend.target --after --all? – Centimane Jan 27 '17 at 19:42
  • I have copied the first 100 lines of output to pastebin.com/ZsdP9Evs. In all 3 cases it seems to print infinitely. That's probably a bad sign. I have saved the log from a suspend-resume cycle with Before=sleep.target at pastebin.com/ziYdyKtr. You can see NetworkManager disconnects way before Systemd attempts to suspend, which is why I looked to see what triggers NM, which led me to my above conclusion that NM kicks in independently of systemd. – David Jan 29 '17 at 20:55
  • @David Did you use --after or --before? Looks like --before (which is the wrong direction). Also, if you're getting an infinite printout, that suggests you've created a cyclical dependency (systemd will still start/stop services successfully because it wont try to start a service that's already started, but still worth looking into). I'm not sure enough of your output is pasted for me to find the service in question, but I would recommend removing any before/after/conflicts in custom services that you're unsure of. – Centimane Jan 31 '17 at 4:04
  • It was definitely after. I reran: pastebin.com/AYzhcgKu. Here is before: pastebin.com/mF60w1wJ. I have basically given up on getting systemd to do it. I am removing all reference to sleep, so at least the script correctly runs for boot/shutdown without getting into state failed if I suspend in between. – David Feb 4 '17 at 5:21
2

Inspired by https://unix.stackexchange.com/a/139664/160746 I upgraded my start/stop scripts to a full blown daemon and made it listen for PrepareToShutdown signal. This is a race against NetworkManager every start/stop, but it seems to work reliably on my system.

I have uploaded my code and systemd unit at https://github.com/davidn/av.

  • Thanks for your code! Unfortunately, on my system (using systemd-networkd) I get a urlopen error [Errno 101] Network is unreachable from the script when I resume. So this is still missing a way to make sure that the network is back. :( – josch Aug 1 '18 at 13:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.