From 704a0b834f8a55240e2db1e4fc15bb3d434c2489 Mon Sep 17 00:00:00 2001 From: Cyril Jaquier Date: Wed, 23 Aug 2006 19:53:09 +0000 Subject: [PATCH] - Fixed startup and daemon mode git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@300 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- fail2ban-server | 16 ++++---- server/server.py | 91 ++++++++++++++++++++++++++++++++++++++++++- server/transmitter.py | 8 +++- 3 files changed, 103 insertions(+), 12 deletions(-) diff --git a/fail2ban-server b/fail2ban-server index e72f9e9c..67a14144 100755 --- a/fail2ban-server +++ b/fail2ban-server @@ -101,20 +101,18 @@ class Fail2banServer: self.getCmdLineOptions(optList) - if self.conf["background"]: - retCode = createDaemon() - #signal.signal(signal.SIGTERM, sigTERMhandler) - if not retCode: - print "Unable to start daemon" - sys.exit(-1) - try: - self.server = Server() + self.server = Server(self.conf["background"]) self.server.start(self.conf["force"]) + return True except Exception, e: print e self.server.quit() + return False if __name__ == "__main__": server = Fail2banServer() - server.start(sys.argv) + if server.start(sys.argv): + sys.exit(0) + else: + sys.exit(-1) diff --git a/server/server.py b/server/server.py index 66a0ce9b..b4430252 100644 --- a/server/server.py +++ b/server/server.py @@ -26,15 +26,16 @@ __license__ = "GPL" from jail import Jail from transmitter import Transmitter -import locale, logging, sys +import locale, logging, sys, os, signal # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.server") class Server: - def __init__(self): + def __init__(self, daemon = False): self.jails = dict() + self.daemon = daemon self.transm = Transmitter(self) self.logLevel = 3 self.logTarget = "STDERR" @@ -43,8 +44,18 @@ class Server: self.setLogTarget(self.logTarget) def start(self, force): + logSys.info("Starting Fail2ban") + if self.daemon: + ret = self.createDaemon() + if ret: + logSys.info("Daemon started") + else: + logSys.error("Could not create daemon") + raise ServerInitializationError("Could not create daemon") # Start the communication + logSys.debug("Starting communication") self.transm.start(force) + logSys.info("Exiting Fail2ban") def quit(self): self.stopAllJail() @@ -354,6 +365,82 @@ class Server: def getLogTarget(self): return self.logTarget + + def createDaemon(self): + """ Detach a process from the controlling terminal and run it in the + background as a daemon. + + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 + """ + + try: + # Fork a child process so the parent can exit. This will return control + # to the command line or shell. This is required so that the new process + # is guaranteed not to be a process group leader. We have this guarantee + # because the process GID of the parent is inherited by the child, but + # the child gets a new PID, making it impossible for its PID to equal its + # PGID. + pid = os.fork() + except OSError, e: + return((e.errno, e.strerror)) # ERROR (return a tuple) + + if pid == 0: # The first child. + + # Next we call os.setsid() to become the session leader of this new + # session. The process also becomes the process group leader of the + # new process group. Since a controlling terminal is associated with a + # session, and this new session has not yet acquired a controlling + # terminal our process now has no controlling terminal. This shouldn't + # fail, since we're guaranteed that the child is not a process group + # leader. + os.setsid() + + # When the first child terminates, all processes in the second child + # are sent a SIGHUP, so it's ignored. + signal.signal(signal.SIGHUP, signal.SIG_IGN) + + try: + # Fork a second child to prevent zombies. Since the first child is + # a session leader without a controlling terminal, it's possible for + # it to acquire one by opening a terminal in the future. This second + # fork guarantees that the child is no longer a session leader, thus + # preventing the daemon from ever acquiring a controlling terminal. + pid = os.fork() # Fork a second child. + except OSError, e: + return((e.errno, e.strerror)) # ERROR (return a tuple) + + if (pid == 0): # The second child. + # Ensure that the daemon doesn't keep any directory in use. Failure + # to do this could make a filesystem unmountable. + os.chdir("/") + else: + os._exit(0) # Exit parent (the first child) of the second child. + else: + os._exit(0) # Exit parent of the first child. + + # Close all open files. Try the system configuration variable, SC_OPEN_MAX, + # for the maximum number of open files to close. If it doesn't exist, use + # the default value (configurable). + try: + maxfd = os.sysconf("SC_OPEN_MAX") + except (AttributeError, ValueError): + maxfd = 256 # default maximum + + for fd in range(0, maxfd): + try: + os.close(fd) + except OSError: # ERROR (ignore) + pass + + # Redirect the standard file descriptors to /dev/null. + os.open("/dev/null", os.O_RDONLY) # standard input (0) + os.open("/dev/null", os.O_RDWR) # standard output (1) + os.open("/dev/null", os.O_RDWR) # standard error (2) + return True + class ServerUnknownJail(Exception): + pass + +class ServerInitializationError(Exception): pass \ No newline at end of file diff --git a/server/transmitter.py b/server/transmitter.py index 5dad10eb..6b1de4aa 100644 --- a/server/transmitter.py +++ b/server/transmitter.py @@ -37,10 +37,16 @@ class Transmitter: self.server = server self.socket = SSocket(self) + ## + # Start the transmittion server. + # + # This function wait for the socket thread. + def start(self, force): try: self.socket.initialize(force) self.socket.start() + self.socket.join() except SSocketErrorException: logSys.error("Could not start server") @@ -61,7 +67,7 @@ class Transmitter: ret = self.actionHandler(action) ack = 0, ret except Exception, e: - logSys.warn("Invalid command") + logSys.warn("Invalid command: " + `action`) ack = 1, e return ack