<s> import errno <EOL> import logging <EOL> import os <EOL> import signal <EOL> import sys <EOL> import time <EOL> import traceback <EOL> import gevent <EOL> from gevent import select <EOL> from . import __version__ <EOL> from . import util <EOL> from . pidfile import Pidfile <EOL> from . proxy import tcp_listener <EOL> from . worker import Worker <EOL> class HaltServer ( Exception ) : <EOL> def __init__ ( self , reason , exit_status = <NUM_LIT:1> ) : <EOL> self . reason = reason <EOL> self . exit_status = exit_status <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" % ( self . reason , self . exit_status ) <EOL> class Arbiter ( object ) : <EOL> WORKER_BOOT_ERROR = <NUM_LIT:3> <EOL> START_CTX = { } <EOL> LISTENER = None <EOL> WORKERS = { } <EOL> PIPE = [ ] <EOL> SIG_QUEUE = [ ] <EOL> SIGNALS = map ( <EOL> lambda x : getattr ( signal , "<STR_LIT>" % x ) , <EOL> "<STR_LIT>" . split ( ) <EOL> ) <EOL> SIG_NAMES = dict ( <EOL> ( getattr ( signal , name ) , name [ <NUM_LIT:3> : ] . lower ( ) ) for name in dir ( signal ) <EOL> if name [ : <NUM_LIT:3> ] == "<STR_LIT>" and name [ <NUM_LIT:3> ] != "<STR_LIT:_>" <EOL> ) <EOL> def __init__ ( self , cfg , script ) : <EOL> self . cfg = cfg <EOL> self . script = script <EOL> self . num_workers = cfg . workers <EOL> self . address = cfg . address <EOL> self . timeout = cfg . timeout <EOL> self . name = cfg . name <EOL> self . pid = <NUM_LIT:0> <EOL> self . pidfile = None <EOL> self . worker_age = <NUM_LIT:0> <EOL> self . reexec_pid = <NUM_LIT:0> <EOL> self . master_name = "<STR_LIT>" <EOL> self . log = logging . getLogger ( __name__ ) <EOL> try : <EOL> a = os . stat ( os . environ [ '<STR_LIT>' ] ) <EOL> b = os . stat ( os . getcwd ( ) ) <EOL> if a . ino == b . ino and a . dev == b . dev : <EOL> cwd = os . environ [ '<STR_LIT>' ] <EOL> else : <EOL> cwd = os . getcwd ( ) <EOL> except : <EOL> cwd = os . getcwd ( ) <EOL> args = sys . argv [ : ] <EOL> args . insert ( <NUM_LIT:0> , sys . executable ) <EOL> self . START_CTX = { <EOL> "<STR_LIT:args>" : args , <EOL> "<STR_LIT>" : cwd , <EOL> <NUM_LIT:0> : sys . executable <EOL> } <EOL> def start ( self ) : <EOL> self . pid = os . getpid ( ) <EOL> self . init_signals ( ) <EOL> if not self . LISTENER : <EOL> self . LISTENER = tcp_listener ( self . address , self . cfg . backlog ) <EOL> if self . cfg . pidfile is not None : <EOL> self . pidfile = Pidfile ( self . cfg . pidfile ) <EOL> self . pidfile . create ( self . pid ) <EOL> util . _setproctitle ( "<STR_LIT>" % self . name ) <EOL> self . log . info ( "<STR_LIT>" % __version__ ) <EOL> self . log . info ( "<STR_LIT>" % self . address ) <EOL> def init_signals ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . PIPE : <EOL> map ( os . close , self . PIPE ) <EOL> self . PIPE = pair = os . pipe ( ) <EOL> map ( util . set_non_blocking , pair ) <EOL> map ( util . close_on_exec , pair ) <EOL> map ( lambda s : signal . signal ( s , self . signal ) , self . SIGNALS ) <EOL> signal . signal ( signal . SIGCHLD , self . handle_chld ) <EOL> def signal ( self , sig , frame ) : <EOL> if len ( self . SIG_QUEUE ) < <NUM_LIT:5> : <EOL> self . SIG_QUEUE . append ( sig ) <EOL> self . wakeup ( ) <EOL> else : <EOL> self . log . warn ( "<STR_LIT>" % sig ) <EOL> def run ( self ) : <EOL> self . start ( ) <EOL> self . manage_workers ( ) <EOL> while True : <EOL> try : <EOL> self . reap_workers ( ) <EOL> sig = self . SIG_QUEUE . pop ( <NUM_LIT:0> ) if len ( self . SIG_QUEUE ) else None <EOL> if sig is None : <EOL> self . sleep ( ) <EOL> self . murder_workers ( ) <EOL> self . manage_workers ( ) <EOL> continue <EOL> if sig not in self . SIG_NAMES : <EOL> self . log . info ( "<STR_LIT>" % sig ) <EOL> continue <EOL> signame = self . SIG_NAMES . get ( sig ) <EOL> handler = getattr ( self , "<STR_LIT>" % signame , None ) <EOL> if not handler : <EOL> self . log . error ( "<STR_LIT>" % signame ) <EOL> continue <EOL> self . log . info ( "<STR_LIT>" % signame ) <EOL> handler ( ) <EOL> self . wakeup ( ) <EOL> except StopIteration : <EOL> self . halt ( ) <EOL> except KeyboardInterrupt : <EOL> self . halt ( ) <EOL> except HaltServer , inst : <EOL> self . halt ( reason = inst . reason , exit_status = inst . exit_status ) <EOL> except SystemExit : <EOL> raise <EOL> except Exception : <EOL> self . log . info ( "<STR_LIT>" % <EOL> traceback . format_exc ( ) ) <EOL> self . stop ( False ) <EOL> if self . pidfile is not None : <EOL> self . pidfile . unlink ( ) <EOL> sys . exit ( - <NUM_LIT:1> ) <EOL> def handle_chld ( self , * args ) : <EOL> "<STR_LIT>" <EOL> self . wakeup ( ) <EOL> self . reap_workers ( ) <EOL> def handle_hup ( self ) : <EOL> """<STR_LIT>""" <EOL> self . log . info ( "<STR_LIT>" % self . master_name ) <EOL> self . reload ( ) <EOL> def handle_quit ( self ) : <EOL> "<STR_LIT>" <EOL> raise StopIteration <EOL> def handle_int ( self ) : <EOL> "<STR_LIT>" <EOL> self . stop ( False ) <EOL> raise StopIteration <EOL> def handle_term ( self ) : <EOL> "<STR_LIT>" <EOL> self . stop ( False ) <EOL> raise StopIteration <EOL> def handle_ttin ( self ) : <EOL> """<STR_LIT>""" <EOL> self . num_workers += <NUM_LIT:1> <EOL> self . manage_workers ( ) <EOL> def handle_ttou ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . num_workers <= <NUM_LIT:1> : <EOL> return <EOL> self . num_workers -= <NUM_LIT:1> <EOL> self . manage_workers ( ) <EOL> def handle_usr1 ( self ) : <EOL> """<STR_LIT>""" <EOL> self . kill_workers ( signal . SIGUSR1 ) <EOL> def handle_usr2 ( self ) : <EOL> """<STR_LIT>""" <EOL> self . reexec ( ) <EOL> def handle_winch ( self ) : <EOL> "<STR_LIT>" <EOL> if os . getppid ( ) == <NUM_LIT:1> or os . getpgrp ( ) != os . getpid ( ) : <EOL> self . log . info ( "<STR_LIT>" ) <EOL> self . num_workers = <NUM_LIT:0> <EOL> self . kill_workers ( signal . SIGQUIT ) <EOL> else : <EOL> self . log . info ( "<STR_LIT>" ) <EOL> def wakeup ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> os . write ( self . PIPE [ <NUM_LIT:1> ] , '<STR_LIT:.>' ) <EOL> except IOError , e : <EOL> if e . errno not in [ errno . EAGAIN , errno . EINTR ] : <EOL> raise <EOL> def halt ( self , reason = None , exit_status = <NUM_LIT:0> ) : <EOL> """<STR_LIT>""" <EOL> self . stop ( ) <EOL> self . log . info ( "<STR_LIT>" % self . master_name ) <EOL> if reason is not None : <EOL> self . log . info ( "<STR_LIT>" % reason ) <EOL> if self . pidfile is not None : <EOL> self . pidfile . unlink ( ) <EOL> sys . exit ( exit_status ) <EOL> def sleep ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> ready = select . select ( [ self . PIPE [ <NUM_LIT:0> ] ] , [ ] , [ ] , <NUM_LIT:1.0> ) <EOL> if not ready [ <NUM_LIT:0> ] : <EOL> return <EOL> while os . read ( self . PIPE [ <NUM_LIT:0> ] , <NUM_LIT:1> ) : <EOL> pass <EOL> except select . error , e : <EOL> if e [ <NUM_LIT:0> ] not in [ errno . EAGAIN , errno . EINTR ] : <EOL> raise <EOL> except OSError , e : <EOL> if e . errno not in [ errno . EAGAIN , errno . EINTR ] : <EOL> raise <EOL> except KeyboardInterrupt : <EOL> sys . exit ( ) <EOL> def stop ( self , graceful = True ) : <EOL> """<STR_LIT>""" <EOL> self . LISTENER = None <EOL> sig = signal . SIGQUIT <EOL> if not graceful : <EOL> sig = signal . SIGTERM <EOL> limit = time . time ( ) + self . timeout <EOL> while self . WORKERS and time . time ( ) < limit : <EOL> self . kill_workers ( sig ) <EOL> gevent . sleep ( <NUM_LIT:0.1> ) <EOL> self . reap_workers ( ) <EOL> self . kill_workers ( signal . SIGKILL ) <EOL> def reexec ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . pidfile is not None : <EOL> self . pidfile . rename ( "<STR_LIT>" % self . pidfile . fname ) <EOL> self . reexec_pid = os . fork ( ) <EOL> if self . reexec_pid != <NUM_LIT:0> : <EOL> self . master_name = "<STR_LIT>" <EOL> return <EOL> os . environ [ '<STR_LIT>' ] = str ( self . LISTENER . fileno ( ) ) <EOL> os . chdir ( self . START_CTX [ '<STR_LIT>' ] ) <EOL> self . cfg . pre_exec ( self ) <EOL> os . execvpe ( self . START_CTX [ <NUM_LIT:0> ] , self . START_CTX [ '<STR_LIT:args>' ] , os . environ ) <EOL> def reload ( self ) : <EOL> for i in range ( self . cfg . workers ) : <EOL> self . spawn_worker ( ) <EOL> if self . pidfile is not None : <EOL> self . pidfile . unlink ( ) <EOL> if self . cfg . pidfile is not None : <EOL> self . pidfile = Pidfile ( self . cfg . pidfile ) <EOL> self . pidfile . create ( self . pid ) <EOL> self . manage_workers ( ) <EOL> def murder_workers ( self ) : <EOL> """<STR_LIT>""" <EOL> for ( pid , worker ) in self . WORKERS . items ( ) : <EOL> try : <EOL> diff = time . time ( ) - os . fstat ( worker . tmp . fileno ( ) ) . st_ctime <EOL> if diff <= self . timeout : <EOL> continue <EOL> except ValueError : <EOL> continue <EOL> self . log . critical ( "<STR_LIT>" % pid ) <EOL> self . kill_worker ( pid , signal . SIGKILL ) <EOL> def reap_workers ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> while True : <EOL> wpid , status = os . waitpid ( - <NUM_LIT:1> , os . WNOHANG ) <EOL> if not wpid : <EOL> break <EOL> if self . reexec_pid == wpid : <EOL> self . reexec_pid = <NUM_LIT:0> <EOL> else : <EOL> exitcode = status >> <NUM_LIT:8> <EOL> if exitcode == self . WORKER_BOOT_ERROR : <EOL> reason = "<STR_LIT>" <EOL> raise HaltServer ( reason , self . WORKER_BOOT_ERROR ) <EOL> worker = self . WORKERS . pop ( wpid , None ) <EOL> if not worker : <EOL> continue <EOL> worker . tmp . close ( ) <EOL> except OSError , e : <EOL> if e . errno == errno . ECHILD : <EOL> pass <EOL> def manage_workers ( self ) : <EOL> """<STR_LIT>""" <EOL> if len ( self . WORKERS . keys ( ) ) < self . num_workers : <EOL> self . spawn_workers ( ) <EOL> num_to_kill = len ( self . WORKERS ) - self . num_workers <EOL> for i in range ( num_to_kill , <NUM_LIT:0> , - <NUM_LIT:1> ) : <EOL> pid , age = <NUM_LIT:0> , sys . maxint <EOL> for ( wpid , worker ) in self . WORKERS . iteritems ( ) : <EOL> if worker . age < age : <EOL> pid , age = wpid , worker . age <EOL> self . kill_worker ( pid , signal . SIGQUIT ) <EOL> def spawn_worker ( self ) : <EOL> self . worker_age += <NUM_LIT:1> <EOL> worker = Worker ( self . worker_age , self . pid , self . LISTENER , self . cfg , <EOL> self . script ) <EOL> pid = os . fork ( ) <EOL> if pid != <NUM_LIT:0> : <EOL> self . WORKERS [ pid ] = worker <EOL> return <EOL> worker_pid = os . getpid ( ) <EOL> try : <EOL> self . log . info ( "<STR_LIT>" % worker_pid ) <EOL> worker . serve_forever ( ) <EOL> sys . exit ( <NUM_LIT:0> ) <EOL> except SystemExit : <EOL> raise <EOL> except KeyboardInterrupt : <EOL> sys . exit ( <NUM_LIT:0> ) <EOL> except : <EOL> self . log . exception ( "<STR_LIT>" ) <EOL> if not worker . booted : <EOL> sys . exit ( self . WORKER_BOOT_ERROR ) <EOL> sys . exit ( - <NUM_LIT:1> ) <EOL> finally : <EOL> self . log . info ( "<STR_LIT>" % worker_pid ) <EOL> try : <EOL> worker . tmp . close ( ) <EOL> except : <EOL> pass <EOL> def spawn_workers ( self ) : <EOL> """<STR_LIT>""" <EOL> for i in range ( self . num_workers - len ( self . WORKERS . keys ( ) ) ) : <EOL> self . spawn_worker ( ) <EOL> def kill_workers ( self , sig ) : <EOL> """<STR_LIT>""" <EOL> for pid in self . WORKERS . keys ( ) : <EOL> self . kill_worker ( pid , sig ) <EOL> def kill_worker ( self , pid , sig ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> os . kill ( pid , sig ) <EOL> except OSError , e : <EOL> if e . errno == errno . ESRCH : <EOL> try : <EOL> worker = self . WORKERS . pop ( pid ) <EOL> worker . tmp . close ( ) <EOL> return <EOL> except ( KeyError , OSError ) : <EOL> return <EOL> raise </s>
