Merge remote-tracking branch 'pr/167/head': FD_CLOEXEC bug fixes (filters) + support (actions). Avoid sockets descriptors leak.

* pr/167/head:
  FD_CLOEXEC support
pull/172/merge
Yaroslav Halchenko 2013-04-11 15:05:56 -04:00
commit 301460f451
3 changed files with 21 additions and 3 deletions

View File

@ -29,7 +29,7 @@ __license__ = "GPL"
from pickle import dumps, loads, HIGHEST_PROTOCOL
from common import helpers
import asyncore, asynchat, socket, os, logging, sys, traceback
import asyncore, asynchat, socket, os, logging, sys, traceback, fcntl
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.server")
@ -107,6 +107,7 @@ class AsyncServer(asyncore.dispatcher):
except TypeError:
logSys.warning("Type error")
return
AsyncServer.__markCloseOnExec(conn)
# Creates an instance of the handler class to handle the
# request/response on the incoming connection.
RequestHandler(conn, self.__transmitter)
@ -134,6 +135,7 @@ class AsyncServer(asyncore.dispatcher):
self.bind(sock)
except Exception:
raise AsyncServerException("Unable to bind socket %s" % self.__sock)
AsyncServer.__markCloseOnExec(self.socket)
self.listen(1)
# Sets the init flag.
self.__init = True
@ -159,6 +161,18 @@ class AsyncServer(asyncore.dispatcher):
os.remove(self.__sock)
logSys.debug("Socket shutdown")
##
# Marks socket as close-on-exec to avoid leaking file descriptors when
# running actions involving command execution.
# @param sock: socket file.
#@staticmethod
def __markCloseOnExec(sock):
fd = sock.fileno()
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
__markCloseOnExec = staticmethod(__markCloseOnExec)
##
# AsyncServerException is used to wrap communication exceptions.

View File

@ -554,7 +554,8 @@ class FileContainer:
self.__handler = open(self.__filename)
# Set the file descriptor to be FD_CLOEXEC
fd = self.__handler.fileno()
fcntl.fcntl(fd, fcntl.F_SETFD, fd | fcntl.FD_CLOEXEC)
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
firstLine = self.__handler.readline()
# Computes the MD5 of the first line.
myHash = md5sum(firstLine).digest()

View File

@ -27,7 +27,7 @@ from failmanager import FailManagerEmpty
from filter import FileFilter
from mytime import MyTime
import time, logging, gamin
import time, logging, gamin, fcntl
# Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter")
@ -52,6 +52,9 @@ class FilterGamin(FileFilter):
self.__modified = False
# Gamin monitor
self.monitor = gamin.WatchMonitor()
fd = self.monitor.get_fd()
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
logSys.debug("Created FilterGamin")