systemd-timers are awesome

I got back to use rtorrent as my main Torrent client. rtorrent is lightweight and fast, also extremely powerful and customizable.

So I was setting up my rtorrent configuration another day, and I put the following line in my .rtorrent.rc:

# Loads unwanted peers from external text file into IPv4 Filter every day, e.g.:
# — (
# — (
schedule2 = load_ipv4_filter, 0, 24:05:00, “ipv4_filter.load=(cat,(session.path),antip2p.list),unwanted”
#schedule2 = load_ipv4_filter, 0, 24:05:00, “ipv4_filter.load=(cat,(session.path),Wael-Blacklist-v8.dat),unwanted”

Basically, I was setting a list of IPs to disallow sharing between them (I am not going to explain why I am doing this or if this approach is effective). schedule2 basically run some command (in this case, ipv4_filter.load), at the start of the program and at every 24:05AM.

This list lived in a server and needs to be updated frequently. I was updating it manually until this got on my nerves and I decided that I needed to automate this task.

My first idea was to use rtorrent own scheduler system, like the one in the example. However I had some issues with racing conditions: I couldn’t load the list at the start of the program anymore since the list may not even be there, so I needed to start later.

Thanks to those issues, I decided to depend on an external program. Good old cron was my first idea, however in Arch Linux it is not installed by default, so I decided to try systemd-timers instead. And I discovered that systemd-timers are really awesome, since it resolved this problem way better than old cronjobs could.

Lets start with how I resolved this problem. First we need to create systemd service file, something like this:

Description=Update rtorrent's IPv4 Blocklist
ExecStart=/bin/sh -c '/usr/bin/curl | gunzip > ~/.session/antip2p.list'

What this service does is run curl with the specific URL of the server hosting the IP list and unzipping this file in the right directory. We need to execute this inside a shell (the /bin/sh part) since we’re using shell features like pipes (|) and redirections (>).

We can put this file in $HOME/.config/systemd/user/ipv4-blocklist-updater.service and run with:

$ systemctl --user start ipv4-blocklist-updater.service

(And here is another amazing systemd feature: user services)

If everything is right we will have a file ~/.session/antip2p.list populated with tons of IPs. So it works! Now we need to run this service without user intervention, and to do this we will create a file named $HOME/.config/systemd/user/ipv4-blocklist-updater.timer (.timer file must have the same name as the .service one or this will not work):

Description=Update rtorrent’s IPv4 Blocklist daily

And we can activate our timer with:

$ systemctl --user start ipv4-blocklist-updater.timer
$ systemctl --user enable ipv4-blocklist-updater.timer

OnCalendar=daily will run this .service each day at midnight. This is something we could accomplish with the following crontab:

0 0 * * * /usr/bin/curl | gunzip > ~/.session/antip2p.list

So no surprises here. systemd-timers seems even worse than a crontab since it needs two files to work. However the advantage of systemd-timers is the parameter Persistent=true.

You see, my desktop computer is not turned on 24hs a day. Most of week it is not even turned on at midnight since I generally sleep earlier than that. In crontab case I would miss the window of running the command and I could pass multiple days without updating this file.

Persistent=true makes systemd store the time when the service was last triggered on disk. If the service should have been triggered when the system was inactive (like when it is shutdown), the service is run immediately.

This is only one of the use cases that makes systemd-timers more powerful than (classic) crontabs. Other features like timers with sub-second accuracy, relative timers to specific events like boot or timers that can wakeup a system from suspend makes systemd-timers a great alternative for other time based event systems.

Like what you read? Give Thiago Okada a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.