diff --git a/fail2ban/client/fail2bancmdline.py b/fail2ban/client/fail2bancmdline.py
index 1268ee9f..53c86de6 100644
--- a/fail2ban/client/fail2bancmdline.py
+++ b/fail2ban/client/fail2bancmdline.py
@@ -35,7 +35,8 @@ logSys = getLogger("fail2ban")
def output(s): # pragma: no cover
print(s)
-CONFIG_PARAMS = ("socket", "pidfile", "logtarget", "loglevel", "syslogsocket",)
+# Config parameters required to start fail2ban which can be also set via command line (overwrite fail2ban.conf),
+CONFIG_PARAMS = ("socket", "pidfile", "logtarget", "loglevel", "syslogsocket")
# Used to signal - we are in test cases (ex: prevents change logging params, log capturing, etc)
PRODUCTION = True
@@ -94,9 +95,10 @@ class Fail2banCmdLine():
output("and bans the corresponding IP addresses using firewall rules.")
output("")
output("Options:")
- output(" -c
configuration directory")
- output(" -s socket path")
- output(" -p pidfile path")
+ output(" -c, --conf configuration directory")
+ output(" -s, --socket socket path")
+ output(" -p, --pidfile pidfile path")
+ output(" --pname name of the process (main thread) to identify instance (default fail2ban-server)")
output(" --loglevel logging level")
output(" --logtarget logging target, use file-name or stdout, stderr, syslog or sysout.")
output(" --syslogsocket auto|")
@@ -129,17 +131,15 @@ class Fail2banCmdLine():
"""
for opt in optList:
o = opt[0]
- if o == "-c":
+ if o in ("-c", "--conf"):
self._conf["conf"] = opt[1]
- elif o == "-s":
+ elif o in ("-s", "--socket"):
self._conf["socket"] = opt[1]
- elif o == "-p":
+ elif o in ("-p", "--pidfile"):
self._conf["pidfile"] = opt[1]
- elif o.startswith("--log") or o.startswith("--sys"):
- self._conf[ o[2:] ] = opt[1]
- elif o in ["-d", "--dp", "--dump-pretty"]:
+ elif o in ("-d", "--dp", "--dump-pretty"):
self._conf["dump"] = True if o == "-d" else 2
- elif o == "-t" or o == "--test":
+ elif o in ("-t", "--test"):
self.cleanConfOnly = True
self._conf["test"] = True
elif o == "-v":
@@ -163,12 +163,14 @@ class Fail2banCmdLine():
from ..server.mytime import MyTime
output(MyTime.str2seconds(opt[1]))
return True
- elif o in ["-h", "--help"]:
+ elif o in ("-h", "--help"):
self.dispUsage()
return True
- elif o in ["-V", "--version"]:
+ elif o in ("-V", "--version"):
self.dispVersion(o == "-V")
return True
+ elif o.startswith("--"): # other long named params (see also resetConf)
+ self._conf[ o[2:] ] = opt[1]
return None
def initCmdLine(self, argv):
@@ -185,6 +187,7 @@ class Fail2banCmdLine():
try:
cmdOpts = 'hc:s:p:xfbdtviqV'
cmdLongOpts = ['loglevel=', 'logtarget=', 'syslogsocket=', 'test', 'async',
+ 'conf=', 'pidfile=', 'pname=', 'socket=',
'timeout=', 'str2sec=', 'help', 'version', 'dp', '--dump-pretty']
optList, self._args = getopt.getopt(self._argv[1:], cmdOpts, cmdLongOpts)
except getopt.GetoptError:
@@ -227,7 +230,8 @@ class Fail2banCmdLine():
if not conf:
self.configurator.readEarly()
conf = self.configurator.getEarlyOptions()
- self._conf[o] = conf[o]
+ if o in conf:
+ self._conf[o] = conf[o]
logSys.info("Using socket file %s", self._conf["socket"])
diff --git a/fail2ban/client/fail2banregex.py b/fail2ban/client/fail2banregex.py
index 334c031f..513b765d 100644
--- a/fail2ban/client/fail2banregex.py
+++ b/fail2ban/client/fail2banregex.py
@@ -709,6 +709,7 @@ class Fail2banRegex(object):
def exec_command_line(*args):
+ logging.exitOnIOError = True
parser = get_opt_parser()
(opts, args) = parser.parse_args(*args)
errors = []
diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py
index 241543c1..6f2bcdd7 100644
--- a/fail2ban/helpers.py
+++ b/fail2ban/helpers.py
@@ -208,6 +208,25 @@ class FormatterWithTraceBack(logging.Formatter):
return logging.Formatter.format(self, record)
+logging.exitOnIOError = False
+def __stopOnIOError(logSys=None, logHndlr=None): # pragma: no cover
+ if logSys and len(logSys.handlers):
+ logSys.removeHandler(logSys.handlers[0])
+ if logHndlr:
+ logHndlr.close = lambda: None
+ logging.StreamHandler.flush = lambda self: None
+ #sys.excepthook = lambda *args: None
+ if logging.exitOnIOError:
+ try:
+ sys.stderr.close()
+ except:
+ pass
+ sys.exit(0)
+
+try:
+ BrokenPipeError
+except NameError: # pragma: 3.x no cover
+ BrokenPipeError = IOError
__origLog = logging.Logger._log
def __safeLog(self, level, msg, args, **kwargs):
"""Safe log inject to avoid possible errors by unsafe log-handlers,
@@ -223,6 +242,10 @@ def __safeLog(self, level, msg, args, **kwargs):
try:
# if isEnabledFor(level) already called...
__origLog(self, level, msg, args, **kwargs)
+ except (BrokenPipeError, IOError) as e: # pragma: no cover
+ if e.errno == 32: # closed / broken pipe
+ __stopOnIOError(self)
+ raise
except Exception as e: # pragma: no cover - unreachable if log-handler safe in this python-version
try:
for args in (
@@ -237,6 +260,18 @@ def __safeLog(self, level, msg, args, **kwargs):
pass
logging.Logger._log = __safeLog
+__origLogFlush = logging.StreamHandler.flush
+def __safeLogFlush(self):
+ """Safe flush inject stopping endless logging on closed streams (redirected pipe).
+ """
+ try:
+ __origLogFlush(self)
+ except (BrokenPipeError, IOError) as e: # pragma: no cover
+ if e.errno == 32: # closed / broken pipe
+ __stopOnIOError(None, self)
+ raise
+logging.StreamHandler.flush = __safeLogFlush
+
def getLogger(name):
"""Get logging.Logger instance with Fail2Ban logger name convention
"""
diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py
index feb3b399..159f6506 100644
--- a/fail2ban/server/server.py
+++ b/fail2ban/server/server.py
@@ -81,8 +81,6 @@ class Server:
'Linux': '/dev/log',
}
self.__prev_signals = {}
- # replace real thread name with short process name (for top/ps/pstree or diagnostic):
- prctl_set_th_name('f2b/server')
def __sigTERMhandler(self, signum, frame): # pragma: no cover - indirect tested
logSys.debug("Caught signal %d. Exiting", signum)
@@ -113,6 +111,9 @@ class Server:
logSys.error(err)
raise ServerInitializationError(err)
# We are daemon.
+
+ # replace main thread (and process) name to identify server (for top/ps/pstree or diagnostic):
+ prctl_set_th_name(conf.get("pname", "fail2ban-server"))
# Set all logging parameters (or use default if not specified):
self.__verbose = conf.get("verbose", None)
diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1
index 745c080a..259a98a0 100644
--- a/man/fail2ban-client.1
+++ b/man/fail2ban-client.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
-.TH FAIL2BAN-CLIENT "1" "January 2020" "fail2ban-client v0.11.1" "User Commands"
+.TH FAIL2BAN-CLIENT "1" "February 2020" "fail2ban-client v0.11.1" "User Commands"
.SH NAME
fail2ban-client \- configure and control the server
.SH SYNOPSIS
@@ -10,15 +10,18 @@ Fail2Ban v0.11.1 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS
.TP
-\fB\-c\fR
+\fB\-c\fR, \fB\-\-conf\fR
configuration directory
.TP
-\fB\-s\fR
+\fB\-s\fR, \fB\-\-socket\fR
socket path
.TP
-\fB\-p\fR
+\fB\-p\fR, \fB\-\-pidfile\fR
pidfile path
.TP
+\fB\-\-pname\fR
+name of the process (main thread) to identify instance (default fail2ban\-server)
+.TP
\fB\-\-loglevel\fR
logging level
.TP
diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1
index 418b46dd..e2b31dbd 100644
--- a/man/fail2ban-server.1
+++ b/man/fail2ban-server.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4.
-.TH FAIL2BAN-SERVER "1" "January 2020" "fail2ban-server v0.11.1" "User Commands"
+.TH FAIL2BAN-SERVER "1" "February 2020" "fail2ban-server v0.11.1" "User Commands"
.SH NAME
fail2ban-server \- start the server
.SH SYNOPSIS
@@ -10,15 +10,18 @@ Fail2Ban v0.11.1 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS
.TP
-\fB\-c\fR
+\fB\-c\fR, \fB\-\-conf\fR
configuration directory
.TP
-\fB\-s\fR
+\fB\-s\fR, \fB\-\-socket\fR
socket path
.TP
-\fB\-p\fR
+\fB\-p\fR, \fB\-\-pidfile\fR
pidfile path
.TP
+\fB\-\-pname\fR
+name of the process (main thread) to identify instance (default fail2ban\-server)
+.TP
\fB\-\-loglevel\fR
logging level
.TP