Network exec in systemd

George Shuklin
OpsOps
Published in
2 min readJan 11, 2023

Today I’m investigating an odd circular dependency in systemd units in openstack-ansible. One of the units under investigation implements a really interesting way to run database checks. I decided to dig deeper to better understand that technique.

Tl;dr; systemd.socket activates unit of unit@.service template which in turn run a binary pass active tcp stream from a socket to stdin/stdout of that unit, implementing something like inetd/cgi-bin.

Side note: I’ve known that systemd can create speculative services, and I’ve known, it can run stuff like inetd can, but I never paid attention to it.

Exercise: we will create a network service reporting current date by running date (/usr/bin/date) command.

Template

/etc/systemd/system/date@.service

[Service]
Type = oneshot
ExecStart = -/usr/bin/date
StandardOutput = socket

It’s really trimmed down to essentials, which is ‘what to run’, ‘use socket’ and ‘type’. Note, there is no ‘[install]’ section.

Magic is in the StandardOutput option

socket connects standard output to a socket acquired via socket activation.

Note, we don’t need stdin, because date does not read from it anything.

Socket

/etc/systemd/system/date.socket

[Unit]
After=network.target

[Socket]
ListenStream=127.0.0.1:4242
Accept=yes

[Install]
WantedBy=sockets.target

The magic is in Accept stanza.

If yes, a service instance is spawned for each incoming connection and only the connection socket is passed to it

Enablement

# systemctl enable date.socket
Created symlink /etc/systemd/system/sockets.target.wants/date.socket → /etc/systemd/system/date.socket.
# systemctl start date.socket

Verification

# ss -lnpt | grep 4242
LISTEN 0 4096 127.0.0.1:4242 0.0.0.0:* users:(("systemd",pid=1,fd=66))

# echo | nc 127.0.0.1 4242
Wed 11 Jan 2023 12:27:39 PM UTC

(echo before nc is needed to close connection, it’s netcat-specific thing).

Applications

Outside of creating silly http servers, the main application, as I see it, is to use to for periodic remote execution. Nagios has a dedicated NRPE server for that.

If you have some rarely scraped exporter (like domain expiration), running it as stdin/stdout unit under systemd supervision may be efficient way to reduce memory/process footprint. You still need to have a good http implementation on stdin/stdout (or can we just mock it with prints? Interesting thinking…).

--

--

George Shuklin
OpsOps

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.