From 96c3b06abbb11f7eb8735acd7741d62854e425ba Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 19 May 2017 13:25:49 +0200 Subject: [PATCH] amend to #1778: repair notifier wait-cycle (too long timeout in polling, too late check for pending files, too long stop) --- fail2ban/server/filterpyinotify.py | 32 ++++++++++++++++++++---------- fail2ban/tests/filtertestcase.py | 1 + 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/fail2ban/server/filterpyinotify.py b/fail2ban/server/filterpyinotify.py index 71540f3c..53f2cbda 100644 --- a/fail2ban/server/filterpyinotify.py +++ b/fail2ban/server/filterpyinotify.py @@ -82,7 +82,7 @@ class FilterPyinotify(FileFilter): self.__watchDirs = dict() self.__pending = dict() self.__pendingChkTime = 0 - self.__pendingNextTime = 0 + self.__pendingMinTime = 60 logSys.debug("Created FilterPyinotify") def callback(self, event, origin=''): @@ -150,7 +150,7 @@ class FilterPyinotify(FileFilter): def _addPending(self, path, reason, isDir=False): if path not in self.__pending: self.__pending[path] = [Utils.DEFAULT_SLEEP_INTERVAL, isDir]; - self.__pendingNextTime = 0 + self.__pendingMinTime = 0 if isinstance(reason, pyinotify.Event): reason = [reason.maskname, reason.pathname] logSys.log(logging.MSG, "Log absence detected (possibly rotation) for %s, reason: %s of %s", @@ -165,7 +165,7 @@ class FilterPyinotify(FileFilter): if not self.__pending: return ntm = time.time() - if ntm < self.__pendingNextTime: + if ntm < self.__pendingChkTime + self.__pendingMinTime: return found = {} minTime = 60 @@ -187,7 +187,7 @@ class FilterPyinotify(FileFilter): del self.__pending[path] except KeyError: pass self.__pendingChkTime = time.time() - self.__pendingNextTime = self.__pendingChkTime + minTime + self.__pendingMinTime = minTime # process now because we've missed it in monitoring: for path, isDir in found.iteritems(): # refresh monitoring of this: @@ -295,6 +295,12 @@ class FilterPyinotify(FileFilter): self.commonError() self.ticks += 1 + @property + def __notify_maxtout(self): + # timeout for pyinotify must be set in milliseconds (fail2ban time values are + # floats contain seconds), max 0.5 sec (additionally regards pending check time) + return min(self.sleeptime, 0.5, self.__pendingMinTime) * 1000 + ## # Main loop. # @@ -304,9 +310,8 @@ class FilterPyinotify(FileFilter): def run(self): prcevent = pyinotify.ProcessEvent() prcevent.process_default = self.__process_default - ## timeout for pyinotify must be set in milliseconds (our time values are floats contain seconds) self.__notifier = pyinotify.Notifier(self.__monitor, - prcevent, timeout=self.sleeptime * 1000) + prcevent, timeout=self.__notify_maxtout) logSys.debug("[%s] filter started (pyinotifier)", self.jailName) while self.active: try: @@ -314,13 +319,19 @@ class FilterPyinotify(FileFilter): # slow check events while idle: if self.idle: if Utils.wait_for(lambda: not self.active or not self.idle, - self.sleeptime * 10, self.sleeptime + min(self.sleeptime * 10, self.__pendingMinTime), + min(self.sleeptime, self.__pendingMinTime) ): if not self.active: break # default pyinotify handling using Notifier: self.__notifier.process_events() - if Utils.wait_for(lambda: not self.active or self.__notifier.check_events(), self.sleeptime): + + # wait for events / timeout: + notify_maxtout = self.__notify_maxtout + def __check_events(): + return not self.active or self.__notifier.check_events(timeout=notify_maxtout) + if Utils.wait_for(__check_events, min(self.sleeptime, self.__pendingMinTime)): if not self.active: break self.__notifier.read_events() @@ -347,11 +358,10 @@ class FilterPyinotify(FileFilter): # Call super.stop() and then stop the 'Notifier' def stop(self): - if self.__notifier: # stop the notifier - self.__notifier.stop() # stop filter thread: super(FilterPyinotify, self).stop() - self.join() + if self.__notifier: # stop the notifier + self.__notifier.stop() ## # Wait for exit with cleanup. diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index ec2dea89..20a7ad50 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -996,6 +996,7 @@ def get_monitor_failures_testcase(Filter_): self._wait4failures(2) # stop before tmpdir deleted (just prevents many monitor events) self.filter.stop() + self.filter.join() def _test_move_into_file(self, interim_kill=False):