diff options
Diffstat (limited to 'pkg/osx/daemon/runner.py')
-rw-r--r-- | pkg/osx/daemon/runner.py | 324 |
1 files changed, 0 insertions, 324 deletions
diff --git a/pkg/osx/daemon/runner.py b/pkg/osx/daemon/runner.py deleted file mode 100644 index de9025d..0000000 --- a/pkg/osx/daemon/runner.py +++ /dev/null @@ -1,324 +0,0 @@ -# -*- coding: utf-8 -*- - -# daemon/runner.py -# Part of ‘python-daemon’, an implementation of PEP 3143. -# -# Copyright © 2009–2015 Ben Finney <ben+python@benfinney.id.au> -# Copyright © 2007–2008 Robert Niederreiter, Jens Klein -# Copyright © 2003 Clark Evans -# Copyright © 2002 Noah Spurrier -# Copyright © 2001 Jürgen Hermann -# -# This is free software: you may copy, modify, and/or distribute this work -# under the terms of the Apache License, version 2.0 as published by the -# Apache Software Foundation. -# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details. - -""" Daemon runner library. - """ - -from __future__ import (absolute_import, unicode_literals) - -import sys -import os -import signal -import errno -try: - # Python 3 standard library. - ProcessLookupError -except NameError: - # No such class in Python 2. - ProcessLookupError = NotImplemented - -import lockfile - -from . import pidfile -from .daemon import (basestring, unicode) -from .daemon import DaemonContext -from .daemon import _chain_exception_from_existing_exception_context - - -class DaemonRunnerError(Exception): - """ Abstract base class for errors from DaemonRunner. """ - - def __init__(self, *args, **kwargs): - self._chain_from_context() - - super(DaemonRunnerError, self).__init__(*args, **kwargs) - - def _chain_from_context(self): - _chain_exception_from_existing_exception_context(self, as_cause=True) - - -class DaemonRunnerInvalidActionError(DaemonRunnerError, ValueError): - """ Raised when specified action for DaemonRunner is invalid. """ - - def _chain_from_context(self): - # This exception is normally not caused by another. - _chain_exception_from_existing_exception_context(self, as_cause=False) - - -class DaemonRunnerStartFailureError(DaemonRunnerError, RuntimeError): - """ Raised when failure starting DaemonRunner. """ - - -class DaemonRunnerStopFailureError(DaemonRunnerError, RuntimeError): - """ Raised when failure stopping DaemonRunner. """ - - -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.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') - 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 _usage_exit(self, argv): - """ Emit a usage message, then exit. - - :param argv: The command-line arguments used to invoke the - program, as a sequence of strings. - :return: ``None``. - - """ - progname = os.path.basename(argv[0]) - usage_exit_code = 2 - action_usage = "|".join(self.action_funcs.keys()) - message = "usage: {progname} {usage}".format( - progname=progname, usage=action_usage) - emit_message(message) - sys.exit(usage_exit_code) - - def parse_args(self, argv=None): - """ Parse command-line arguments. - - :param argv: The command-line arguments used to invoke the - program, as a sequence of strings. - - :return: ``None``. - - The parser expects the first argument as the program name, the - second argument as the action to perform. - - If the parser fails to parse the arguments, emit a usage - message and exit the program. - - """ - if argv is None: - argv = sys.argv - - min_args = 2 - if len(argv) < min_args: - self._usage_exit(argv) - - self.action = unicode(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. - - :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 _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(): - error = DaemonRunnerStopFailureError( - "PID file {pidfile.path!r} not locked".format( - pidfile=self.pidfile)) - raise error - - 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, - } - - 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 emit_message(message, stream=None): - """ Emit a message to the specified stream (default `sys.stderr`). """ - if stream is None: - stream = sys.stderr - stream.write("{message}\n".format(message=message)) - stream.flush() - - -def make_pidlockfile(path, acquire_timeout): - """ Make a PIDLockFile instance with the given filesystem path. """ - if not isinstance(path, basestring): - error = ValueError("Not a filesystem path: {path!r}".format( - path=path)) - raise error - if not os.path.isabs(path): - error = ValueError("Not an absolute path: {path!r}".format( - path=path)) - raise error - lockfile = pidfile.TimeoutPIDLockFile(path, acquire_timeout) - - return lockfile - - -def is_pidfile_stale(pidfile): - """ Determine whether a PID file is stale. - - :return: ``True`` iff the PID file is stale; otherwise ``False``. - - The PID file is “stale” if its contents are valid but do not - match the PID of a currently-running process. - - """ - result = False - - pidfile_pid = pidfile.read_pid() - if pidfile_pid is not None: - try: - os.kill(pidfile_pid, signal.SIG_DFL) - except ProcessLookupError: - # The specified PID does not exist. - result = True - except OSError as exc: - if exc.errno == errno.ESRCH: - # Under Python 2, process lookup error is an OSError. - # The specified PID does not exist. - result = True - - return result - - -# Local variables: -# coding: utf-8 -# mode: python -# End: -# vim: fileencoding=utf-8 filetype=python : |