summaryrefslogtreecommitdiffstats
path: root/man/notify-selfcontained-example.py
diff options
context:
space:
mode:
Diffstat (limited to 'man/notify-selfcontained-example.py')
-rw-r--r--man/notify-selfcontained-example.py104
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")