systemd socket service

An adsb-centric example to run a socket service via systemd.



Motivation

Combing multiple dump1090 and dump978 feeders was historically a task for cron and keepalive scripts using ncat or socat.

With my SDR receivers in different windows for maximum coverage, I would use ncat to connect different input and output ports to feed the central server running as --net-only. dump978 adds a complication as it only outputs UAT downlink messages and our map expects ADS-B ES/NT messages.

Aggregation to the primary dump1090 map was handled with cron via:

#!/bin/bash
# test 1090 receivers are forwarding
# runs via cron @reboot and a few times an hour for reliability

for receiver in pizero pi3
do
#echo "testing $receiver"
ps -ef |grep "TCP\:$receiver\:30005" > /dev/null
if [ $? != 0 ]
then
      echo "restarting 1090 $receiver"
      socat -u TCP:$receiver:30005 TCP:localhost:30004 &
# original netcat way
#     #ncat --recv-only $receiver 30005 | ncat --send-only localhost 30004 &
fi
done
# 978 receiver (more complicated with uat2esnt)
for receiver in pizero
do
    #echo "checking $receiver"
    pgrep -a 'ncat' | grep "$receiver 20002" > /dev/null
    if [ $? != 0 ]
    then
        echo "restarting 978 $receiver"
        ncat --recv-only $receiver 20002 | /path/to/git/dump978/uat2esnt | ncat --send-only localhost 30001 &
    fi
done

systemd replacement

So I finally stopped worrying and learned to love the systemd. Reading Pid Eins was helpful. The uat2esnt situation sounded like a good case for a socket service. This will consist of a uat2esnt.socket and uat2esnt@.service.

uat2esnt.socket

[Unit]
Description = dump978 uat2esnt conversion service

[Socket]
ListenStream = 40404
Accept = yes

[Install]
WantedBy = sockets.target

uat2esnt@.service

[Unit]
Description = dump978 uat2esnt conversion service

[Service]
#User=dump1090
ExecStart=/path/to/git/dump978/uat2esnt
StandardInput=socket
StandardOutput=socket
SyslogIdentifier=uat2esnt
Type=simple
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target

Link or copy these to /etc/systemd/system/ and systemctl start uat2esnt.socket.

Now you can test this by connecting to port 40404 and feeding it some UAT messages and see the raw translated message.

socat - TCP:localhost:40404

sample data to paste:

-00ad723335ade35230d4065900941d813800;rs=1;
-08a04568350889526cb40b7911e81100b806431335ed2d0b62a4c0a0000c00000000;
-08ad723335adf55230fc0669008c1e815807ae2094e6c40b42a4c2800006c0000000;rs=5;
-00a0456835083f526cce0b7911e81180b800;
-10a0456835082b526cd40b8911e81180ce0000000000000000000000000c00000000;
-00a045683507d7526cf20b8911e41180bf00;
-00a045683507c3526cfa0b9911e41200b800;
-08a0456835078f526d0c0b9911e41200b909d9073469c40b96a4c2a0000c20000000;

You should see the ADS-B ES/NT messages coded as replies in the terminal.

making it more useful

Great! It processes messages but I'm feeding other services. Fixing this, remove the StandardOutput= line and change the ExecStart= line to feed my 1090 receiver and make sure that service is running with an After=dump1090.service in the [Unit] section.

As a gotcha, systemd doesn't support pipes in the ExecStart= so I pass a shell command as an argument hack. ExecStart=/usr/bin/sh -c '/path/to/git/dump978/uat2esnt | socat -u STDIN TCP:localhost:30001'. When you've done this, systemctl daemon-reload is your friend to test the changes.

Upstream, the UAT 978 receiver still needs to send the messages to HQ.

socat978@.service:

[Unit]
Description=dump978 feed from a %I to dump1090
Wants=network.target
After=network.target dump1090.service uat2esnt.socket

[Service]
User=dump1090
ExecStart=socat -u TCP:%i:20002 TCP:localhost:40404
SyslogIdentifier=socat978
Type=simple
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target

This script can be setup to feed from multiple hosts ala systemctl start socat978@hostname.

Don't forget to systemctl enable your services to persist when you have everything working.

more things to try

  • feed via unix sockets
  • improve the sh -c hack for the pipe in systemd