20

When my debian jessie desktop box wakes up from sleep (via the new shiny systemd) my mouse settings are returned to their defaults, having reset my customisation

xinput set-prop 12 'Device Accel Constant Deceleration' 2.5

which runs when I log in.

how can I run an arbitrary user script on wakeup? (assume that the user is the owner of the X session)

As far as I can recall, the following is the only customisation I've made of the systemd setup (yes, I know it's completely wrong because it doesn't work for arbitrary users, but I've not worked out how to do that yet... this is somewhat related)

additionally, how can I run an arbitrary user script before wakeup, as the user who is currently using the X screen?

cat /etc/systemd/system/i3lock.service

#systemctl enable i3lock.service

[Unit]
Description=i3lock
Before=sleep.target

[Service]
User=fommil
Type=forking
Environment=DISPLAY=:0
ExecStart=/usr/bin/i3lock -c 000000

[Install]
WantedBy=sleep.target
1

5 Answers 5

15

This answer is based on askubuntu.com/a/661747/394818 (as also referred to in the comment by @sun-bear), askubuntu.com/q/616272/394818 and superuser.com/a/1269158/585953.

Using a system service:

Create the file /etc/systemd/system/my_user_script.service:

[Unit]
Description=Run my_user_script
After=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target


[Service]
ExecStart=/path/to/my_user_script
#User=my_user_name
#Environment=DISPLAY=:0

[Install]
WantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

Remove suspend/hibernate/hybrid in case the service should only be executed after waking up from a specific type of sleep. In case the service needs to be run by a specific user, uncomment the User= and Environment= lines and replace the relevant user name.

Install the service file with:

sudo systemctl enable my_user_script

Using a user service will not work:

In order to avoid setting a hard coded user name with User=, one could create the exact same service file at ~/.config/systemd/user/my_user_script.service and activate with

systemctl --user enable my_user_script

However, that will not work. @grawity explains in more detail at unix.stackexchange.com/a/174837/163108 why that is:

sleep.target is specific to system services. The reason is, sleep.target is not a magic target that automatically gets activated when going to sleep. It's just a regular target that puts the system to sleep – so the 'user' instances of course won't have an equivalent. (And unfortunately the 'user' instances currently have no way to depend on systemwide services.)

6
  • 1
    Note that according to wiki.archlinux.org/index.php/Systemd/User#How_it_works user scripts belong in ~/.config/systemd/user/ and ~/.local/share/systemd/user/ should be used for units of packages that have been installed in the home directory. Dec 21, 2020 at 9:35
  • Do you have any suggestion about how to automatically map User and Environment to correct settings when system is used by multiple users (that is, Switch Account feature is used and multiple X sessions are running in different virtual terminals)? The value of DISPLAY and XAUTHORITY environment variables would be need to be set correctly depending on who has logged into which virtual terminal. Jun 4, 2021 at 8:04
  • @MikkoRantalainen I think I may have an answer. However, we shouldn't do Q&A in comments. Please post your question and I'll attempt to answer. Sep 3, 2022 at 10:09
  • @PhilHudson: good point – I created new question here: unix.stackexchange.com/q/716204/20336 Sep 5, 2022 at 12:30
  • For actually starting an user unit checkout my answer below.
    – Fabian T.
    Oct 23, 2023 at 8:37
11

I created a file in /lib/systemd/system-sleep/ (you can call it e.g. wakeup):

#!/bin/sh
case $1 in
  post)
    /bin/runme
  ;;
esac

Then I set the executable permission bits.

# chmod +x /usr/lib/systemd/system-sleep/wakeup

The post is called on wakeup.

For further reading, look here.

5
  • 1
    This didn't work for me. Can you link to some documentation explaining how it's supposed to work?
    – Ryan Lue
    Jul 22, 2018 at 9:34
  • 1
    This doesn't work for me either. The context this script runs in is extremely limited, and does not run as the user who was active when the system went to sleep. So it does not get access to the display (which excludes most desktop utilities here), info/variables for the user who is logged in, or anything else related to the user's session who is currently logged in. I need a script to run as the user who is logged in inside the context of the desktop session. I still haven't found anything to do this but I will update here if I do. Dec 20, 2018 at 23:10
  • @theferrit32 you should be able change to your preferred user with the 'su' command from within your script.
    – borizzzzz
    Jun 10, 2019 at 5:43
  • 2
    If this didn't work for you, it's probably because you did not make the script executable.
    – smac89
    Oct 7, 2020 at 1:52
  • 1
    If you want to run something as your user and have it affect the active X session you need to do something pretty roundabout, e.g., sudo -u [username] env DISPLAY=:0 /usr/bin/runme (provided root can run sudo without a password); if it requires D-Bus you might need to set the DBUS_SESSION_BUS_ADDRESS variable as well, which can be a pain.
    – frabjous
    Dec 14, 2022 at 13:30
1

You can also create a systemd unit file:

# /etc/sysstemd/system/awake.service
# ExecStart is executed on wake up from 'sleep' (suspend or hibernate).
[Unit]
WantedBy=sleep.target
After=systemd-suspend.service systemd-hybrid-sleep.service systemd-hibernate.service

[Service]
Type=simple
ExecStart=/bin/bash -c 'echo awake from sleep' >> /tmp/awake.notes'

Often this will be used to restart a systemd service.

ExecStart=/bin/systemctl restart fancontrol
1

Open this file:

sudo vim /lib/systemd/system-sleep/hdparm

Contents:

#!/bin/sh

case $1 in   
post)
    /usr/lib/pm-utils/power.d/95hdparm-apm resume
    ## Paste your command to run your script
    ;; 
esac

Your command will execute with admin privileges.

1
  • Ludicrously, I can't make this edit because "it's too small": There should be a newline after ;; and esac should be on its own line. Aug 26, 2022 at 8:53
1

All previous solutions do not provide a way to actually start a user unit after wakeup. You may want to use a user unit since you imported your environment variables there (e.g. with dbus-update-activation-environment --all --systemd).

I could only find an ugly solution to this problem which involves creating a system service that starts the user service:

Create the system service /etc/systemd/system/[email protected]:

[Unit]
Description=Run user unit of %i after wakeup.
After=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

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

[Install]
WantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

Inspired by https://unix.stackexchange.com/a/713646/454143.

And the user unit ~/.config/systemd/user/afterwakeup.service:

[Unit]
Description=Runs after wakeup from suspend etc.

[Service]
ExecStart=<do anything you want here, dont forget to import your environment>
Type=oneshot

Now run systemctl enable afterwakeup@<youruser>.service and don't forget to import your user environment, e.g. in .xinitrc (dbus-update-activation-environment --all --systemd).

You must log in to answer this question.

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