LinkedIn Sourceforge

Vincent's Blog

Pleasure in the job puts perfection in the work (Aristote)

Locking the screen and running vdcron when the lid closes on Void Linux

Posted on 2026-11-08 12:25:00 from Vincent in VoidLinux vdcron

Give a shoutout to Ales Nesetril on unsplash.com My laptop runs Void Linux with the lightweight pekwm window manager, started by the LightDM display manager. I use it like most people use a laptop: I close the lid, put it in my bag, and open it again a few hours later. Two things bothered me about this cycle. First, the desktop came back exactly as I had left it, with no password asked. Second, I wanted my small scheduler, vdcron, to catch up with its pending tasks every time the machine wakes up. This post explains where these things happen on a Void system, and how a single shell script now takes care of both.


A cron for machines that sleep

Classic cron assumes the machine is always on. If a job is scheduled at 3 a.m. and the laptop is asleep in a bag at that time, the job is simply missed. vdcron is a small POSIX shell script I wrote to solve this. It does not run as a daemon. Each time it is invoked, it reads a config file, looks at which tasks are due or overdue, runs them, and saves the new state after every task. A task can be scheduled on an exact date (20261201), on a date and time (202612010800), "on or after" a date (+20261201), on a weekday (Monday/7), on a month (January/12), or on every invocation (Always).

Because vdcron only works when it is called, the important question is when to call it. On a laptop, the natural moment is right after a resume from suspend.

Finding where resume hooks live

On Void Linux there are two tools that can suspend the machine, and each one has its own hook directory (see also the power management page of the Void handbook):

  • zzz, Void's own small suspend script, runs the executables found in /etc/zzz.d/suspend/ before sleeping and in /etc/zzz.d/resume/ after waking. It calls them without any argument.
  • elogind, which handles the lid switch on my setup, runs the executables found in /usr/libexec/elogind/system-sleep/. It calls each one twice: once before sleeping with the arguments pre suspend, and once after waking with post suspend.

When you close the lid, it is elogind that suspends the laptop, so the elogind directory is the one that really matters. To keep everything in one place, I wrote a single script, /usr/local/bin/on-resume.sh, and linked it from both directories:

ln -s /usr/local/bin/on-resume.sh /etc/zzz.d/resume/99-on-resume
ln -s /usr/local/bin/on-resume.sh /usr/libexec/elogind/system-sleep/99-on-resume

The script checks its first argument to know at which moment it is called. Since zzz passes no argument, only the elogind calls trigger an action; the zzz link is harmless and simply ready if I ever suspend with zzz by hand.

Locking the screen: before sleeping, not after

The obvious idea is to lock the screen when the laptop wakes up. It works, but it has a flaw: for a short moment after resume, the desktop is visible before the lock appears. The better approach is to lock the screen before the machine goes to sleep, in the pre phase. When the lid opens again, the lock screen is already there.

The next question is which program should lock the screen. Many lockers (xscreensaver, xfce4-screensaver, light-lockerโ€ฆ) need a daemon running inside the user session, and my minimal pekwm session has none. But LightDM already provides everything needed: its command-line tool dm-tool, which comes with the lightdm package, can ask the display manager to show its greeter and lock the current session.

XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0 dm-tool lock

The XDG_SEAT_PATH variable is normally set inside a LightDM session. The sleep hook runs as root, outside the session, so I set it explicitly to tell dm-tool which seat to lock. The result is the LightDM login screen, and my password is required to get back to my desktop.

The final script

Here is the complete /usr/local/bin/on-resume.sh:

#!/bin/sh
export PATH=/usr/local/bin:$PATH
# Called two ways:
#  - by zzz:      no args, only invoked on resume
#  - by elogind:  $1=pre|post $2=suspend|hibernate|...
echo "$(date): user $(/usr/bin/id) resumed: $*" >> /tmp/resume-test.log
if [ "$1" = "pre" ] ; then
     # Lock the screen *before* sleeping, so nothing is visible on wake-up.
     # dm-tool asks LightDM to switch to its greeter; a password is then required.
     echo "    lock screen" >> /tmp/resume-test.log
     XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0 /usr/bin/dm-tool lock
fi
if [ "$1" = "post" ] ; then
     echo "    execute vdcron" >> /tmp/resume-test.log
     /usr/local/bin/vdcron /etc/zzz.d/resume
fi

The pre branch locks the screen. The post branch starts vdcron. The argument given to vdcron, /etc/zzz.d/resume, is just a context label: vdcron writes it in its log so that I can later see what triggered each run (a resume, a manual call, and so on).

Note that the hook runs as root. This means vdcron uses the system-wide files: /etc/vdcron.conf for the tasks, /var/log/vdcron.log for the log and /var/run/vdcron.lock for the lock. When run as a normal user, it uses ~/.vdcron/ instead.

Checking that it works

The echo lines write to /tmp/resume-test.log, which makes it easy to verify the whole cycle. After closing and reopening the lid, the log looks like this:

Sat Nov  7 22:31:40 CET 2026: user uid=0(root) gid=0(root) groups=0(root) resumed: pre suspend
    lock screen
Sun Nov  8 12:08:31 CET 2026: user uid=0(root) gid=0(root) groups=0(root) resumed: post suspend
    execute vdcron

The pre call locks the screen, the post call runs vdcron, and both happen as root, exactly as expected. Once everything is stable, these debug lines can be removed.

Conclusion

With one small shell script and two symbolic links, my laptop now locks itself every time I close the lid, and vdcron catches up with its overdue tasks every time I open it. No extra daemon is needed: elogind provides the hook, LightDM provides the lock screen, and vdcron does the scheduling. It is a good example of how far you can go on a minimal system by connecting the small tools that are already there.



๐Ÿ‘ 0, ๐Ÿ‘Ž 0
displayed: 19



What is the last letter of the word Moon?