Repository: fail2ban/fail2ban

Issue:
No warning if *logpath* improperly configured or file missing
This can save new users hours of frustration. Not a serious error at all,
just something that might save people hours of looking around :)

After spending hours trying to figure out why my fail2ban was not banning, I
thought to myself, why not run verbose mode, and maybe some extra info
might appear (the following run from the command line) :

```
fail2ban-client -v start
```

Doing so revealed the following error ERROR :
# fail2ban-client start

WARNING 'action' not defined in 'php-url-fopen'. Using default value
WARNING 'action' not defined in 'lighttpd-fastcgi'. Using default value
ERROR  No file found for /var/log/mail.log
2012-07-13 10:10:57,284 fail2ban.server : INFO   Starting Fail2ban v0.8.5
2012-07-13 10:10:57,287 fail2ban.server : INFO   Starting in daemon mode

Post Note : turns out you don't need verbose (-v) to get the above info.

This ERROR does not appear if you use the _normal_ way of starting fail2ban :

   /etc/init.d/fail2ban start

Unfortunately the ERROR is not written to log or anywhere else I can see.

I only found it after running the fail2ban client on the command line.

Maiko Langelaar
IT Physics and Astronomy
University of Manitoba



Discussion:
FWIW -- now (as of 0.8.9) fail2ban-client would spit out an error if inaccessible file was specified... but those are veiled by init.d script... until we make those effectively communicated back to client so it would report failure or just log in the fail2ban.log -- I will keep this one open


Resolving patch:
diff --git a/client/jailreader.py b/client/jailreader.py
--- a/client/jailreader.py
+++ b/client/jailreader.py
@@ -103,16 +103,30 @@ def getOptions(self):
 				logSys.warn("No actions were defined for %s" % self.__name)
 		return True
 	
-	def convert(self):
+	def convert(self, allow_no_files=False):
+		"""Convert read before __opts to the commands stream
+
+		Parameters
+		----------
+		allow_missing : bool
+		  Either to allow log files to be missing entirely.  Primarily is
+		  used for testing
+		 """
+
 		stream = []
 		for opt in self.__opts:
 			if opt == "logpath":
+				found_files = 0
 				for path in self.__opts[opt].split("\n"):
 					pathList = glob.glob(path)
 					if len(pathList) == 0:
-						logSys.error("No file found for " + path)
+						logSys.error("No file(s) found for glob %s" % path)
 					for p in pathList:
+						found_files += 1
 						stream.append(["set", self.__name, "addlogpath", p])
+				if not (found_files or allow_no_files):
+					raise ValueError(
+						"Have not found any log file for %s jail" % self.__name)
 			elif opt == "backend":
 				backend = self.__opts[opt]
 			elif opt == "maxretry":
diff --git a/client/jailsreader.py b/client/jailsreader.py
--- a/client/jailsreader.py
+++ b/client/jailsreader.py
@@ -79,14 +79,23 @@ def getOptions(self, section = None):
 					return False
 		return True
 	
-	def convert(self):
+	def convert(self, allow_no_files=False):
+		"""Convert read before __opts and jails to the commands stream
+
+		Parameters
+		----------
+		allow_missing : bool
+		  Either to allow log files to be missing entirely.  Primarily is
+		  used for testing
+		"""
+
 		stream = list()
 		for opt in self.__opts:
 			if opt == "":
 				stream.append([])
 		# Convert jails
 		for jail in self.__jails:
-			stream.extend(jail.convert())
+			stream.extend(jail.convert(allow_no_files=allow_no_files))
 		# Start jails
 		for jail in self.__jails:
 			stream.append(["start", jail.getName()])
diff --git a/testcases/clientreadertestcase.py b/testcases/clientreadertestcase.py
--- a/testcases/clientreadertestcase.py
+++ b/testcases/clientreadertestcase.py
@@ -132,7 +132,7 @@ def testReadStockJailConfForceEnabled(self):
 		jails = JailsReader(basedir='config', force_enable=True) # we are running tests from root project dir atm
 		self.assertTrue(jails.read())		  # opens fine
 		self.assertTrue(jails.getOptions())	  # reads fine
-		comm_commands = jails.convert()
+		comm_commands = jails.convert(allow_no_files=True)
 
 		# by default we have lots of jails ;)
 		self.assertTrue(len(comm_commands))


Test patch: