@ -27,12 +27,14 @@ __license__ = "GPL"
from pickle import dumps , loads , HIGHEST_PROTOCOL
from pickle import dumps , loads , HIGHEST_PROTOCOL
import asynchat
import asynchat
import asyncore
import asyncore
import errno
import fcntl
import fcntl
import os
import os
import socket
import socket
import sys
import sys
import traceback
import traceback
from . utils import Utils
from . . protocol import CSPROTO
from . . protocol import CSPROTO
from . . helpers import getLogger , formatExceptionInfo
from . . helpers import getLogger , formatExceptionInfo
@ -89,6 +91,29 @@ class RequestHandler(asynchat.async_chat):
self . close ( )
self . close ( )
def loop ( active , timeout = None , use_poll = False ) :
# Use poll instead of loop, because of recognition of active flag,
# because of loop timeout mistake: different in poll and poll2 (sec vs ms),
# and to prevent sporadical errors like EBADF 'Bad file descriptor' etc. (see gh-161)
if timeout is None :
timeout = Utils . DEFAULT_SLEEP_TIME
poll = asyncore . poll
if use_poll and asyncore . poll2 and hasattr ( asyncore . select , ' poll ' ) : # pragma: no cover
logSys . debug ( ' Server listener (select) uses poll ' )
# poll2 expected a timeout in milliseconds (but poll and loop in seconds):
timeout = float ( timeout ) / 1000
poll = asyncore . poll2
# Poll as long as active:
while active ( ) :
try :
poll ( timeout )
except Exception as e : # pragma: no cover
if e . args [ 0 ] in ( errno . ENOTCONN , errno . EBADF ) : # (errno.EBADF, 'Bad file descriptor')
logSys . info ( ' Server connection was closed: %s ' , str ( e ) )
else :
logSys . error ( ' Server connection was closed: %s ' , str ( e ) )
##
##
# Asynchronous server class.
# Asynchronous server class.
#
#
@ -102,6 +127,7 @@ class AsyncServer(asyncore.dispatcher):
self . __transmitter = transmitter
self . __transmitter = transmitter
self . __sock = " /var/run/fail2ban/fail2ban.sock "
self . __sock = " /var/run/fail2ban/fail2ban.sock "
self . __init = False
self . __init = False
self . __active = False
##
##
# Returns False as we only read the socket first.
# Returns False as we only read the socket first.
@ -129,7 +155,7 @@ class AsyncServer(asyncore.dispatcher):
# @param sock: socket file.
# @param sock: socket file.
# @param force: remove the socket file if exists.
# @param force: remove the socket file if exists.
def start ( self , sock , force ):
def start ( self , sock , force , use_poll = False ):
self . __sock = sock
self . __sock = sock
# Remove socket
# Remove socket
if os . path . exists ( sock ) :
if os . path . exists ( sock ) :
@ -149,28 +175,31 @@ class AsyncServer(asyncore.dispatcher):
AsyncServer . __markCloseOnExec ( self . socket )
AsyncServer . __markCloseOnExec ( self . socket )
self . listen ( 1 )
self . listen ( 1 )
# Sets the init flag.
# Sets the init flag.
self . __init = True
self . __init = self . __active = True
# TODO Add try..catch
# Event loop as long as active:
# There's a bug report for Python 2.6/3.0 that use_poll=True yields some 2.5 incompatibilities:
loop ( lambda : self . __active )
if ( sys . version_info > = ( 2 , 7 ) and sys . version_info < ( 2 , 8 ) ) \
# Cleanup all
or ( sys . version_info > = ( 3 , 4 ) ) : # if python 2.7 ...
self . stop ( )
logSys . debug ( " Detected Python 2.7. asyncore.loop() using poll " )
asyncore . loop ( use_poll = True ) # workaround for the "Bad file descriptor" issue on Python 2.7, gh-161
else :
def close ( self ) :
asyncore . loop ( use_poll = False ) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
if self . __active :
asyncore . dispatcher . close ( self )
# Remove socket (file) only if it was created:
if self . __init and os . path . exists ( self . __sock ) :
logSys . debug ( " Removed socket file " + self . __sock )
os . remove ( self . __sock )
logSys . debug ( " Socket shutdown " )
self . __active = False
##
##
# Stops the communication server.
# Stops the communication server.
def stop ( self ) :
def stop ( self ) :
if self . __init :
self . close ( )
# Only closes the socket if it was initialized first.
self . close ( )
def isActive ( self ) :
# Remove socket
return self . __active
if os . path . exists ( self . __sock ) :
logSys . debug ( " Removed socket file " + self . __sock )
os . remove ( self . __sock )
logSys . debug ( " Socket shutdown " )
##
##
# Marks socket as close-on-exec to avoid leaking file descriptors when
# Marks socket as close-on-exec to avoid leaking file descriptors when