Switch from inotify watching individual files to monitoring the containing directories, as because dhcpd renames its leases file into a backup, monitoring the single inode does not work well. Additionally, python appears to have a bug with replacing expired inotify watches on single files. --- unbound-dhcp-leases-bridge | 47 +++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 16 deletions(-)
diff --git unbound-dhcp-leases-bridge unbound-dhcp-leases-bridge index a2df5f1..6e22066 100644 --- unbound-dhcp-leases-bridge +++ unbound-dhcp-leases-bridge @@ -72,6 +72,15 @@ class UnboundDHCPLeasesBridge(object): self.fix_leases_file = fix_leases_file self.hosts_file = hosts_file
+ # base mask for a completed file change + mask = inotify.constants.IN_CLOSE_WRITE | inotify.constants.IN_MOVED_TO + # IN_MODIFY since dhcpd appends lease updates to an open file + self.watches = { + self.leases_file: mask | inotify.constants.IN_MODIFY, + self.fix_leases_file: mask, + self.hosts_file: mask + } + self.unbound = UnboundConfigWriter(unbound_leases_file) self.running = False
@@ -80,36 +89,42 @@ class UnboundDHCPLeasesBridge(object): self.running = True
# Initial setup - self.hosts = self.read_static_hosts() - self.update_dhcp_leases() + update_hosts = True + update_leases = True + + i = inotify.adapters.Inotify()
- i = inotify.adapters.Inotify([ - self.leases_file, - self.fix_leases_file, - self.hosts_file, - ]) + for f in self.watches: + i.add_watch(os.path.dirname(f), self.watches[f])
for event in i.event_gen(): # End if we are requested to terminate if not self.running: break
+ # Make pending updates once inotify queue is empty if event is None: + if update_hosts: + self.hosts = self.read_static_hosts() + update_hosts = False + if update_leases: + self.update_dhcp_leases() + update_leases = False continue
header, type_names, watch_path, filename = event
- # Update leases after leases file has been modified - if "IN_MODIFY" in type_names: - # Reload hosts - if watch_path == self.hosts_file: - self.hosts = self.read_static_hosts() + file = os.path.join(watch_path, filename) + + if not file in self.watches: + continue + + log.debug("Inotify %s: %s", file, " ".join(type_names))
- self.update_dhcp_leases() + update_leases = True
- # If the file is deleted, we re-add the watcher - if "IN_IGNORED" in type_names: - i.add_watch(watch_path) + if file == self.hosts_file: + update_hosts = True
log.info("Unbound DHCP Leases Bridge terminated")