From f31195a4fc1acc346268220e02cd79d572dea16f Mon Sep 17 00:00:00 2001
From: sebres <info@sebres.de>
Date: Sun, 26 Nov 2017 23:03:29 +0100
Subject: [PATCH 1/3] added new logtarget "SYSOUT" to log from fail2ban working
 in foreground as systemd-service (in opposite to "STDOUT" don't log
 time-stamps).

---
 config/fail2ban.conf               |  2 +-
 fail2ban/client/fail2bancmdline.py |  2 +-
 fail2ban/client/fail2banserver.py  |  3 ++-
 fail2ban/helpers.py                |  6 ++++--
 fail2ban/server/server.py          | 15 +++++++++------
 files/bash-completion              |  2 +-
 files/fail2ban.service.in          |  4 ++--
 man/fail2ban-client.1              |  5 +++--
 man/fail2ban-server.1              |  5 +++--
 9 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/config/fail2ban.conf b/config/fail2ban.conf
index 7c001155..52e47187 100644
--- a/config/fail2ban.conf
+++ b/config/fail2ban.conf
@@ -30,7 +30,7 @@ loglevel = INFO
 #         using logrotate -- also adjust or disable rotation in the
 #         corresponding configuration file
 #         (e.g. /etc/logrotate.d/fail2ban on Debian systems)
-# Values: [ STDOUT | STDERR | SYSLOG | FILE ]  Default: STDERR
+# Values: [ STDOUT | STDERR | SYSLOG | SYSOUT | FILE ]  Default: STDERR
 #
 logtarget = /var/log/fail2ban.log
 
diff --git a/fail2ban/client/fail2bancmdline.py b/fail2ban/client/fail2bancmdline.py
index 401aa9b6..269fa174 100644
--- a/fail2ban/client/fail2bancmdline.py
+++ b/fail2ban/client/fail2bancmdline.py
@@ -99,7 +99,7 @@ class Fail2banCmdLine():
 		output("    -s <FILE>               socket path")
 		output("    -p <FILE>               pidfile path")
 		output("    --loglevel <LEVEL>      logging level")
-		output("    --logtarget <FILE>|STDOUT|STDERR|SYSLOG")
+		output("    --logtarget <TARGET>    logging target, use file-name or stdout, stderr, syslog or sysout.")
 		output("    --syslogsocket auto|<FILE>")
 		output("    -d                      dump configuration. For debugging")
 		output("    --dp, --dump-pretty     dump the configuration using more human readable representation")
diff --git a/fail2ban/client/fail2banserver.py b/fail2ban/client/fail2banserver.py
index 006a02cf..2dcaddf7 100644
--- a/fail2ban/client/fail2banserver.py
+++ b/fail2ban/client/fail2banserver.py
@@ -210,7 +210,8 @@ class Fail2banServer(Fail2banCmdLine):
 					if server: # pragma: no cover
 						server.quit()
 					exit(-1)
-				logSys.debug('Starting server done')
+				if background:
+					logSys.debug('Starting server done')
 
 		except Exception as e:
 			if self._conf["verbose"] > 1:
diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py
index 7eaa59f8..5b027b32 100644
--- a/fail2ban/helpers.py
+++ b/fail2ban/helpers.py
@@ -143,7 +143,7 @@ def str2LogLevel(value):
 		raise ValueError("Invalid log level %r" % value)
 	return ll
 
-def getVerbosityFormat(verbosity, fmt=' %(message)s'):
+def getVerbosityFormat(verbosity, fmt=' %(message)s', addtime=True):
 	"""Custom log format for the verbose runs
 	"""
 	if verbosity > 1: # pragma: no cover
@@ -152,7 +152,9 @@ def getVerbosityFormat(verbosity, fmt=' %(message)s'):
 		if verbosity > 2:
 			fmt = ' +%(relativeCreated)5d %(thread)X %(name)-25.25s %(levelname)-5.5s' + fmt
 		else:
-			fmt = ' %(asctime)-15s %(thread)X %(levelname)-5.5s' + fmt
+			fmt = ' %(thread)X %(levelname)-5.5s' + fmt
+			if addtime:
+				fmt = ' %(asctime)-15s' + fmt
 	return fmt
 
 
diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py
index e3b22c44..ce28e3f9 100644
--- a/fail2ban/server/server.py
+++ b/fail2ban/server/server.py
@@ -27,7 +27,6 @@ __license__ = "GPL"
 import threading
 from threading import Lock, RLock
 import logging
-import logging.handlers
 import os
 import signal
 import stat
@@ -561,10 +560,8 @@ class Server:
 				self.__logTarget = target
 				return True
 			# set a format which is simpler for console use
-			fmt = "%(asctime)s %(name)-24s[%(process)d]: %(levelname)-7s %(message)s"
+			fmt = "%(name)-24s[%(process)d]: %(levelname)-7s %(message)s"
 			if systarget == "SYSLOG":
-				# Syslog daemons already add date to the message.
-				fmt = "%(name)s[%(process)d]: %(levelname)s %(message)s"
 				facility = logging.handlers.SysLogHandler.LOG_DAEMON
 				if self.__syslogSocket == "auto":
 					import platform
@@ -581,7 +578,7 @@ class Server:
 						"Syslog socket file: %s does not exists"
 						" or is not a socket" % self.__syslogSocket)
 					return False
-			elif systarget == "STDOUT":
+			elif systarget in ("STDOUT", "SYSOUT"):
 				hdlr = logging.StreamHandler(sys.stdout)
 			elif systarget == "STDERR":
 				hdlr = logging.StreamHandler(sys.stderr)
@@ -615,8 +612,14 @@ class Server:
 			if logger.getEffectiveLevel() <= logging.DEBUG: # pragma: no cover
 				if self.__verbose is None:
 					self.__verbose = logging.DEBUG - logger.getEffectiveLevel() + 1
+			# If handler don't already add date to the message:
+			addtime = systarget not in ("SYSLOG", "SYSOUT")
+			# verbose log-format:
 			if self.__verbose is not None and self.__verbose > 2: # pragma: no cover
-				fmt = getVerbosityFormat(self.__verbose-1)
+				fmt = getVerbosityFormat(self.__verbose-1,
+					addtime=addtime)
+			elif addtime:
+				fmt = "%(asctime)s " + fmt
 			# tell the handler to use this format
 			hdlr.setFormatter(logging.Formatter(fmt))
 			logger.addHandler(hdlr)
diff --git a/files/bash-completion b/files/bash-completion
index 36e0cbba..b8887001 100644
--- a/files/bash-completion
+++ b/files/bash-completion
@@ -108,7 +108,7 @@ _fail2ban () {
                     ;;
                 logtarget)
                     if [[ "$cmd" == "set" ]];then
-                        COMPREPLY=( $( compgen -W "STDOUT STDERR SYSLOG" -- "$cur" ) )
+                        COMPREPLY=( $( compgen -W "STDOUT STDERR SYSLOG SYSOUT" -- "$cur" ) )
                         _filedir # And files
                     fi
                     return 0
diff --git a/files/fail2ban.service.in b/files/fail2ban.service.in
index 37ae4f07..24dcb51e 100644
--- a/files/fail2ban.service.in
+++ b/files/fail2ban.service.in
@@ -8,8 +8,8 @@ PartOf=iptables.service firewalld.service ip6tables.service ipset.service
 Type=simple
 ExecStartPre=/bin/mkdir -p /var/run/fail2ban
 ExecStart=@BINDIR@/fail2ban-server -xf start
-# if should be logged in systemd journal, use following line or set logtarget to stdout in fail2ban.local
-# ExecStart=@BINDIR@/fail2ban-server -xf --logtarget=stdout start
+# if should be logged in systemd journal, use following line or set logtarget to sysout in fail2ban.local
+# ExecStart=@BINDIR@/fail2ban-server -xf --logtarget=sysout start
 ExecStop=@BINDIR@/fail2ban-client stop
 ExecReload=@BINDIR@/fail2ban-client reload
 PIDFile=/var/run/fail2ban/fail2ban.pid
diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1
index 26e5ee59..6e44b387 100644
--- a/man/fail2ban-client.1
+++ b/man/fail2ban-client.1
@@ -21,8 +21,9 @@ pidfile path
 .TP
 \fB\-\-loglevel\fR <LEVEL>
 logging level
-.HP
-\fB\-\-logtarget\fR <FILE>|STDOUT|STDERR|SYSLOG
+.TP
+\fB\-\-logtarget\fR <TARGET>
+logging target, use file\-name or stdout, stderr, syslog or sysout.
 .HP
 \fB\-\-syslogsocket\fR auto|<FILE>
 .TP
diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1
index cb71e288..aca3eeb0 100644
--- a/man/fail2ban-server.1
+++ b/man/fail2ban-server.1
@@ -21,8 +21,9 @@ pidfile path
 .TP
 \fB\-\-loglevel\fR <LEVEL>
 logging level
-.HP
-\fB\-\-logtarget\fR <FILE>|STDOUT|STDERR|SYSLOG
+.TP
+\fB\-\-logtarget\fR <TARGET>
+logging target, use file\-name or stdout, stderr, syslog or sysout.
 .HP
 \fB\-\-syslogsocket\fR auto|<FILE>
 .TP

From af0f7e93ce3f9b2879e43f544b01464a3448c211 Mon Sep 17 00:00:00 2001
From: sebres <info@sebres.de>
Date: Sun, 26 Nov 2017 23:06:35 +0100
Subject: [PATCH 2/3] better handling by start/stop of server in foreground
 mode; don't call logging.shutdown because part of exit in fail2bancmdline.

---
 fail2ban/server/server.py | 51 ++++++++++++++++++---------------------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py
index ce28e3f9..9cb6f320 100644
--- a/fail2ban/server/server.py
+++ b/fail2ban/server/server.py
@@ -151,24 +151,13 @@ class Server:
 			self.__asyncServer.start(sock, force)
 		except AsyncServerException as e:
 			logSys.error("Could not start server: %s", e)
-		# Removes the PID file.
-		try:
-			logSys.debug("Remove PID file %s", pidfile)
-			os.remove(pidfile)
-		except (OSError, IOError) as e: # pragma: no cover
-			logSys.error("Unable to remove PID file: %s", e)
-		logSys.info("Exiting Fail2ban")
-	
-	def quit(self):
-		# Stop communication first because if jail's unban action
-		# tries to communicate via fail2ban-client we get a lockup
-		# among threads.  So the simplest resolution is to stop all
-		# communications first (which should be ok anyways since we
-		# are exiting)
-		# See https://github.com/fail2ban/fail2ban/issues/7
-		if self.__asyncServer is not None:
-			self.__asyncServer.stop()
-			self.__asyncServer = None
+
+		logSys.info("Shutdown in progress...")
+
+		# Restore default signal handlers:
+		if _thread_name() == '_MainThread':
+			for s, sh in self.__prev_signals.iteritems():
+				signal.signal(s, sh)
 
 		# Now stop all the jails
 		self.stopAllJail()
@@ -179,18 +168,26 @@ class Server:
 			self.__db.close()
 			self.__db = None
 
-		# Only now shutdown the logging.
-		if self.__logTarget is not None:
-			with self.__loggingLock:
-				logging.shutdown()
-
-		# Restore default signal handlers:
-		if _thread_name() == '_MainThread':
-			for s, sh in self.__prev_signals.iteritems():
-				signal.signal(s, sh)
+		# Removes the PID file.
+		try:
+			logSys.debug("Remove PID file %s", pidfile)
+			os.remove(pidfile)
+		except (OSError, IOError) as e: # pragma: no cover
+			logSys.error("Unable to remove PID file: %s", e)
+		logSys.info("Exiting Fail2ban")
 
+	def quit(self):
 		# Prevent to call quit twice:
 		self.quit = lambda: False
+		# Stop communication first because if jail's unban action
+		# tries to communicate via fail2ban-client we get a lockup
+		# among threads.  So the simplest resolution is to stop all
+		# communications first (which should be ok anyways since we
+		# are exiting)
+		# See https://github.com/fail2ban/fail2ban/issues/7
+		if self.__asyncServer is not None:
+			self.__asyncServer.stop()
+			self.__asyncServer = None
 
 	def addJail(self, name, backend):
 		addflg = True

From 6db9ae857468835e38426290cb6e194f813bf0b4 Mon Sep 17 00:00:00 2001
From: sebres <info@sebres.de>
Date: Sun, 26 Nov 2017 23:35:11 +0100
Subject: [PATCH 3/3] ChangeLog updated

---
 ChangeLog | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 44834880..9abafd79 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -39,6 +39,8 @@ ver. 0.10.2-dev-1 (2017/??/??) - development edition
   ports are enclosed in curly braces `{ }` in the `jail.local` etc. This may cause a double-brackets now.
 
 ### Fixes
+* Fixed logging to systemd-journal: new logtarget value SYSOUT can be used instead of STDOUT, to avoid 
+  write of the time-stamp, if logging to systemd-journal from foreground mode (gh-1876)
 * jail.conf: port `imap3` replaced with `imap` everywhere, since imap3 is not a standard port and old rarely 
   (if ever) used and can missing on some systems (e. g. debian stretch), see gh-1942.
 * config/paths-common.conf: added missing initial values (and small normalization in config/paths-*.conf)