Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 46 You must be signed in to star a gist
  • Fork 17 You must be signed in to fork a gist
  • Save drmalex07/333d8a88c4918954e8e4 to your computer and use it in GitHub Desktop.
Save drmalex07/333d8a88c4918954e8e4 to your computer and use it in GitHub Desktop.
An example network service with systemd-activated socket in Python. #systemd #python #socket #socket-activation

README

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.

An example server

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()

Define the systemd service unit

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

Define the systemd socket unit

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

Test it

Send a message to the listening service:

echo "Hello World"| netcat 127.0.0.1 9999
@kylemanna
Copy link

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.

@pkaramol
Copy link

This is very useful thanks for sharing.

Are there any throttling limits on this imposed say by python or something?

If I open it up (by changing 127.0.0.1 --> 0.0.0.0) and have a syslog-ng server streaming on this listener, could there (in theory) be any logs lost due to the rate that syslog-ng serves data?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment