fetchmail, IMAP IDLE & systemd generators

TL;DR;

If you want to spin up a fetchmail process per ​/etc/fetchmailrc.d/*.config entry, build and install fetchmail-conf-d package.

The problem

The problem which I wanted to solve was fetching my email from multiple servers with relatively low latency. You might ask why fetch your email in the first place, but that’s out of scope of this post – I just want to use it as an excuse for expressing my admiration for systemd anyway. For years the go-to solution for fetching email was fetchmail.

Design decisions

fetchmail allows you to list multiple accounts and run as a daemon to scrape them every once in a while. This polling-style solution is not a pretty one. Fortunately, IMAP has a feature called IDLE, which basically makes an email client keep a connection open and the server notify clients as soon as it gets a new message. fetchmail supports it, however, only for one account. The obvious solution is to spin up a separate fetchmail for every account and this is what I decided to use systemd generators for.

Overview

systemd generators are basically templated services. They comprise a unit file template and its instantiations. The template is specified just like any other systemd unit file, except with a ‘@’ suffix. Instantiations are generated by a script or program, which you provide.

We’ll just put per-account configuration in /etc/fetchmailrc.d/some_config.config and the script will instantiate the unit file with some_config or something analogous for every file in /etc/fetchmailrc.d  with the suffix .config.

Service template

In ubuntu 18.04 fetchmail comes with an old school init.d script, so I had to start from scratch. This is what I came up with:

[Unit]
Description=Fetchmail for %i
After=network-online.target

[Service]
User=root
ExecStart=/usr/bin/fetchmail \
    -d180 \
    -l104857699 \
    -f /etc/fetchmailrc.d/%i.config \
    -N \
    --syslog \
    --sslcertck \
    --pidfile /var/run/fetchmail/%i.pid
Restart=always
RuntimeDirectory=fetchmail
RuntimeDirectoryMode=0750

[Install]
WantedBy=multi-user.target

Notice the use of %i. This is the instantiation – it will be substituted by whatever our script generates. Based on this, /etc/fetchmailrc.d/%i.config will be used for configuration and /var/run/fetchmail/%i.pid will be used to store the PID. Other than this, it’s a standard unit file.

Putting this file in a proper place is already enough to run systemctl start fetchmail@myconfig and it will create a service which will try to use /etc/fetchmailrc.d/myconfig.config.

Instantiating the template

Our script has to make sure thatmulti-user.target depends on fetchmail@something for every file we have in /etc/fetchmailrc.d. That way the fetchmails will be started at boot time. The systemd generator infrastructure allows you to write scripts for exactly that. Here is what I came up with:

#!/bin/bash
set -e

normal=${1?}
early=${2?}
late=${3?}

log() {
  echo "$@" > /dev/stderr
}

wantdir="$normal/multi-user.target.wants"
mkdir -p "$wantdir"

for config in /etc/fetchmailrc.d/*.config ; do
  [ -f "$config" ] || continue
  basename="$(basename "$config")"
  conf_name="${basename%.config}"
  ln -s "/lib/systemd/system/fetchmail@.service" \
      "${wantdir}/fetchmail@${conf_name}.service"
done

The script basically creates symlinks of ​fetchmail@.service unit file to a directory subdirectory of a directory provided by an argument. That’s how you fit in systemd’s generator infrastructure.

Putting it all together

All you have to do is put the unit file and the script to where they live. I went for /lib/systemd/system/fetchmail@.service for the unit file because I have put it in a debian package. If you want to just copy the file then /etc/systemd/user/ might be a better choice, but remember to update the generator script to resemble that.

Similarly for the generator script: I have put it in /lib/systemd/system-generators/system-fetchmail-generator but you mind find it more convenient to put it in /etc/systemd/user-generators/ directory.

In order to regenerate the symlinks just run systemctl daemon-reload.

You can even run systemctl status fetchmail@* to see the status of all the running instances.

If you want a ready solution, you can get the debian package source which I prepared, build it by running debuild -us -uc -F and install by running dpkg -i.

 

Published by

dopiera

Full time geek.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.