The example below creates a TCP server listening on a stream (i.e. SOCK_STREAM
) socket. A similar approach can be followed to
create a UDP server on a datagram (i.e. SOCK_DGRAM
) socket. See man systemd.socket
for details.
Create an simple echo server at /opt/foo/serve.py
.
#!/usr/bin/python
from SocketServer import TCPServer, StreamRequestHandler
import socket
import logging
class Handler(StreamRequestHandler):
def handle(self):
self.data = self.rfile.readline().strip()
logging.info("From <%s>: %s" % (self.client_address, self.data))
self.wfile.write(self.data.upper() + "\r\n")
class Server(TCPServer):
# The constant would be better initialized by a systemd module
SYSTEMD_FIRST_SOCKET_FD = 3
def __init__(self, server_address, handler_cls):
# Invoke base but omit bind/listen steps (performed by systemd activation!)
TCPServer.__init__(
self, server_address, handler_cls, bind_and_activate=False)
# Override socket
self.socket = socket.fromfd(
self.SYSTEMD_FIRST_SOCKET_FD, self.address_family, self.socket_type)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
HOST, PORT = "localhost", 9999 # not really needed here
server = Server((HOST, PORT), Handler)
server.serve_forever()
Create the service definition unit file at /etc/systemd/system/foo.service
(note that this unit refers to foo.socket
):
[Unit]
Description=Foo Service
After=network.target foo.socket
Requires=foo.socket
[Service]
Type=simple
ExecStart=/usr/bin/python /opt/foo/serve.py
TimeoutStopSec=5
[Install]
WantedBy=multi-user.target
Create the socket definition unit file at /etc/systemd/system/foo.socket
:
[Unit]
Description=Foo Socket
PartOf=foo.service
[Socket]
ListenStream=127.0.0.1:9999
[Install]
WantedBy=sockets.target
Send a message to the listening service:
echo "Hello World"| netcat 127.0.0.1 9999
Thanks for writing this up! It saved me alot of time experimenting with some socket activation stuff. For those interested, my fork adds python3 support and uses the systemd user daemon.