Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cpaelzer/fc3abd28f81eda55ffb317bb4091bf48 to your computer and use it in GitHub Desktop.
Save cpaelzer/fc3abd28f81eda55ffb317bb4091bf48 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 Unix Socket 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 UnixStreamServer, StreamRequestHandler

import logging
import os
import sys

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

    # 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!)
        UnixStreamServer.__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
    CHECKPATH='/opt/foo/wouldbesysfsfile'
    if not os.path.isfile(CHECKPATH):
        logging.info("%s does not exist failing" % CHECKPATH)
        sys.exit(1);
    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
ConditionPathExists=/opt/foo/wouldbesysfsfile

[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=/run/foo.sock

[Install]
WantedBy=sockets.target

Test it

Send a message to the listening service:

echo "Hello World"| netcat -U /run/foo.sock

Modifications

This is based on a gist by drmalex07 Modifications are made to debug an rdma issue

  • convert to use a local unix socket path
  • add a Condition check to the service
  • Let the service fail to initialize if the checked apth does not exist
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment