diff options
Diffstat (limited to 'man/notify-selfcontained-example.py')
-rw-r--r-- | man/notify-selfcontained-example.py | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/man/notify-selfcontained-example.py b/man/notify-selfcontained-example.py new file mode 100644 index 0000000..a1efb41 --- /dev/null +++ b/man/notify-selfcontained-example.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT-0 +# +# Implement the systemd notify protocol without external dependencies. +# Supports both readiness notification on startup and on reloading, +# according to the protocol defined at: +# https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html +# This protocol is guaranteed to be stable as per: +# https://systemd.io/PORTABILITY_AND_STABILITY/ + +import errno +import os +import signal +import socket +import sys +import time + +reloading = False +terminating = False + +def notify(message): + if not message: + raise ValueError("notify() requires a message") + + socket_path = os.environ.get("NOTIFY_SOCKET") + if not socket_path: + return + + if socket_path[0] not in ("/", "@"): + raise OSError(errno.EAFNOSUPPORT, "Unsupported socket type") + + # Handle abstract socket. + if socket_path[0] == "@": + socket_path = "\0" + socket_path[1:] + + with socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM | socket.SOCK_CLOEXEC) as sock: + sock.connect(socket_path) + sock.sendall(message) + +def notify_ready(): + notify(b"READY=1") + +def notify_reloading(): + microsecs = time.clock_gettime_ns(time.CLOCK_MONOTONIC) // 1000 + notify(f"RELOADING=1\nMONOTONIC_USEC={microsecs}".encode()) + +def notify_stopping(): + notify(b"STOPPING=1") + +def reload(signum, frame): + global reloading + reloading = True + +def terminate(signum, frame): + global terminating + terminating = True + +def main(): + print("Doing initial setup") + global reloading, terminating + + # Set up signal handlers. + print("Setting up signal handlers") + signal.signal(signal.SIGHUP, reload) + signal.signal(signal.SIGINT, terminate) + signal.signal(signal.SIGTERM, terminate) + + # Do any other setup work here. + + # Once all setup is done, signal readiness. + print("Done setting up") + notify_ready() + + print("Starting loop") + while not terminating: + if reloading: + print("Reloading") + reloading = False + + # Support notifying the manager when reloading configuration. + # This allows accurate state tracking as well as automatically + # enabling 'systemctl reload' without needing to manually + # specify an ExecReload= line in the unit file. + + notify_reloading() + + # Do some reconfiguration work here. + + print("Done reloading") + notify_ready() + + # Do the real work here ... + + print("Sleeping for five seconds") + time.sleep(5) + + print("Terminating") + notify_stopping() + +if __name__ == "__main__": + sys.stdout.reconfigure(line_buffering=True) + print("Starting app") + main() + print("Stopped app") |