This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "IPFire 2.x development tree".
The branch, next has been updated via 8ead2ddf3d0f1521c2a99b509373615f77a754a0 (commit) from 048d2be91a17ea57b870b0b944d439978b2f88a8 (commit)
Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below.
- Log ----------------------------------------------------------------- commit 8ead2ddf3d0f1521c2a99b509373615f77a754a0 Author: Michael Tremer michael.tremer@ipfire.org Date: Wed Aug 21 10:10:33 2024 +0100
unbound-dhcp-leases-bridge: Watch unbound
This patch adds a watcher thread which monitors if Unbound is still alive. If not, it will wait until Unbound comes back, rewrite the leases file and reload Unbound to get it back into sync.
Afterwards Unbound will receive updates as usual.
Signed-off-by: Michael Tremer michael.tremer@ipfire.org
-----------------------------------------------------------------------
Summary of changes: config/unbound/unbound-dhcp-leases-bridge | 115 +++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-)
Difference in files: diff --git a/config/unbound/unbound-dhcp-leases-bridge b/config/unbound/unbound-dhcp-leases-bridge index 3972a45c6..986fae2d2 100644 --- a/config/unbound/unbound-dhcp-leases-bridge +++ b/config/unbound/unbound-dhcp-leases-bridge @@ -83,10 +83,10 @@ class UnboundDHCPLeasesBridge(object): # Initialize the worker self.worker = Worker(self.queue, callback=self._handle_message)
- self.unbound = UnboundConfigWriter(unbound_leases_file) + # Initialize the watcher + self.watcher = Watcher(reload=self.reload)
- # Load all required data - self.reload() + self.unbound = UnboundConfigWriter(unbound_leases_file)
def run(self): log.info("Unbound DHCP Leases Bridge started on %s" % self.leases_file) @@ -94,6 +94,9 @@ class UnboundDHCPLeasesBridge(object): # Launch the worker self.worker.start()
+ # Launch the watcher + self.watcher.start() + # Open the server socket self.socket = self._open_socket(self.socket_path)
@@ -132,7 +135,13 @@ class UnboundDHCPLeasesBridge(object):
# Terminate the worker self.queue.put(None) + + # Terminate the watcher + self.watcher.terminate() + + # Wait for the worker and watcher to finish self.worker.join() + self.watcher.join()
log.info("Unbound DHCP Leases Bridge terminated")
@@ -359,6 +368,84 @@ class UnboundDHCPLeasesBridge(object): self.socket.close()
+class Watcher(threading.Thread): + """ + Watches if Unbound is still running. + """ + def __init__(self, reload, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.reload = reload + + # Set to true if this thread should be terminated + self._terminated = threading.Event() + + def run(self): + log.debug("Watcher launched") + + pidfd = None + + while True: + # One iteration takes 30 seconds unless we don't know the process + # when we try to find it once a second. + if self._terminated.wait(30 if pidfd else 1): + break + + # Fetch a PIDFD for Unbound + if pidfd is None: + pidfd = self._get_pidfd() + + # If we could not acquire a PIDFD, we will try again soon... + if not pidfd: + log.warning("Cannot find Unbound...") + continue + + # Since Unbound has been restarted, we need to reload it all... + self.reload() + + log.debug("Checking if Unbound is still alive...") + + # Send the process a signal + try: + signal.pidfd_send_signal(pidfd, signal.SIG_DFL) + + # If the process has died, we land here and will have to wait until Unbound + # has come back and reload it... + except ProcessLookupError as e: + log.error("Unbound has died") + + # Reset the PIDFD + pidfd = None + + else: + log.debug("Unbound is alive") + + log.debug("Watcher terminated") + + def terminate(self): + """ + Called to signal this thread to terminate + """ + self._terminated.set() + + def _get_pidfd(self): + """ + Returns a PIDFD for unbound if it is running, otherwise None. + """ + # Try to find the PID + pid = pidof("unbound") + + if pid: + log.debug("Unbound is running as PID %s" % pid) + + # Open a PIDFD + pidfd = os.pidfd_open(pid) + + log.debug("Acquired PIDFD %s for PID %s" % (pidfd, pid)) + + return pidfd + + class Worker(threading.Thread): """ The worker is launched in a separate thread @@ -727,6 +814,28 @@ class UnboundConfigWriter(object): self._control("local_data_remove", name)
+def pidof(program): + """ + Returns the first PID of the given program. + """ + try: + output = subprocess.check_output(["pidof", program]) + except subprocess.CalledProcessError as e: + return + + # Convert to string + output = output.decode() + + # Return the first PID + for pid in output.split(): + try: + pid = int(pid) + except ValueError: + continue + + return pid + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Bridge for DHCP Leases and Unbound DNS")
hooks/post-receive -- IPFire 2.x development tree