The need for an After=
or Before=
can finally be seen in examples from archlinux (a remarkable source of help as usual). Based on that link, there are two solutions to running a command on suspend and resume.
One method is to use two units, say mysyssuspend
and mysysresume
. The following examples just run the date
command to syslog so we can see when they get activated:
/etc/systemd/system/mysyssuspend.service
[Unit]
Before=suspend.target
[Service]
Type=simple
StandardOutput=syslog
ExecStart=/bin/date +'mysyssuspend start %%H:%%M:%%S'
[Install]
WantedBy=suspend.target
/etc/systemd/system/mysysresume.service
[Unit]
After=suspend.target
[Service]
Type=simple
StandardOutput=syslog
ExecStart=/bin/date +'mysysresume start %%H:%%M:%%S'
[Install]
WantedBy=suspend.target
As usual, do a systemctl daemon-reload
and systemctl enable mysyssuspend mysysresume
after creating the unit files.
The first unit has a Before
dependency on the suspend target and gets run when the computer enters suspend. The second unit similarly has an After
dependency, and gets run on resuming.
The other method puts all the commands in a single unit:
/etc/systemd/system/mysuspendresume.service
[Unit]
Before=sleep.target
StopWhenUnneeded=yes
[Service]
Type=oneshot
StandardOutput=syslog
RemainAfterExit=yes
ExecStart=/bin/date +'mysuspendresume start %%H:%%M:%%S'
ExecStop=/bin/date +'mysuspendresume stop %%H:%%M:%%S'
[Install]
WantedBy=sleep.target
This works with StopWhenUnneeded=yes
, so the service is stopped when no active service requires it. The sleep target also has StopWhenUnneeded
, so when it is finished it will run ExecStop
of our unit.
The RemainAfterExit
is needed so that our unit is still seen as active, even after ExecStart
has finished.
I tested both of these methods on Ubuntu 18.04.5 with systemd version 237 and they both seem to work correctly.
Rather than trying to merge your requirement into the above working mechanisms, it is probably more pragmatic to use one of them to stop/start an independent unit. For example, use the second method and add a mylongrun
service:
/etc/systemd/system/mysuspendresume.service
[Unit]
Before=sleep.target
StopWhenUnneeded=yes
[Service]
Type=oneshot
StandardOutput=syslog
RemainAfterExit=yes
ExecStart=-/bin/date +'my1 %%H:%%M:%%S' ; /bin/systemctl stop mylongrun ; /bin/date +'my2 %%H:%%M:%%S'
ExecStop=-/bin/date +'my3 %%H:%%M:%%S' ; /bin/systemctl start mylongrun ; /bin/date +'my4 %%H:%%M:%%S'
[Install]
WantedBy=sleep.target
/etc/systemd/system/mylongrun.service
[Unit]
Description=Long Run
[Service]
Type=simple
StandardOutput=syslog
ExecStart=/bin/bash -c 'date +"my11 %%H:%%M:%%S"; while sleep 2; do date +"my12 %%H:%%M:%%S"; done'
ExecStop=/bin/bash -c 'date +"my13 %%H:%%M:%%S"; sleep 10; date +"my14 %%H:%%M:%%S"'
[Install]
WantedBy=multi-user.target
Testing this by starting mylongrun
then closing the lid gives the following journalctl entries:
09:29:19 bash[3626]: my12 09:29:19
09:29:21 bash[3626]: my12 09:29:21
09:29:22 systemd-logind[803]: Lid closed.
09:29:22 systemd-logind[803]: Suspending...
09:29:22 date[3709]: my1 09:29:22
09:29:22 systemd[1]: Stopping Long Run...
09:29:22 bash[3715]: my13 09:29:22
09:29:23 bash[3626]: my12 09:29:23
09:29:25 bash[3626]: my12 09:29:25
09:29:27 bash[3626]: my12 09:29:27
09:29:29 bash[3626]: my12 09:29:29
09:29:31 bash[3626]: my12 09:29:31
09:29:32 bash[3715]: my14 09:29:32
09:29:32 systemd[1]: Stopped Long Run.
09:29:32 date[3729]: my2 09:29:32
09:29:32 systemd[1]: Reached target Sleep.
09:29:33 systemd[1]: Starting Suspend...
We can see the long running stop command (sleep 10
) completed correctly. On resume, the long run command is started again:
09:35:12 systemd[1]: Stopped target Sleep.
09:35:12 systemd[1]: mysuspendresume.service: Unit not needed anymore. Stopping.
09:35:12 systemd[1]: Reached target Suspend.
09:35:12 date[3813]: my3 09:35:12
09:35:12 systemd[1]: Started Long Run.
09:35:12 date[3817]: my4 09:35:12
09:35:12 bash[3816]: my11 09:35:12
09:35:14 bash[3816]: my12 09:35:14
09:35:16 bash[3816]: my12 09:35:16
09:35:18 bash[3816]: my12 09:35:18
Best Answer
The suggested solution is to run the service unit as a normal service - have a look at the
[Install]
section. So everything has to be thought reverse, dependencies too. Because the shutdown order is the reverse startup order. That's why the script has to be placed inExecStop=
.The following solution is working for me:
RemainAfterExit=true
is needed when you don't have anExecStart
action.After creating the file, make sure to
systemctl daemon-reload
andsystemctl enable yourservice --now
.I just got it from systemd IRC, credits are going to mezcalero.