def __init__(self, chroot_directory=None, working_directory='/', umask=0, uid=None, gid=None, prevent_core=True, detach_process=None, files_preserve=[], pidfile=None, stdout_logger = None, stderr_logger = None, signal_map=None, exit_callback=None): """Custom __init__ to replace stdout/stderr with loggers""" self.logger = logging.getLogger("seminode.core.runtime.daemoncontext") devnull_in = open(os.devnull, 'r+') devnull_out = open(os.devnull, 'w+') files_preserve.extend([devnull_in, devnull_out]) self.exit_callback = exit_callback DaemonContext.__init__(self, chroot_directory = chroot_directory, working_directory = working_directory, umask = umask, uid = uid, gid = gid, prevent_core = prevent_core, detach_process = detach_process, files_preserve = files_preserve, pidfile = pidfile, stdin = devnull_in, stdout = devnull_out, stderr = devnull_out, signal_map = signal_map)
def run_as_daemon(loader, interval): """ Given a loader object, start it as a daemon process. """ log.info("The loader will run as a daemon.") # We need to preserve the file descriptor for any log files. rootlogger = logging.getLogger() log_files = [x.stream for x in rootlogger.handlers] dc = DaemonContext(files_preserve=log_files) try: try: # Note: Because we need to be compatible with Python 2.4, we can't # use "with dc:" here - we need to call the open() and close() # methods manually. dc.open() loader.startup() # every <interval> seconds, process the records in the incoming # directory while True: loader.load_all_msgs() time.sleep(interval) except SystemExit, e: log.info("Received the shutdown signal: %s", e) except LoaderException, e: log.critical("An unrecoverable exception was thrown: %s", e)
def __init__(self, lock_file, uid, gid, is_nofork): lock = None self.thread_manager = ThreadManager() try: lock = FileLock(lock_file) self.thread_manager.lock = lock # Prepare to finish when receive a interrupt key/kill command if is_nofork: [ signal(signal_key, self.thread_manager.finish) for signal_key in [SIGTERM, SIGINT, SIGABRT] ] with lock: self.thread_manager.start() else: context = DaemonContext(working_directory=getcwd(), pidfile=lock, uid=uid, gid=gid, files_preserve=log.handler_file, # umask=0o002, # Uncomment for direct command-line debugging # stdout=sys.stdout, # stderr=sys.stderr, ) context.signal_map = dict([ (signal_key, self.thread_manager.finish) for signal_key in [SIGTERM, SIGINT, SIGABRT] ]) with context: self.thread_manager.start() except Exception, e: log.exception("The instance generated a error, please check the Traceback [%s]." % e)
def setup(self): # Determine the uid and gid to change to try: nrpe_uid = pwd.getpwnam(self.cfg.nrpe_user).pw_uid except KeyError: log.error('invalid nrpe_user: {}'.format(self.nrpe_user)) sys.exit(1) try: nrpe_gid = grp.getgrnam(self.cfg.nrpe_group).gr_gid except KeyError: log.error('invalid nrpe_group: {}'.format(self.nrpe_group)) sys.exit(1) # Prepare Daemon Context dctx = DaemonContext( detach_process=self.cfg.daemon, files_preserve=[], uid=nrpe_uid, gid=nrpe_gid, initgroups=True, ) dctx.signal_map.update({ signal.SIGHUP: self.handle_signal, signal.SIGTERM: self.handle_signal, }) self.daemon_context = dctx # Prepare PID file if self.cfg.daemon: dctx.pidfile = TimeoutPIDLockFile(self.cfg.pid_file) # If we are not daemonising, don't redirect stdout or stderr if not self.cfg.daemon: dctx.stdout = sys.stdout dctx.stderr = sys.stderr
def run_as_daemon(loader, interval): """ Given a loader object, start it as a daemon process. """ log.info("The loader will run as a daemon.") # We need to preserve the file descriptor for any log files. rootlogger = logging.getLogger() log_files = [x.stream for x in rootlogger.handlers] dc = DaemonContext(files_preserve=log_files) try: # Note: because we need to be compatible with python 2.4, we can't use # with dc: # here - we need to call the open() and close() methods # manually. dc.open() loader.startup() # every <interval> seconds, process the records in the incoming # directory while True: loader.load_all_msgs() time.sleep(interval) except SystemExit, e: log.info("Received the shutdown signal: " + str(e)) loader.shutdown() dc.close()
def __configure_daemon(detach: bool, camguard: Any) -> DaemonContext: """in case of running daemonized, this configures daemon as described in PEP 3143 Args: detach (bool): set to true to detach from terminal camguard (Any): the camguard instance to run Returns: DaemonContext: daemon context which handles turning program into a daemon process """ signal_map: Dict[Signals, Any] = { # lambda type couldn't be inferred SIGTERM: lambda sig, _: __shutdown(camguard, sig), # type: ignore SIGINT: lambda sig, _: __shutdown(camguard, sig) # type: ignore } # setup pid file context (/var/run/camguard.pid) pid_file: Optional[PidFile] = PidFile( pidname='camguard') if detach else None work_dir: str = '/' if detach else '.' return DaemonContext(detach_process=detach, pidfile=pid_file, signal_map=signal_map, stderr=sys.stderr, stdout=sys.stdout, working_directory=work_dir)
def __init__(self, host=None, port=None, pidfile=None, logfile=None, daemonize=False, debug=False): if not logfile: logfile = app.config['WEB_LOG_FILE'] logfile = os.path.realpath(logfile) pidfile = os.path.realpath(pidfile or app.config['WEB_PID_FILE']) if daemonize: detach_process = True else: detach_process = False self.daemon_context = DaemonContext(detach_process=detach_process) self.daemon_context.stdout = open(logfile, 'w+') self.daemon_context.stderr = open(logfile, 'w+', buffering=0) self.pidfile = make_pidlockfile(pidfile, self.pidfile_timeout) self.daemon_context.pidfile = self.pidfile self.host = host or app.config['WEB_HOST'] self.port = port or app.config['WEB_PORT'] self.debug = debug # HACK: set app to self so self.app.run() works self.app = self
def _detach_and_poll(rpipe, keep_fds): preserve_fds = list( chain(keep_fds, (rpipe,))) with DaemonContext(detach_process=True, files_preserve=preserve_fds): # Blocking drain pipe until EOF while True: select((rpipe,), (), ()) if not rpipe.read(1024): break
def __init__(self, app): """ Set up the parameters of a new runner. :param app: The application instance; see below. :return: ``None``. The `app` argument must have the following attributes: * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem paths to open and replace the existing `sys.stdin`, `sys.stdout`, `sys.stderr`. * `pidfile_path`: Absolute filesystem path to a file that will be used as the PID file for the daemon. If ``None``, no PID file will be used. * `pidfile_timeout`: Used as the default acquisition timeout value supplied to the runner's PID lock file. * `run`: Callable that will be invoked when the daemon is started. """ self.app = app if 'start' in app.cmd: self.action = 'start' elif 'stop' in app.cmd: self.action = 'stop' elif 'restart' in app.cmd: self.action = 'restart' elif 'status' in app.cmd: self.action = 'status' self.daemon_context = DaemonContext() self.daemon_context.stdin = open(app.stdin_path, 'rt') self.daemon_context.stdout = open(app.stdout_path, 'wb+', buffering=0) self.daemon_context.stderr = open(app.stderr_path, 'wb+', buffering=0) self.pidfile = None if app.pidfile_path is not None: self.pidfile = make_pidlockfile(app.pidfile_path, app.pidfile_timeout) self.daemon_context.pidfile = self.pidfile
def __init__(self, app, pidfile=None, stdout=sys.stdout, stderr=sys.stderr, **options): """ Set up the parameters of a new runner. The `app` argument must have the following attributes: * `run`: Callable that will be invoked when the daemon is started. """ self.app = app if pidfile: self.pidfile = make_pidlockfile(pidfile, 5) else: self.pidfile = None self.daemon_context = DaemonContext( stdout=stdout, stderr=stderr, pidfile=self.pidfile, **options )
def __init__(self, app): self.parse_args() self.app = app self.daemon_context = DaemonContext() self.daemon_context.stdin = open(app.stdin_path, 'rt') self.daemon_context.stdout = open(app.stdout_path, 'w+t') if sys.version_info[0] >= 3: self.daemon_context.stderr = open( app.stderr_path, 'w+b', buffering=0) else: self.daemon_context.stderr = open( app.stderr_path, 'w+t', buffering=0) self.pidfile = None if app.pidfile_path is not None: self.pidfile = make_pidlockfile( app.pidfile_path, app.pidfile_timeout) self.daemon_context.pidfile = self.pidfile
def __init__(self, app): """ Set up the parameters of a new runner. :param app: The application instance; see below. :return: ``None``. The `app` argument must have the following attributes: * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem paths to open and replace the existing `sys.stdin`, `sys.stdout`, `sys.stderr`. * `pidfile_path`: Absolute filesystem path to a file that will be used as the PID file for the daemon. If ``None``, no PID file will be used. * `pidfile_timeout`: Used as the default acquisition timeout value supplied to the runner's PID lock file. * `run`: Callable that will be invoked when the daemon is started. """ self.app = app if 'start' in app.cmd: self.action = 'start' elif 'stop' in app.cmd: self.action = 'stop' elif 'restart' in app.cmd: self.action = 'restart' elif 'status' in app.cmd: self.action = 'status' self.daemon_context = DaemonContext() self.daemon_context.stdin = open(app.stdin_path, 'rt') self.daemon_context.stdout = open(app.stdout_path, 'wb+', buffering=0) self.daemon_context.stderr = open(app.stderr_path, 'wb+', buffering=0) self.pidfile = None if app.pidfile_path is not None: self.pidfile = make_pidlockfile( app.pidfile_path, app.pidfile_timeout) self.daemon_context.pidfile = self.pidfile
def __init__(self, host=None, port=None, pidfile=None, logfile=None): from sentry.conf import settings if not logfile: logfile = settings.WEB_LOG_FILE self.daemon_context = DaemonContext() self.daemon_context.stdout = open(logfile, 'w+') self.daemon_context.stderr = open(logfile, 'w+', buffering=0) self.pidfile = make_pidlockfile(pidfile or settings.WEB_PID_FILE, self.pidfile_timeout) self.daemon_context.pidfile = self.pidfile self.host = host or settings.WEB_HOST self.port = port or settings.WEB_PORT # HACK: set app to self so self.app.run() works self.app = self
def __init__(self, app): self.parse_args() self.app = app signalHandlerMap = {signal.SIGTERM: self.app.stop} uid = pwd.getpwnam(REPO_CONFIG.get('DAEMON_USER')).pw_uid gid = grp.getgrnam(REPO_CONFIG.get('DAEMON_GROUP')).gr_gid self.daemon_context = DaemonContext(signal_map=signalHandlerMap, working_directory='.', uid=uid, gid=gid, umask=022) self.daemon_context.stdin = None self.daemon_context.stdout = self.app.stdout self.daemon_context.stderr = self.app.stderr self.pidfile = None if app.pidfile_path is not None: self.pidfile = make_pidlockfile(app.pidfile_path, app.pidfile_timeout) self.daemon_context.pidfile = self.pidfile
class Daemon(object): """ Controller for a callable running in a separate background process. """ start_message = u"{1} started with pid {0}" def __init__(self, app, pidfile=None, stdout=sys.stdout, stderr=sys.stderr, **options): """ Set up the parameters of a new runner. The `app` argument must have the following attributes: * `run`: Callable that will be invoked when the daemon is started. """ self.app = app if pidfile: self.pidfile = make_pidlockfile(pidfile, 5) else: self.pidfile = None self.daemon_context = DaemonContext( stdout=stdout, stderr=stderr, pidfile=self.pidfile, **options ) def start(self): """ Open the daemon context and run the application. """ if self.pidfile and is_pidfile_stale(self.pidfile): self.pidfile.break_lock() try: self.daemon_context.open() except lockfile.AlreadyLocked: raise DaemonRunnerStartFailureError( u"PID file %(pidfile_path)r already locked".format( self.pidfile.path)) pid = os.getpid() message = self.start_message.format(pid, self.app.name) emit_message(message) signal.signal(signal.SIGHUP, self.restart) self.app.run() def stop(self): """ Exit the daemon process specified in the current PID file. """ if not self.pidfile: self.daemon_context.close() if not self.pidfile.is_locked(): raise DaemonRunnerStopFailureError( u"PID file {0} not locked".format(self.pidfile.path)) if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._terminate_daemon_process() def restart(self, *args): """ Stop, then start. """ self.stop() self.start() def _terminate_daemon_process(self): """ Terminate the daemon process specified in the current PID file. """ pid = self.pidfile.read_pid() try: os.kill(pid, signal.SIGTERM) except OSError, exc: raise DaemonRunnerStopFailureError( u"Failed to terminate {0}: {1}".format(pid, exc))
class DaemonRunner: """ Controller for a callable running in a separate background process. The first command-line argument is the action to take: * 'start': Become a daemon and call `app.run()`. * 'stop': Exit the daemon process specified in the PID file. * 'restart': Stop, then start. """ __metaclass__ = type start_message = "started with pid {pid:d}" def __init__(self, app): """ Set up the parameters of a new runner. :param app: The application instance; see below. :return: ``None``. The `app` argument must have the following attributes: * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem paths to open and replace the existing `sys.stdin`, `sys.stdout`, `sys.stderr`. * `pidfile_path`: Absolute filesystem path to a file that will be used as the PID file for the daemon. If ``None``, no PID file will be used. * `pidfile_timeout`: Used as the default acquisition timeout value supplied to the runner's PID lock file. * `run`: Callable that will be invoked when the daemon is started. """ self.app = app if 'start' in app.cmd: self.action = 'start' elif 'stop' in app.cmd: self.action = 'stop' elif 'restart' in app.cmd: self.action = 'restart' elif 'status' in app.cmd: self.action = 'status' self.daemon_context = DaemonContext() self.daemon_context.stdin = open(app.stdin_path, 'rt') self.daemon_context.stdout = open(app.stdout_path, 'wb+', buffering=0) self.daemon_context.stderr = open(app.stderr_path, 'wb+', buffering=0) self.pidfile = None if app.pidfile_path is not None: self.pidfile = make_pidlockfile( app.pidfile_path, app.pidfile_timeout) self.daemon_context.pidfile = self.pidfile def _start(self): """ Open the daemon context and run the application. :return: ``None``. :raises DaemonRunnerStartFailureError: If the PID file cannot be locked by this process. """ if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() try: self.daemon_context.open() except lockfile.AlreadyLocked: error = DaemonRunnerStartFailureError("PID file {pidfile.path!r} already locked".format(pidfile=self.pidfile)) raise error pid = os.getpid() message = self.start_message.format(pid=pid) emit_message(message) self.app.run() def _terminate_daemon_process(self): """ Terminate the daemon process specified in the current PID file. :return: ``None``. :raises DaemonRunnerStopFailureError: If terminating the daemon fails with an OS error. """ pid = self.pidfile.read_pid() try: os.kill(pid, signal.SIGTERM) except OSError as exc: error = DaemonRunnerStopFailureError("Failed to terminate {pid:d}: {exc}".format(pid=pid, exc=exc)) raise error def _status(self): """ Print the daemon process status to stdout. :return: ``None``. """ if is_pidfile_stale(self.pidfile): emit_message('rephone not runnig, but PID File is stale.') elif self.pidfile.is_locked(): emit_message('rephone is running PID {pid:d}'.format(pid=self.pidfile.read_pid())) else: emit_message('rephone is not running') def _stop(self): """ Exit the daemon process specified in the current PID file. :return: ``None``. :raises DaemonRunnerStopFailureError: If the PID file is not already locked. """ if not self.pidfile.is_locked(): emit_message('no PID file found') exit(0) if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._terminate_daemon_process() def _restart(self): """ Stop, then start. """ self._stop() self._start() action_funcs = {'start': _start, 'stop': _stop, 'restart': _restart, 'status': _status} def _get_action_func(self): """ Get the function for the specified action. :return: The function object corresponding to the specified action. :raises DaemonRunnerInvalidActionError: if the action is unknown. The action is specified by the `action` attribute, which is set during `parse_args`. """ try: func = self.action_funcs[self.action] except KeyError: error = DaemonRunnerInvalidActionError("Unknown action: {action!r}".format(action=self.action)) raise error return func def do_action(self): """ Perform the requested action. :return: ``None``. The action is specified by the `action` attribute, which is set during `parse_args`. """ func = self._get_action_func() func(self)
def wrap(): ''' Run a module inside a daemon. * Module (dotted) name passed in on the command line * Module must have a run() method as an entry point ''' parser = argparse.ArgumentParser() parser.add_argument( 'module', help='Python run module', ) parser.add_argument('-f', '--foreground', help='run in foreground', default=False, action="store_true") parser.add_argument('-p', '--pidfile', help='PID file path', default=None) parser.add_argument('-L', '--lockfile', help='PID file path', default="/var/run/%(prog)s_lock.pid") parser.add_argument('-v', '--verbosity', help='verbosity level', default=DFLT_LEVEL) parser.add_argument('-a', '--args', help='quoted module arguments', default=None) args = parser.parse_args() bad_level = False lvl = args.verbosity.upper() try: level = getattr(logging, lvl) except AttributeError: bad_level = True level = getattr(logging, DFLT_LEVEL.upper()) if args.foreground: context = ForegroundContext() else: context = DaemonContext(pidfile=lockfile.FileLock(args.lockfile), ) with context: logger = logging.getLogger('root') if args.foreground: handler = logging.StreamHandler(sys.stdout) else: handler = logging.handlers.SysLogHandler(address='/dev/log') formatter = logging.Formatter('%(levelname)s: %(message)s') handler.setLevel(level) handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(level) if bad_level: logger.error("Invalid verbosity (%s): defaulted to %s" % (args.verbosity, DFLT_LEVEL)) prog = os.path.basename(sys.argv[0]) if args.pidfile is None: if args.foreground: pidfile = "./%s.pid" % prog else: pidfile = "/var/run/%s.pid" % prog else: pidfile = args.pidfile if args.module is None: raise Exception("Module name missing") try: module = importlib.import_module(args.module) except Exception as e: logger.error("Failed to import module %s : %s" % (args.module, e)) return 3 rv = 3 if (hasattr(module, 'run') and hasattr(module.run, '__call__')): try: with open(pidfile, 'w') as fobj: fobj.write("%s" % os.getpid()) (_args, varargs, keywords, defaults) = inspect.getargspec(module.run) if len(_args) > 0: #argv = [args.module] argv = [] if args.args is not None: argv = string.split(args.args) logger.info(argv) rv = module.run(argv) else: if args.args is not None: logger.warn( 'Method [%s.run()] does not take an argument list' % args.module) rv = module.run() if rv is None: rv = 0 except Exception as e: logger.error("Execution failure for %s.run() %s : %s" % (args.module, e)) finally: try: os.unlink(pidfile) except Exception: pass else: logger.error("Module [%s] does not have a 'run' method" % args.module) return rv
def open(self): self._setup_logging() DaemonContext.open(self) if self.logger: sys.stdout = FileLikeLogger(self.logger) sys.stderr = FileLikeLogger(self.logger)
class DaemonRunner: """ Controller for a callable running in a separate background process. The first command-line argument is the action to take: * 'start': Become a daemon and call `app.run()`. * 'stop': Exit the daemon process specified in the PID file. * 'restart': Stop, then start. """ __metaclass__ = type start_message = "started with pid {pid:d}" def __init__(self, app): """ Set up the parameters of a new runner. :param app: The application instance; see below. :return: ``None``. The `app` argument must have the following attributes: * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem paths to open and replace the existing `sys.stdin`, `sys.stdout`, `sys.stderr`. * `pidfile_path`: Absolute filesystem path to a file that will be used as the PID file for the daemon. If ``None``, no PID file will be used. * `pidfile_timeout`: Used as the default acquisition timeout value supplied to the runner's PID lock file. * `run`: Callable that will be invoked when the daemon is started. """ self.app = app if 'start' in app.cmd: self.action = 'start' elif 'stop' in app.cmd: self.action = 'stop' elif 'restart' in app.cmd: self.action = 'restart' elif 'status' in app.cmd: self.action = 'status' self.daemon_context = DaemonContext() self.daemon_context.stdin = open(app.stdin_path, 'rt') self.daemon_context.stdout = open(app.stdout_path, 'wb+', buffering=0) self.daemon_context.stderr = open(app.stderr_path, 'wb+', buffering=0) self.pidfile = None if app.pidfile_path is not None: self.pidfile = make_pidlockfile(app.pidfile_path, app.pidfile_timeout) self.daemon_context.pidfile = self.pidfile def _start(self): """ Open the daemon context and run the application. :return: ``None``. :raises DaemonRunnerStartFailureError: If the PID file cannot be locked by this process. """ if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() try: self.daemon_context.open() except lockfile.AlreadyLocked: error = DaemonRunnerStartFailureError( "PID file {pidfile.path!r} already locked".format( pidfile=self.pidfile)) raise error pid = os.getpid() message = self.start_message.format(pid=pid) emit_message(message) self.app.run() def _terminate_daemon_process(self): """ Terminate the daemon process specified in the current PID file. :return: ``None``. :raises DaemonRunnerStopFailureError: If terminating the daemon fails with an OS error. """ pid = self.pidfile.read_pid() try: os.kill(pid, signal.SIGTERM) except OSError as exc: error = DaemonRunnerStopFailureError( "Failed to terminate {pid:d}: {exc}".format(pid=pid, exc=exc)) raise error def _status(self): """ Print the daemon process status to stdout. :return: ``None``. """ if is_pidfile_stale(self.pidfile): emit_message('rephone not runnig, but PID File is stale.') elif self.pidfile.is_locked(): emit_message('rephone is running PID {pid:d}'.format( pid=self.pidfile.read_pid())) else: emit_message('rephone is not running') def _stop(self): """ Exit the daemon process specified in the current PID file. :return: ``None``. :raises DaemonRunnerStopFailureError: If the PID file is not already locked. """ if not self.pidfile.is_locked(): emit_message('no PID file found') exit(0) if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._terminate_daemon_process() def _restart(self): """ Stop, then start. """ self._stop() self._start() action_funcs = { 'start': _start, 'stop': _stop, 'restart': _restart, 'status': _status } def _get_action_func(self): """ Get the function for the specified action. :return: The function object corresponding to the specified action. :raises DaemonRunnerInvalidActionError: if the action is unknown. The action is specified by the `action` attribute, which is set during `parse_args`. """ try: func = self.action_funcs[self.action] except KeyError: error = DaemonRunnerInvalidActionError( "Unknown action: {action!r}".format(action=self.action)) raise error return func def do_action(self): """ Perform the requested action. :return: ``None``. The action is specified by the `action` attribute, which is set during `parse_args`. """ func = self._get_action_func() func(self)
def _start(self): if self.conf.logdir is not None: self.access_log = open('{}/{}'.format( self.conf.logdir, 'access.log'), 'ab') sys.stderr.write('Starting server on {}\n'.format(self.url)) #### Daemonize if self.conf.daemonize: assert self.access_log is not None if self.pidlockfile.is_locked(): exit('PID file {} already locked'.format( self.pidlockfile.path)) daemon = DaemonContext( working_directory=os.getcwd(), umask=0o077, pidfile=self.pidlockfile, signal_map={SIGTERM: None}, # let me handle signals stderr=self.access_log) daemon.open() #### Setup logging log_dest = { 'REQUEST': [], 'DEBUG': self.conf.debug_log, 'INFO': self.conf.log, 'ERROR': self.conf.log} if self.conf.logdir is not None: log_dest['INFO'].append([__name__, 'event.log']) if self.conf.request_log is not None: log_dest['REQUEST'].append( [MY_PKG_NAME, self.conf.request_log]) self.loggers = get_loggers(log_dest, logdir=self.conf.logdir, fmt=self.log_fmt) #### Connect to the databases # This has to be done after daemonization because the sockets # may be closed for name, d in self.db_bases.items(): base = d['base'] url = getattr(self.conf, '{}_dburl'.format(name)) session_kargs = d.get('session_args', {}) engine_kargs = d.get('engine_args', {}) cache = d.get('cache', False) DBConnection(base, url, session_kargs=session_kargs, engine_kargs=engine_kargs) if cache: # ETag support self.reqhandler.enable_client_cache(name, base) #### Load users if self.conf.userfile is not None: self.reqhandler.load_users_from_file( self.conf.userfile, plaintext=not self.conf.userfile_hashed) if self._delete_tmp_userfile: os.remove(self.conf.userfile) #### Create the server # This has to be done after daemonization because it binds to # the listening port at creation time srv_cls = HTTPServer if self.conf.multithread: srv_cls = ThreadingHTTPServer self.httpd = srv_cls((self.conf.address, self.conf.port), self.reqhandler) if self.conf.ssl: self.httpd.socket = ssl.wrap_socket( self.httpd.socket, keyfile=self.conf.keyfile, certfile=self.conf.certfile, server_side=True) #### Setup signal handlers signal(SIGTERM, self._term_sighandler) signal(SIGINT, self._term_sighandler) #### Redirect stderr to access.log if self.conf.logdir is not None and not self.conf.daemonize: # running in foreground, but logging to logdir, redirect # stderr to access.log as http.server writes to stderr os.dup2(self.access_log.fileno(), sys.stderr.fileno()) #### Change working directory and run os.chdir(self.conf.webroot) self._log_event('Starting server on {}'.format(self.url)) self.httpd.serve_forever()