<s> """<STR_LIT>""" <EOL> import errno <EOL> import os <EOL> import resource <EOL> import signal <EOL> import socket <EOL> import subprocess <EOL> import sys <EOL> import time <EOL> import psutil <EOL> from . exceptions import DaemonError <EOL> def expose_action ( func ) : <EOL> """<STR_LIT>""" <EOL> func . __daemonocle_exposed__ = True <EOL> return func <EOL> class Daemon ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( <EOL> self , worker = None , shutdown_callback = None , prog = None , pidfile = None , <EOL> detach = True , uid = None , gid = None , workdir = '<STR_LIT:/>' , chrootdir = None , <EOL> umask = <NUM_LIT> , stop_timeout = <NUM_LIT:10> , close_open_files = False ) : <EOL> """<STR_LIT>""" <EOL> self . worker = worker <EOL> self . shutdown_callback = shutdown_callback <EOL> self . prog = prog if prog is not None else os . path . basename ( sys . argv [ <NUM_LIT:0> ] ) <EOL> self . pidfile = pidfile <EOL> if self . pidfile is not None : <EOL> self . pidfile = os . path . realpath ( self . pidfile ) <EOL> self . detach = detach & self . _is_detach_necessary ( ) <EOL> self . uid = uid if uid is not None else os . getuid ( ) <EOL> self . gid = gid if gid is not None else os . getgid ( ) <EOL> self . workdir = workdir <EOL> self . chrootdir = chrootdir <EOL> self . umask = umask <EOL> self . stop_timeout = stop_timeout <EOL> self . close_open_files = close_open_files <EOL> self . _pid_fd = None <EOL> self . _shutdown_complete = False <EOL> self . _orig_workdir = '<STR_LIT:/>' <EOL> @ classmethod <EOL> def _emit_message ( cls , message ) : <EOL> """<STR_LIT>""" <EOL> sys . stdout . write ( message ) <EOL> sys . stdout . flush ( ) <EOL> @ classmethod <EOL> def _emit_ok ( cls ) : <EOL> """<STR_LIT>""" <EOL> cls . _emit_message ( '<STR_LIT>' ) <EOL> @ classmethod <EOL> def _emit_failed ( cls ) : <EOL> """<STR_LIT>""" <EOL> cls . _emit_message ( '<STR_LIT>' ) <EOL> @ classmethod <EOL> def _emit_error ( cls , message ) : <EOL> """<STR_LIT>""" <EOL> sys . stderr . write ( '<STR_LIT>' . format ( message = message ) ) <EOL> sys . stderr . flush ( ) <EOL> @ classmethod <EOL> def _emit_warning ( cls , message ) : <EOL> """<STR_LIT>""" <EOL> sys . stderr . write ( '<STR_LIT>' . format ( message = message ) ) <EOL> sys . stderr . flush ( ) <EOL> def _setup_piddir ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . pidfile is None : <EOL> return <EOL> piddir = os . path . dirname ( self . pidfile ) <EOL> if not os . path . isdir ( piddir ) : <EOL> os . makedirs ( piddir , <NUM_LIT> & ~ self . umask ) <EOL> os . chown ( piddir , self . uid , self . gid ) <EOL> def _read_pidfile ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . pidfile is None : <EOL> return None <EOL> if not os . path . isfile ( self . pidfile ) : <EOL> return None <EOL> with open ( self . pidfile , '<STR_LIT:r>' ) as fp : <EOL> try : <EOL> pid = int ( fp . read ( ) ) <EOL> except ValueError : <EOL> self . _emit_warning ( '<STR_LIT>' <EOL> '<STR_LIT>' . format ( pidfile = self . pidfile ) ) <EOL> pid = None <EOL> if pid is not None and psutil . pid_exists ( pid ) : <EOL> return pid <EOL> else : <EOL> os . remove ( self . pidfile ) <EOL> return None <EOL> def _write_pidfile ( self ) : <EOL> """<STR_LIT>""" <EOL> flags = os . O_CREAT | os . O_RDWR <EOL> try : <EOL> flags = flags | os . O_EXLOCK <EOL> except AttributeError : <EOL> pass <EOL> self . _pid_fd = os . open ( self . pidfile , flags , <NUM_LIT> & ~ self . umask ) <EOL> os . write ( self . _pid_fd , str ( os . getpid ( ) ) . encode ( '<STR_LIT:utf-8>' ) ) <EOL> def _close_pidfile ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . _pid_fd is not None : <EOL> os . close ( self . _pid_fd ) <EOL> try : <EOL> os . remove ( self . pidfile ) <EOL> except OSError as ex : <EOL> if ex . errno != errno . ENOENT : <EOL> raise <EOL> @ classmethod <EOL> def _prevent_core_dump ( cls ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> resource . getrlimit ( resource . RLIMIT_CORE ) <EOL> except ValueError : <EOL> return <EOL> else : <EOL> resource . setrlimit ( resource . RLIMIT_CORE , ( <NUM_LIT:0> , <NUM_LIT:0> ) ) <EOL> def _setup_environment ( self ) : <EOL> """<STR_LIT>""" <EOL> self . _orig_workdir = os . getcwd ( ) <EOL> if self . chrootdir is not None : <EOL> try : <EOL> os . chdir ( self . chrootdir ) <EOL> os . chroot ( self . chrootdir ) <EOL> except Exception as ex : <EOL> raise DaemonError ( '<STR_LIT>' <EOL> '<STR_LIT>' . format ( error = str ( ex ) ) ) <EOL> self . _prevent_core_dump ( ) <EOL> try : <EOL> os . chdir ( self . workdir ) <EOL> except Exception as ex : <EOL> raise DaemonError ( '<STR_LIT>' <EOL> '<STR_LIT>' . format ( error = str ( ex ) ) ) <EOL> self . _setup_piddir ( ) <EOL> try : <EOL> os . umask ( self . umask ) <EOL> except Exception as ex : <EOL> raise DaemonError ( '<STR_LIT>' <EOL> '<STR_LIT>' . format ( error = str ( ex ) ) ) <EOL> try : <EOL> os . setgid ( self . gid ) <EOL> os . setuid ( self . uid ) <EOL> except Exception as ex : <EOL> raise DaemonError ( '<STR_LIT>' <EOL> '<STR_LIT>' . format ( error = str ( ex ) ) ) <EOL> def _reset_file_descriptors ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . close_open_files : <EOL> max_fds = resource . getrlimit ( resource . RLIMIT_NOFILE ) [ <NUM_LIT:1> ] <EOL> if max_fds == resource . RLIM_INFINITY : <EOL> max_fds = <NUM_LIT> <EOL> else : <EOL> max_fds = <NUM_LIT:3> <EOL> for fd in range ( max_fds ) : <EOL> try : <EOL> os . close ( fd ) <EOL> except OSError : <EOL> pass <EOL> devnull_fd = os . open ( os . devnull , os . O_RDWR ) <EOL> os . dup2 ( devnull_fd , <NUM_LIT:0> ) <EOL> os . dup2 ( devnull_fd , <NUM_LIT:1> ) <EOL> os . dup2 ( devnull_fd , <NUM_LIT:2> ) <EOL> @ classmethod <EOL> def _is_socket ( cls , stream ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> fd = stream . fileno ( ) <EOL> except ValueError : <EOL> return False <EOL> sock = socket . fromfd ( fd , socket . AF_INET , socket . SOCK_RAW ) <EOL> try : <EOL> sock . getsockopt ( socket . SOL_SOCKET , socket . SO_TYPE ) <EOL> except socket . error as ex : <EOL> if ex . args [ <NUM_LIT:0> ] != errno . ENOTSOCK : <EOL> return True <EOL> else : <EOL> return True <EOL> @ classmethod <EOL> def _pid_is_alive ( cls , pid , timeout ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> proc = psutil . Process ( pid ) <EOL> except psutil . NoSuchProcess : <EOL> return False <EOL> try : <EOL> proc . wait ( timeout = timeout ) <EOL> except psutil . TimeoutExpired : <EOL> return True <EOL> return False <EOL> @ classmethod <EOL> def _is_detach_necessary ( cls ) : <EOL> """<STR_LIT>""" <EOL> if os . getppid ( ) == <NUM_LIT:1> : <EOL> return False <EOL> if cls . _is_socket ( sys . stdin ) : <EOL> return False <EOL> return True <EOL> def _detach_process ( self ) : <EOL> """<STR_LIT>""" <EOL> pid = os . fork ( ) <EOL> if pid > <NUM_LIT:0> : <EOL> os . waitpid ( pid , <NUM_LIT:0> ) <EOL> sys . exit ( <NUM_LIT:0> ) <EOL> os . setsid ( ) <EOL> pid = os . fork ( ) <EOL> if pid > <NUM_LIT:0> : <EOL> time . sleep ( <NUM_LIT:1> ) <EOL> status = os . waitpid ( pid , os . WNOHANG ) <EOL> if status [ <NUM_LIT:0> ] == pid : <EOL> exitcode = status [ <NUM_LIT:1> ] % <NUM_LIT:255> <EOL> self . _emit_failed ( ) <EOL> self . _emit_error ( '<STR_LIT>' <EOL> '<STR_LIT>' . format ( code = exitcode ) ) <EOL> sys . exit ( exitcode ) <EOL> else : <EOL> self . _emit_ok ( ) <EOL> sys . exit ( <NUM_LIT:0> ) <EOL> self . _reset_file_descriptors ( ) <EOL> @ classmethod <EOL> def _orphan_this_process ( cls , wait_for_parent = False ) : <EOL> """<STR_LIT>""" <EOL> ppid = os . getpid ( ) <EOL> pid = os . fork ( ) <EOL> if pid > <NUM_LIT:0> : <EOL> sys . exit ( <NUM_LIT:0> ) <EOL> if wait_for_parent and cls . _pid_is_alive ( ppid , timeout = <NUM_LIT:1> ) : <EOL> raise DaemonError ( <EOL> '<STR_LIT>' ) <EOL> @ classmethod <EOL> def _fork_and_supervise_child ( cls ) : <EOL> """<STR_LIT>""" <EOL> pid = os . fork ( ) <EOL> if pid == <NUM_LIT:0> : <EOL> cls . _orphan_this_process ( ) <EOL> return <EOL> os . waitpid ( pid , <NUM_LIT:0> ) <EOL> pgid = os . getpgrp ( ) <EOL> exclude_pids = set ( [ <NUM_LIT:0> , os . getpid ( ) ] ) <EOL> proc = psutil . Process ( ) <EOL> while os . getpgid ( proc . pid ) == pgid : <EOL> exclude_pids . add ( proc . pid ) <EOL> proc = psutil . Process ( proc . ppid ( ) ) <EOL> while True : <EOL> try : <EOL> group_procs = [ ] <EOL> for proc in psutil . process_iter ( ) : <EOL> try : <EOL> if ( os . getpgid ( proc . pid ) == pgid and <EOL> proc . pid not in exclude_pids ) : <EOL> group_procs . append ( proc ) <EOL> except ( psutil . NoSuchProcess , OSError ) : <EOL> continue <EOL> if group_procs : <EOL> psutil . wait_procs ( group_procs , timeout = <NUM_LIT:1> ) <EOL> else : <EOL> cls . _emit_message ( <EOL> '<STR_LIT>' ) <EOL> sys . exit ( <NUM_LIT:0> ) <EOL> except KeyboardInterrupt : <EOL> cls . _emit_message ( '<STR_LIT:\n>' ) <EOL> continue <EOL> def _shutdown ( self , message = None , code = <NUM_LIT:0> ) : <EOL> """<STR_LIT>""" <EOL> if self . _shutdown_complete : <EOL> sys . exit ( code ) <EOL> if self . shutdown_callback is not None : <EOL> self . shutdown_callback ( message , code ) <EOL> if self . pidfile is not None : <EOL> self . _close_pidfile ( ) <EOL> self . _shutdown_complete = True <EOL> sys . exit ( code ) <EOL> def _handle_terminate ( self , signal_number , _ ) : <EOL> """<STR_LIT>""" <EOL> signal_names = { <EOL> signal . SIGINT : '<STR_LIT>' , <EOL> signal . SIGQUIT : '<STR_LIT>' , <EOL> signal . SIGTERM : '<STR_LIT>' , <EOL> } <EOL> message = '<STR_LIT>' . format ( <EOL> name = signal_names [ signal_number ] , number = signal_number ) <EOL> self . _shutdown ( message , code = <NUM_LIT> + signal_number ) <EOL> def _run ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> self . worker ( ) <EOL> except SystemExit as ex : <EOL> if isinstance ( ex . code , int ) : <EOL> if ex . code is not None and ex . code != <NUM_LIT:0> : <EOL> self . _shutdown ( <EOL> '<STR_LIT>' . format ( <EOL> exitcode = ex . code ) , <EOL> ex . code ) <EOL> else : <EOL> self . _shutdown ( <EOL> '<STR_LIT>' . format ( msg = ex . code ) , <NUM_LIT:1> ) <EOL> except Exception as ex : <EOL> if self . detach : <EOL> self . _shutdown ( '<STR_LIT>' . format ( <EOL> cls = ex . __class__ . __name__ , msg = str ( ex ) ) , <NUM_LIT> ) <EOL> else : <EOL> raise <EOL> self . _shutdown ( '<STR_LIT>' ) <EOL> @ expose_action <EOL> def start ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . worker is None : <EOL> raise DaemonError ( '<STR_LIT>' ) <EOL> if os . environ . get ( '<STR_LIT>' ) : <EOL> self . _emit_message ( '<STR_LIT>' . format ( prog = self . prog ) ) <EOL> self . _orphan_this_process ( wait_for_parent = True ) <EOL> pid = self . _read_pidfile ( ) <EOL> if ( pid is not None and <EOL> self . _pid_is_alive ( pid , timeout = self . stop_timeout ) ) : <EOL> self . _emit_failed ( ) <EOL> message = ( '<STR_LIT>' <EOL> '<STR_LIT>' ) . format ( pid = pid ) <EOL> self . _emit_error ( message ) <EOL> self . _shutdown ( message , <NUM_LIT:1> ) <EOL> pid = self . _read_pidfile ( ) <EOL> if pid is not None : <EOL> self . _emit_warning ( '<STR_LIT>' . format ( <EOL> prog = self . prog , pid = pid ) ) <EOL> return <EOL> if not self . detach and not os . environ . get ( '<STR_LIT>' ) : <EOL> self . _fork_and_supervise_child ( ) <EOL> if not os . environ . get ( '<STR_LIT>' ) : <EOL> self . _emit_message ( '<STR_LIT>' . format ( prog = self . prog ) ) <EOL> self . _setup_environment ( ) <EOL> if self . detach : <EOL> self . _detach_process ( ) <EOL> else : <EOL> self . _emit_ok ( ) <EOL> if self . pidfile is not None : <EOL> self . _write_pidfile ( ) <EOL> signal . signal ( signal . SIGINT , self . _handle_terminate ) <EOL> signal . signal ( signal . SIGQUIT , self . _handle_terminate ) <EOL> signal . signal ( signal . SIGTERM , self . _handle_terminate ) <EOL> self . _run ( ) <EOL> @ expose_action <EOL> def stop ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . pidfile is None : <EOL> raise DaemonError ( '<STR_LIT>' ) <EOL> pid = self . _read_pidfile ( ) <EOL> if pid is None : <EOL> self . _emit_warning ( '<STR_LIT>' . format ( prog = self . prog ) ) <EOL> return <EOL> self . _emit_message ( '<STR_LIT>' . format ( prog = self . prog ) ) <EOL> try : <EOL> os . kill ( pid , signal . SIGTERM ) <EOL> except OSError as ex : <EOL> self . _emit_failed ( ) <EOL> self . _emit_error ( str ( ex ) ) <EOL> sys . exit ( <NUM_LIT:1> ) <EOL> if self . _pid_is_alive ( pid , timeout = self . stop_timeout ) : <EOL> self . _emit_failed ( ) <EOL> self . _emit_error ( '<STR_LIT>' <EOL> '<STR_LIT>' . format ( pid = pid ) ) <EOL> sys . exit ( <NUM_LIT:1> ) <EOL> self . _emit_ok ( ) <EOL> @ expose_action <EOL> def restart ( self ) : <EOL> """<STR_LIT>""" <EOL> self . stop ( ) <EOL> self . start ( ) <EOL> @ expose_action <EOL> def status ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . pidfile is None : <EOL> raise DaemonError ( '<STR_LIT>' ) <EOL> pid = self . _read_pidfile ( ) <EOL> if pid is None : <EOL> self . _emit_message ( <EOL> '<STR_LIT>' . format ( prog = self . prog ) ) <EOL> sys . exit ( <NUM_LIT:1> ) <EOL> proc = psutil . Process ( pid ) <EOL> data = { <EOL> '<STR_LIT>' : self . prog , <EOL> '<STR_LIT>' : pid , <EOL> '<STR_LIT:status>' : proc . status ( ) , <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : <NUM_LIT:0.0> , <EOL> '<STR_LIT>' : <NUM_LIT:0.0> , <EOL> } <EOL> pgid = os . getpgid ( pid ) <EOL> for gproc in psutil . process_iter ( ) : <EOL> try : <EOL> if os . getpgid ( gproc . pid ) == pgid and gproc . pid != <NUM_LIT:0> : <EOL> data [ '<STR_LIT>' ] += gproc . cpu_percent ( interval = <NUM_LIT:0.1> ) <EOL> data [ '<STR_LIT>' ] += gproc . memory_percent ( ) <EOL> except ( psutil . Error , OSError ) : <EOL> continue <EOL> try : <EOL> uptime_mins = int ( round ( ( time . time ( ) - proc . create_time ( ) ) / <NUM_LIT> ) ) <EOL> uptime_hours , uptime_mins = divmod ( uptime_mins , <NUM_LIT> ) <EOL> data [ '<STR_LIT>' ] = str ( uptime_mins ) + '<STR_LIT:m>' <EOL> if uptime_hours : <EOL> uptime_days , uptime_hours = divmod ( uptime_hours , <NUM_LIT> ) <EOL> data [ '<STR_LIT>' ] = str ( uptime_hours ) + '<STR_LIT>' + data [ '<STR_LIT>' ] <EOL> if uptime_days : <EOL> data [ '<STR_LIT>' ] = str ( uptime_days ) + '<STR_LIT>' + data [ '<STR_LIT>' ] <EOL> except psutil . Error : <EOL> pass <EOL> template = ( '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> self . _emit_message ( template . format ( ** data ) ) <EOL> @ classmethod <EOL> def list_actions ( cls ) : <EOL> """<STR_LIT>""" <EOL> actions = [ '<STR_LIT:start>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT:status>' ] <EOL> for func_name in dir ( cls ) : <EOL> func = getattr ( cls , func_name ) <EOL> if ( not hasattr ( func , '<STR_LIT>' ) or <EOL> not getattr ( func , '<STR_LIT>' , False ) ) : <EOL> continue <EOL> action = func_name . replace ( '<STR_LIT:_>' , '<STR_LIT:->' ) <EOL> if action not in actions : <EOL> actions . append ( action ) <EOL> return actions <EOL> def get_action ( self , action ) : <EOL> """<STR_LIT>""" <EOL> func_name = action . replace ( '<STR_LIT:->' , '<STR_LIT:_>' ) <EOL> if not hasattr ( self , func_name ) : <EOL> raise DaemonError ( <EOL> '<STR_LIT>' . format ( action = action ) ) <EOL> func = getattr ( self , func_name ) <EOL> if ( not hasattr ( func , '<STR_LIT>' ) or <EOL> getattr ( func , '<STR_LIT>' , False ) is not True ) : <EOL> raise DaemonError ( <EOL> '<STR_LIT>' . format ( action = action ) ) <EOL> return func <EOL> def do_action ( self , action ) : <EOL> """<STR_LIT>""" <EOL> func = self . get_action ( action ) <EOL> func ( ) <EOL> def reload ( self ) : <EOL> """<STR_LIT>""" <EOL> pid = self . _read_pidfile ( ) <EOL> if pid is None or pid != os . getpid ( ) : <EOL> raise DaemonError ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> new_environ = os . environ . copy ( ) <EOL> new_environ [ '<STR_LIT>' ] = '<STR_LIT:true>' <EOL> subprocess . call ( <EOL> [ sys . executable ] + sys . argv , cwd = self . _orig_workdir , <EOL> env = new_environ ) <EOL> self . _shutdown ( '<STR_LIT>' ) </s>
