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.