While using Void Linux on my laptop, I wanted some way to trigger scripts as my user when the machine went to sleep and when it resumed. The main goal of this was to be able to lock my machine using i3lock on suspend.
I currently use xss-lock to register
i3lock to be called whenever
the screensaver is activated with this command (run at session start):
xss-lock -- ~/bin/i3lock-retry -enfi ~/Pictures/lock.png
xss-lock running, I set my machine to lock automatically after 600
seconds (10 minutes) of inactivity with:
xset s 600
And locking my machine can be done manually by activating the screensaver with:
xset s activate
Note: i3lock-retry is a simple wrapper-script that calls
i3lock continuously until it exits successfully (i.e. when the correct
password has been entered). I've had some issues with
i3lock crashing in the
past and I just have this wrapper in place because of paranoia over ensuring
my system locks correctly.
Lid Close Event
Initially, to get the machine to lock automatically when I suspended it, I
had naively modified the
acpi handler for the
button/lid/close event (when
the laptop lid is closed). I originally had code like this:
button/lid) case "$3" in close) # suspend-to-ram logger "LID closed, suspending..." xset s activate sleep 1 zzz ;;
The handler was set up, by default with Void, to call
zzz, I just manually
added the lines to activate the screensaver and sleep for a second before
zzz. This "worked" for the most part, but there were some issues.
The most obvious issue (that I didn't think of at first, of course) was that
this didn't lock the machine when it was slept, but instead locked the machine
when the lid was closed. This means that manually invoking sleep by running the
zzz command - or just invoking sleep by any other means than closing the
lid - would leave the system unlocked.
To remedy this problem, I looked more closely into the
zzz command that came
with Void. By reading the zzz(8) manual, I found that the
had the ability to invoke hooks on suspend and resume!
zzz User Hooks
zzz will call any and all executables that live in
/etc/zzz.d/resume when the machine suspends and resumes, respectively. Using
this knowledge, I wrote a generic script that will:
- Figure out the user currently logged in with an active X session
- Determine if they have a user hook installed (script in
- Call the user script with the correct permissions (using
The code is on GitHub:
The code was written specifically for Void Linux but should work on any OS that
has or uses the
zzz command to suspend.
Once the code is checked out,
make can be used to install the hooks:
$ sudo make install cp user-script /etc/zzz.d cp hooks/resume/99-user-script /etc/zzz.d/resume cp hooks/suspend/99-user-script /etc/zzz.d/suspend
This will result in the following layout being created:
$ tree /etc/zzz.d/ /etc/zzz.d/ ├── resume │ └── 99-user-script ├── suspend │ └── 99-user-script └── user-script 2 directories, 3 files
With these scripts in place, the following scripts will be run for the currently logged in user:
~/.onsuspend- called before the machine is suspended
~/.onresume- called when the machine wakes up
The above scripts will be called with the permissions of the user for whom they
are being called (using
sudo -Hu <user>). The
variable will be set to the currently active display. The
command will be used to ensure the user scripts don't take any longer than 5
seconds so they cannot hang the system indefinitely.
I use the following scripts:
#!/usr/bin/env bash # activate the screensaver (lock) xset s activate sleep 1
#!/usr/bin/env bash # turn on the keyboard backlight ~/bin/keyboard dim # ensure the output is set to the laptop display ~/bin/monitor laptop
monitor are scripts in my dotfiles.
User scripts being called when using the
zzz command directly:
$ sudo zzz Zzzz... [user-script] called Sun Sep 23 11:33:59 EDT 2018 [user-script] running /home/dave/.onsuspend for user dave (DISPLAY=:0) [user-script] ran /home/dave/.onsuspend for user dave, exited 0 [user-script] called Sun Sep 23 11:34:05 EDT 2018 [user-script] running /home/dave/.onresume for user dave (DISPLAY=:0) [user-script] ran /home/dave/.onresume for user dave, exited 0 yawn.
Note that any output generated by the user scripts will show up in the same
location as the
User scripts being called when using
acpid to trigger
zzz when the laptop
lid is closed:
$ less /tmp/sv/log/acpid/current 2018-09-27_06:04:11.08679 rule from /etc/acpi/events/anything matched 2018-09-27_06:04:11.08723 executing action "/etc/acpi/handler.sh button/lid LID close" 2018-09-27_06:04:11.13167 Zzzz... [user-script] called Thu Sep 27 06:04:11 UTC 2018 2018-09-27_06:04:11.14506 [user-script] running /home/dave/.onsuspend for user dave (DISPLAY=:0) 2018-09-27_06:04:12.16758 [user-script] ran /home/dave/.onsuspend for user dave, exited 0 2018-09-28_00:57:20.98021 [user-script] called Fri Sep 28 00:57:20 UTC 2018 2018-09-28_00:57:20.98195 [user-script] running /home/dave/.onresume for user dave (DISPLAY=:0) 2018-09-28_00:57:20.99676 [user-script] ran /home/dave/.onresume for user dave, exited 0 2018-09-28_00:57:20.99684 yawn. 2018-09-28_00:57:20.99712 action exited with status 0 2018-09-28_00:57:20.99713 3 total rules matched 2018-09-28_00:57:20.99713 completed input layer event "button/lid LID close"