diff --git a/bin/fail2ban-client b/bin/fail2ban-client
index bc0c0be8..631afb31 100755
--- a/bin/fail2ban-client
+++ b/bin/fail2ban-client
@@ -54,6 +54,7 @@ class Fail2banClient:
PROMPT = "fail2ban> "
def __init__(self):
+ self.__server = None
self.__argv = None
self.__stream = None
self.__configurator = Configurator()
@@ -89,13 +90,16 @@ class Fail2banClient:
print " -c
configuration directory"
print " -s socket path"
print " -p pidfile path"
+ print " --loglevel logging level"
+ print " --logtarget |STDOUT|STDERR|SYSLOG"
+ print " --syslogsocket auto|file"
print " -d dump configuration. For debugging"
print " -i interactive mode"
print " -v increase verbosity"
print " -q decrease verbosity"
print " -x force execution of the server (remove socket file)"
print " -b start server in background (default)"
- print " -f start server in foreground (note that the client forks once itself)"
+ print " -f start server in foreground"
print " -h, --help display this help message"
print " -V, --version print the version"
print
@@ -128,6 +132,8 @@ class Fail2banClient:
self.__conf["socket"] = opt[1]
elif opt[0] == "-p":
self.__conf["pidfile"] = opt[1]
+ elif opt[0].startswith("--log") or opt[0].startswith("--sys"):
+ self.__conf[ opt[0][2:] ] = opt[1]
elif opt[0] == "-d":
self.__conf["dump"] = True
elif opt[0] == "-v":
@@ -234,24 +240,32 @@ class Fail2banClient:
"Directory %s exists but not accessible for writing"
% (socket_dir,))
return False
- # Start the server
- self.__startServerAsync(self.__conf["socket"],
- self.__conf["pidfile"],
- self.__conf["force"],
- self.__conf["background"])
- try:
- # Wait for the server to start
- self.__waitOnServer()
- # Configure the server
- self.__processCmd(self.__stream, False)
- return True
- except ServerExecutionException:
- logSys.error("Could not start server. Maybe an old "
- "socket file is still present. Try to "
- "remove " + self.__conf["socket"] + ". If "
- "you used fail2ban-client to start the "
- "server, adding the -x option will do it")
+
+ # Check already running
+ if not self.__conf["force"] and os.path.exists(self.__conf["socket"]):
+ logSys.error("Fail2ban seems to be in unexpected state (not running but socket exists)")
return False
+
+ # Start the server
+ t = None
+ if self.__conf["background"]:
+ # Start server daemon as fork of client process:
+ self.__startServerAsync()
+ # Send config stream to server:
+ return self.__processStartStreamAfterWait()
+ else:
+ # In foreground mode we should start server/client communication in other thread:
+ from threading import Thread
+ t = Thread(target=Fail2banClient.__processStartStreamAfterWait, args=(self,))
+ t.start()
+ # Start server direct here in main thread:
+ try:
+ self.__startServerDirect()
+ except KeyboardInterrupt:
+ None
+
+ return True
+
elif len(cmd) == 1 and cmd[0] == "reload":
if self.__ping():
ret = self.__readConfig()
@@ -281,12 +295,50 @@ class Fail2banClient:
return self.__processCmd([cmd])
+ def __processStartStreamAfterWait(self):
+ try:
+ # Wait for the server to start
+ self.__waitOnServer()
+ # Configure the server
+ self.__processCmd(self.__stream, False)
+ except ServerExecutionException:
+ logSys.error("Could not start server. Maybe an old "
+ "socket file is still present. Try to "
+ "remove " + self.__conf["socket"] + ". If "
+ "you used fail2ban-client to start the "
+ "server, adding the -x option will do it")
+ if not self.__conf["background"]:
+ self.__server.quit()
+ sys.exit(-1)
+ return False
+ return True
+
+
+ ##
+ # Start Fail2Ban server in main thread without fork (foreground).
+ #
+ # Start the Fail2ban server in foreground (daemon mode or not).
+
+ def __startServerDirect(self):
+ from fail2ban.server.server import Server
+ try:
+ self.__server = Server(False)
+ self.__server.start(self.__conf["socket"],
+ self.__conf["pidfile"], self.__conf["force"],
+ conf=self.__conf)
+ except Exception, e:
+ logSys.exception(e)
+ if self.__server:
+ self.__server.quit()
+ sys.exit(-1)
+
+
##
# Start Fail2Ban server.
#
# Start the Fail2ban server in daemon mode.
- def __startServerAsync(self, socket, pidfile, force = False, background = True):
+ def __startServerAsync(self):
# Forks the current process.
pid = os.fork()
if pid == 0:
@@ -294,18 +346,15 @@ class Fail2banClient:
args.append(self.SERVER)
# Set the socket path.
args.append("-s")
- args.append(socket)
+ args.append(self.__conf["socket"])
# Set the pidfile
args.append("-p")
- args.append(pidfile)
+ args.append(self.__conf["pidfile"])
# Force the execution if needed.
- if force:
+ if self.__conf["force"]:
args.append("-x")
- # Start in foreground mode if requested.
- if background:
- args.append("-b")
- else:
- args.append("-f")
+ # Start in background as requested.
+ args.append("-b")
try:
# Use the current directory.
@@ -361,7 +410,7 @@ class Fail2banClient:
# Reads the command line options.
try:
cmdOpts = 'hc:s:p:xfbdviqV'
- cmdLongOpts = ['help', 'version']
+ cmdLongOpts = ['loglevel', 'logtarget', 'syslogsocket', 'help', 'version']
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
except getopt.GetoptError:
self.dispUsage()
@@ -398,7 +447,17 @@ class Fail2banClient:
self.__conf["socket"] = conf["socket"]
if self.__conf["pidfile"] is None:
self.__conf["pidfile"] = conf["pidfile"]
- logSys.info("Using socket file " + self.__conf["socket"])
+ if self.__conf.get("logtarget", None) is None:
+ self.__conf["logtarget"] = conf["logtarget"]
+ if self.__conf.get("loglevel", None) is None:
+ self.__conf["loglevel"] = conf["loglevel"]
+ if self.__conf.get("syslogsocket", None) is None:
+ self.__conf["syslogsocket"] = conf["syslogsocket"]
+
+ logSys.info("Using socket file %s", self.__conf["socket"])
+
+ logSys.info("Using pid file %s, [%s] logging to %s",
+ self.__conf["pidfile"], self.__conf["loglevel"], self.__conf["logtarget"])
if self.__conf["dump"]:
ret = self.__readConfig()
diff --git a/bin/fail2ban-server b/bin/fail2ban-server
index f522f418..0b8b6418 100755
--- a/bin/fail2ban-server
+++ b/bin/fail2ban-server
@@ -129,7 +129,8 @@ class Fail2banServer:
return True
except Exception, e:
logSys.exception(e)
- self.__server.quit()
+ if self.__server:
+ self.__server.quit()
return False
if __name__ == "__main__":
diff --git a/fail2ban/client/fail2banreader.py b/fail2ban/client/fail2banreader.py
index c55f65ea..b3012c9b 100644
--- a/fail2ban/client/fail2banreader.py
+++ b/fail2ban/client/fail2banreader.py
@@ -40,8 +40,13 @@ class Fail2banReader(ConfigReader):
ConfigReader.read(self, "fail2ban")
def getEarlyOptions(self):
- opts = [["string", "socket", "/var/run/fail2ban/fail2ban.sock"],
- ["string", "pidfile", "/var/run/fail2ban/fail2ban.pid"]]
+ opts = [
+ ["string", "socket", "/var/run/fail2ban/fail2ban.sock"],
+ ["string", "pidfile", "/var/run/fail2ban/fail2ban.pid"],
+ ["string", "loglevel", "INFO"],
+ ["string", "logtarget", "/var/log/fail2ban.log"],
+ ["string", "syslogsocket", "auto"]
+ ]
return ConfigReader.getOptions(self, "Definition", opts)
def getOptions(self):
diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py
index 7f75c347..1d3e89d3 100644
--- a/fail2ban/server/server.py
+++ b/fail2ban/server/server.py
@@ -67,10 +67,6 @@ class Server:
'FreeBSD': '/var/run/log',
'Linux': '/dev/log',
}
- self.setSyslogSocket("auto")
- # Set logging level
- self.setLogLevel("INFO")
- self.setLogTarget("STDOUT")
def __sigTERMhandler(self, signum, frame):
logSys.debug("Caught signal %d. Exiting" % signum)
@@ -80,7 +76,12 @@ class Server:
logSys.debug("Caught signal %d. Flushing logs" % signum)
self.flushLogs()
- def start(self, sock, pidfile, force = False):
+ def start(self, sock, pidfile, force=False, conf={}):
+ # First set all logging parameters:
+ self.setSyslogSocket(conf.get("syslogsocket", "auto"))
+ self.setLogLevel(conf.get("loglevel", "INFO"))
+ self.setLogTarget(conf.get("logtarget", "STDOUT"))
+
logSys.info("Starting Fail2ban v%s", version.version)
# Install signal handlers
@@ -402,8 +403,9 @@ class Server:
# @param target the logging target
def setLogTarget(self, target):
- try:
- self.__loggingLock.acquire()
+ with self.__loggingLock:
+ if self.__logTarget == target:
+ return True
# set a format which is simpler for console use
formatter = logging.Formatter("%(asctime)s %(name)-24s[%(process)d]: %(levelname)-7s %(message)s")
if target == "SYSLOG":
@@ -471,8 +473,6 @@ class Server:
# Sets the logging target.
self.__logTarget = target
return True
- finally:
- self.__loggingLock.release()
##
# Sets the syslog socket.