Systemd user unit that depends on system unit (sleep.target)

六月 15, 2024 [C/C++] #Linux #obsidian #编译链接

Systemd user unit that depends on system unit (sleep.target)

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/sleep@.service

[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.

may deal with

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

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

Create this file on /etc/systemd/system/suspend@.service:

[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

or using this solution

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