Systemd socket based activation

In the previous post we have seen how to start a simple deamon with systemd. Now we will use the socket based activation of this init system to make our daemon start when it is needed.

Let’s start with a basic server which only prints a message.

#include <stdio.h>
#include <stdlib.h>
#include "CommonLib/net_socket.h"

void panic(lerror *error) {
	  
lstring *buf = NULL;

l_assert(error!=NULL);
	  
buf = lstring_new();
	  
buf = lerror_fill_f(error, buf);
	  
fprintf(stderr, "%s", buf);
	  
lstring_delete(buf);

abort();
}

int main() {
	  
lerror *myError = NULL;
	  
TCPListenSocket *listeningSocket = NULL;
	  
TCPSocket *socket = NULL;

listeningSocket = TCPListenSocket_new("0.0.0.0",
		  
"3233", &myError);
	  
if (myError!=NULL) panic(myError);

puts("Hello server is accepting connections");
	  
fflush(stdout);

while(1) {
		  
socket = TCPListenSocket_accept(listeningSocket,
			  
&myError);
		  
if (myError!=NULL) panic(myError);

TCPSocket_send_string(socket,
			  
"Hello from the server!", &myError);
		  
if (myError!=NULL) panic(myError);

TCPSocket_destroy(socket);
		  
socket = NULL;
	  
}

TCPListenSocket_destroy(listeningSocket);
	  
return 0;
}

This code is based on the CommonLib library. Now, if systemd started us, we need to get the listening file descriptor and to do so with must include the systemd header file:

#include <systemd/sd-daemon.h>

and to replace:

	  
listeningSocket = TCPListenSocket_new("0.0.0.0",
		  
"3233", &myError);
	  
if (myError!=NULL) panic(myError);

with:

	  
if (sd_listen_fds(0)==0) {
		  
listeningSocket = TCPListenSocket_new("0.0.0.0",
			  
"3233", &myError);
		  
if (myError!=NULL) panic(myError);
	  
} else {
		  
listeningSocket = TCPListenSocket_new_from_fd(
			  
SD_LISTEN_FDS_START + 0,
			  
"localhost:3233");
		  
puts("Hello server received socket from SystemD");
		  
fflush(stdout);
	  
}

The sd_listen_fds function will count the sockets passed by systemd and, if systemd started us, the file descriptor SD_LISTEN_FDS_START + 0, the first, is encapsulated in a TCPListenSocket object. The server then follows in the same way. Simple, isn’t?

Now our server is socket activatable. We must tell it to systemd writing a my_server.socket like this:

[Socket]
ListenStream=3233

[Install]
WantedBy=sockets.target

We must also write a my_server.service file like this:

[Unit]
Description=<description here>
After=network.target

[Service]
User=<user name here>
ExecStart=<executable file name here>

Note that this service file has not the install section.

These files must be placed in our /usr/lib/systemd/system directory. We can make systemd preallocate the socket for us with:

\# systemctl start my_server.socket

If you ask your system for the running processes you will not see the daemon process. When you will make the first connection systemd will start it.