summaryrefslogtreecommitdiffstats
path: root/man/notify-selfcontained-example.py
blob: a1efb419ced56b6c2293dd6a9938e51040b371e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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")