예제 #1
0
 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) 
예제 #2
0
파일: dbloader.py 프로젝트: yhshin/apel
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)
예제 #3
0
 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)
예제 #4
0
파일: server.py 프로젝트: bootc/nrpe-ng
    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
예제 #5
0
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()
예제 #6
0
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)
예제 #7
0
    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
예제 #8
0
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
예제 #9
0
    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
예제 #10
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
        )
예제 #11
0
    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
예제 #12
0
파일: runner.py 프로젝트: rickmer/rephone
    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
예제 #13
0
    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
예제 #14
0
파일: daemon.py 프로젝트: snkashis/sentry
    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
        )
예제 #15
0
    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
예제 #16
0
파일: daemon.py 프로젝트: snkashis/sentry
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))
예제 #17
0
파일: runner.py 프로젝트: rickmer/rephone
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)
예제 #18
0
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
예제 #19
0
 def open(self):
     self._setup_logging()
     DaemonContext.open(self)
     if self.logger:
         sys.stdout = FileLikeLogger(self.logger)
         sys.stderr = FileLikeLogger(self.logger)
예제 #20
0
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)
예제 #21
0
    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()
예제 #22
0
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))