From 44490664f5616046eca504aab3f4b78f3fdadf23 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 9 Feb 2016 14:23:40 +0100 Subject: [PATCH] try to start server in foreground # Conflicts: # fail2ban/server/server.py --- bin/fail2ban-client | 117 ++++++++++++++++++++++-------- bin/fail2ban-server | 3 +- fail2ban/client/fail2banreader.py | 9 ++- fail2ban/server/server.py | 18 ++--- 4 files changed, 106 insertions(+), 41 deletions(-) diff --git a/bin/fail2ban-client b/bin/fail2ban-client index 7f3f5639..4a1bab39 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() @@ -396,7 +445,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 3bdfd71b..923b6ba3 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 @@ -392,8 +393,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": @@ -461,8 +463,6 @@ class Server: # Sets the logging target. self.__logTarget = target return True - finally: - self.__loggingLock.release() ## # Sets the syslog socket.