repz stosb

Systemd Socket Activation

I recently wrote my first service that has (optional) systemd socket activation, and I have to say: I really like it.

It's really easy to use, and it makes writing services with graceful restarts really easy. In the simplest usage you add a small amount of boilerplate to your code:

int listen_sock;
int fd_count = sd_listen_fds(0);
if (fd_count == 1) {
  listen_sock = SD_LISTEN_FDS_START;
} else {
  // set listen_sock w/ usual bind/listen stuff here
struct sockaddr addr;
socklen_t addrlen;
while (int client_sock = accept(listen_sock, &addr, &addrlen)) {
  // accept loop here

The only code here that's different is that we try to sd_listen_fds() before doing bind()/accept(). Then if the process has been started in socket activation mode the process will start with a ready listen socket. There's also some systemd configuration you have to write to use this; namely you'll have to create a short file name myservice.socket that has the listen address, and then:

systemctl enable myservice.socket
systemctl start myservice.socket

Just with this code things are already an improvement over usual because when you restart the process (say, because you're upgrading the code) the listen socket will be created by systemd as soon as the old process exits, meaning that there's a shorter gap when no listen socket is available to clients.

There's also an easy way to do completely graceful process restarts using sd_notify_with_fds(). Briefly, the way this works is you call this while your process is exiting you use this API call to hand file descriptors (along with their "names") back to systemd. Then when the new process starts up it can request those same file descriptors back with sd_listen_fds_with_names(). You can control just the listen socket this way (so there's no gap whatsoever where the listen socket is closed), and you can also use this mechanism to transfer client sockets: it's up to you. If you want to transfer client sockets you'll have to come up with your own serialization format to send any connection state over. Systemd has some helpers for this too.

There have always been ways to do this: you've been able to transfer file descriptors on Unix systems over AF_UNIX systems for eternity. But systemd makes this process much easier, and does it in a way that's integrated with init which is really nice.