Exemple #1
0
    def __init__(self, app):
        """ Set up the parameters of a new runner.

            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.parse_args()
        self.app = app
        detach_process = None
        try:
            detach_process = app.detach_process
        except AttributeError:
            pass
        self.daemon_context = DaemonContext(detach_process=detach_process)
        if hasattr(app, 'stdin_path'):
            self.daemon_context.stdin = open(app.stdin_path, 'r')
        else:
            self.daemon_context.stdin = sys.stdin

        if hasattr(app, 'stdout_path'):
            self.daemon_context.stdout = open(app.stdout_path, 'w+')
        else:
            self.daemon_context.stdout = sys.stdout

        if hasattr(app, 'stderr_path'):
            self.daemon_context.stderr = open(app.stderr_path,
                                              'w+',
                                              buffering=0)
        else:
            self.daemon_context.stderr = sys.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
Exemple #2
0
    def __init__(self, app):
        """ Set up the parameters of a new runner.

            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.parse_args()
        self.app = app
        detach_process = None
        try:
            detach_process = app.detach_process
        except AttributeError:
            pass
        self.daemon_context = DaemonContext(detach_process=detach_process)
        if hasattr(app, 'stdin_path'):
            self.daemon_context.stdin = open(app.stdin_path, 'r')
        else:
            self.daemon_context.stdin = sys.stdin
            
        if hasattr(app, 'stdout_path'):
            self.daemon_context.stdout = open(app.stdout_path, 'w+')
        else:
            self.daemon_context.stdout = sys.stdout

        if hasattr(app, 'stderr_path'):
            self.daemon_context.stderr = open(
                app.stderr_path, 'w+', buffering=0)
        else:
            self.daemon_context.stderr = sys.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
Exemple #3
0
class DaemonRunner(object):
    """ 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.

        """

    start_message = "started with pid %(pid)d"

    def __init__(self, app):
        """ Set up the parameters of a new runner.

            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.parse_args()
        self.app = app
        detach_process = None
        try:
            detach_process = app.detach_process
        except AttributeError:
            pass
        self.daemon_context = DaemonContext(detach_process=detach_process)
        if hasattr(app, 'stdin_path'):
            self.daemon_context.stdin = open(app.stdin_path, 'r')
        else:
            self.daemon_context.stdin = sys.stdin
            
        if hasattr(app, 'stdout_path'):
            self.daemon_context.stdout = open(app.stdout_path, 'w+')
        else:
            self.daemon_context.stdout = sys.stdout

        if hasattr(app, 'stderr_path'):
            self.daemon_context.stderr = open(
                app.stderr_path, 'w+', buffering=0)
        else:
            self.daemon_context.stderr = sys.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

    def _usage_exit(self, argv):
        """ Emit a usage message, then exit.
            """
        progname = os.path.basename(argv[0])
        usage_exit_code = 2
        action_usage = "|".join(self.action_funcs.keys())
        message = "usage: %(progname)s %(action_usage)s" % vars()
        emit_message(message)
        sys.exit(usage_exit_code)

    def parse_args(self, argv=None):
        """ Parse command-line arguments.
            """
        if argv is None:
            argv = sys.argv

        min_args = 2
        if len(argv) < min_args:
            self._usage_exit(argv)

        self.action = argv[1]
        if self.action not in self.action_funcs:
            self._usage_exit(argv)

    def _start(self):
        """ Open the daemon context and run the application.
            """
        if is_pidfile_stale(self.pidfile):
            self.pidfile.break_lock()

        try:
            self.daemon_context.open()
        except pidlockfile.AlreadyLocked:
            pidfile_path = self.pidfile.path
            raise DaemonRunnerStartFailureError(
                "PID file %(pidfile_path)r already locked" % vars())

        pid = os.getpid()
        message = self.start_message % vars()
        emit_message(message)

        self.app.run()

    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)
            time.sleep(3)
            try:
                os.kill(pid, signal.SIGKILL)
            except OSError, exc:
                # It might already be dead.  If we now get a no such
                # process error, ignore it.
                if 'No such process' not in str(exc):
                    raise
        except OSError, exc:
            raise DaemonRunnerStopFailureError(
                "Failed to terminate %(pid)d: %(exc)s" % vars())
Exemple #4
0
class DaemonRunner(object):
    """ 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.

        """

    start_message = "started with pid %(pid)d"

    def __init__(self, app):
        """ Set up the parameters of a new runner.

            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.parse_args()
        self.app = app
        detach_process = None
        try:
            detach_process = app.detach_process
        except AttributeError:
            pass
        self.daemon_context = DaemonContext(detach_process=detach_process)
        if hasattr(app, 'stdin_path'):
            self.daemon_context.stdin = open(app.stdin_path, 'r')
        else:
            self.daemon_context.stdin = sys.stdin

        if hasattr(app, 'stdout_path'):
            self.daemon_context.stdout = open(app.stdout_path, 'w+')
        else:
            self.daemon_context.stdout = sys.stdout

        if hasattr(app, 'stderr_path'):
            self.daemon_context.stderr = open(app.stderr_path,
                                              'w+',
                                              buffering=0)
        else:
            self.daemon_context.stderr = sys.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

    def _usage_exit(self, argv):
        """ Emit a usage message, then exit.
            """
        progname = os.path.basename(argv[0])
        usage_exit_code = 2
        action_usage = "|".join(self.action_funcs.keys())
        message = "usage: %(progname)s %(action_usage)s" % vars()
        emit_message(message)
        sys.exit(usage_exit_code)

    def parse_args(self, argv=None):
        """ Parse command-line arguments.
            """
        if argv is None:
            argv = sys.argv

        min_args = 2
        if len(argv) < min_args:
            self._usage_exit(argv)

        self.action = argv[1]
        if self.action not in self.action_funcs:
            self._usage_exit(argv)

    def _start(self):
        """ Open the daemon context and run the application.
            """
        if is_pidfile_stale(self.pidfile):
            self.pidfile.break_lock()

        try:
            self.daemon_context.open()
        except pidlockfile.AlreadyLocked:
            pidfile_path = self.pidfile.path
            raise DaemonRunnerStartFailureError(
                "PID file %(pidfile_path)r already locked" % vars())

        pid = os.getpid()
        message = self.start_message % vars()
        emit_message(message)

        self.app.run()

    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)
            time.sleep(3)
            try:
                os.kill(pid, signal.SIGKILL)
            except OSError, exc:
                # It might already be dead.  If we now get a no such
                # process error, ignore it.
                if 'No such process' not in str(exc):
                    raise
        except OSError, exc:
            raise DaemonRunnerStopFailureError(
                "Failed to terminate %(pid)d: %(exc)s" % vars())