42

I was reading doc and it is still unclear for me, whether the following is possible to accomplish:

service defined in ~/.config/systemd/user/task.service that depends on system sleep.target (~/.config/systemd/user/sleep.target.wants/task.service).

Now I expect task.service to start when I run $ systemctl suspend, however task.service is not started.

I'm running debian, with systemd version 208, systemd --user configured more or less as described on the ArchWiki.

I wonder whether my scenario could be implemented with systemd at all, or are --system and --user completely isolated by design so that --user unit may not be a dependency of a --system unit.

In case it is possible, what might be the problem in my case?

1

5 Answers 5

30

From systemd/User - Archwiki

systemd --user runs as a separate process from the systemd --system process. User units can not reference or depend on system units.

There is a feature request in the systemd GitHub repository tracking this question.

3
  • 24
    This sucks. That's too bad.
    – Rolf
    Commented Mar 29, 2018 at 12:49
  • 5
    I was just bitten by this. Thing is, systemctl --user daemon-reload doesn't report that there is a useless dependency in the user service file. Commented Sep 17, 2021 at 14:51
  • As per the other answer (currently with fewer votes), the other way works, i.e. the --system processes can reference the --user services, so you can hack around this limitation.
    – Sparhawk
    Commented Jun 26 at 11:30
9

You can also create a "proxy" for all your sleep/suspend targets between service and user instances.

Create this file on /etc/systemd/system/[email protected]:

[Unit]
Description=Call user's suspend target after system suspend
After=suspend.target

[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl --user --machine=%i@ start --wait suspend.target

[Install]
WantedBy=suspend.target

And enable it by running sudo systemctl daemon-reload && sudo systemctl enable suspend@youruser.

Now create your user suspend.target at ~/.config/systemd/user/suspend.target:

[Unit]
Description=User level suspend target
StopWhenUnneeded=yes
Wants=my-etc-service.service

Then add your services on the Wants list and make sure to run systemctl --user daemon-reload

2
  • Thanks ! I made it work with graphical.target flawlessly. Just don't forget to also enable your user service with systemctl --user enable my-etc-service.service of course ;)
    – Dynamite
    Commented May 20, 2023 at 13:02
  • @Dynamite I didn't have to enable my-etc-service.service, and it still worked. However, I reckon it makes more sense to not list the services you want to run in ~/.config/systemd/user/suspend.target's Wants=, but instead put [Install] <newline> WantedBy=suspend.target into my-etc-service.service. This has the added advantage of putting all the dependency information in each new service file itself. In this case you would need to enable my-etc-service.service though.
    – Sparhawk
    Commented Jun 26 at 11:57
5

systemd user session services run in a completely separate instance of systemd, and doesn’t have any way to depend on system services directly.

There are other ways to accomplish what you want though. The cleanest would probably be to make whatever you want to run when the system is going to sleep hook into logind’s inhibitors and then run it as a background daemon.

A more general solution would be to have a daemon hooks into the logind inhibitors, (see systemd-lock-handler and xss-lock,) and then when the system is going to sleep it will activate a user session target that you can order your services under.

0
4

Adding to the response from @kyrias, here's a way to create your own user level sleep.target:

~/.local/share/systemd/user/sleep.target

[Unit]
Description=User level sleep target
StopWhenUnneeded=yes

~/.local/bin/watch_sleep

#!/bin/bash

dbus-monitor --system "type='signal',interface='org.freedesktop.login1.Manager',member=PrepareForSleep" | while read x; do
    case "$x" in
        *"boolean false"*) systemctl --user --no-block stop sleep.target;;
        *"boolean true"*) systemctl --user --no-block start sleep.target;;
    esac
done

~/.local/share/systemd/user/watch_sleep.service

[Unit]
Description=watch for sleep signal to start sleep.target

[Service]
ExecStart=%h/.local/bin/watch_sleep
Restart=on-failure

[Install]
WantedBy=default.target

See my blog post https://medium.com/@aiguofer/systemd-sleep-target-for-user-level-10eb003b3bfd

0

You can do the following to achieve sleep.target (and a complementary awake.target) in userspace:

Create the following service template in /etc/systemd/system/[email protected]

[Unit]
Description=%I sleep hook
PartOf=sleep.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/systemctl --user --machine=%i@ start sleep.target
ExecStop=/usr/bin/systemctl --user --machine=%i@ start awake.target

[Install]
WantedBy=sleep.target

Create an instance of it for your user (don't start it):

sudo systemctl enable sleep@$(whoami)

Now for the equivalent sleep.target for users:

[Unit]
Description=User-level Sleep Target
Conflicts=awake.target

And awake.target:

[Unit]
Description=User-level Awake Target
Conflicts=sleep.target

[Install]
WantedBy=default.target

Enable with systemctl --user enable awake.target so that it starts at login

When either of these targets are reached, they stop the other target, thanks to the Conflicts directive, which makes them mutually exclusive targets.

For my use-case, I actually found the awake.target to be more useful than the sleep.target because I could make some user services use the PartOf directive to start and stop with it.

The only use-case I could think of that would benefit from the sleep target is if someone was trying to make use systemd-inhibit to do some last minute cleanup before the system goes to sleep. This would also mean delaying the actual sleep target until after the work is done.

You must log in to answer this question.

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