summaryrefslogtreecommitdiff
path: root/pkg/osx/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/osx/daemon')
-rw-r--r--pkg/osx/daemon/LICENSE15
-rw-r--r--pkg/osx/daemon/_metadata.py149
-rw-r--r--pkg/osx/daemon/pidfile.py67
-rw-r--r--pkg/osx/daemon/runner.py324
4 files changed, 16 insertions, 539 deletions
diff --git a/pkg/osx/daemon/LICENSE b/pkg/osx/daemon/LICENSE
new file mode 100644
index 0000000..5e2e41b
--- /dev/null
+++ b/pkg/osx/daemon/LICENSE
@@ -0,0 +1,15 @@
+Copying
+=======
+
+This work, ‘python-daemon’, is free software: you may copy, modify,
+and/or distribute this work under certain conditions; see the relevant
+files for specific grant of license. No warranty expressed or implied.
+
+* Parts of this work are licensed to you under the terms of the GNU
+General Public License as published by the Free Software Foundation;
+version 3 of that license or any later version.
+See the file ‘LICENSE.GPL-3’ for details.
+
+* Parts of this work are licensed to you under the terms of the Apache
+License, version 2.0 as published by the Apache Software Foundation.
+See the file ‘LICENSE.ASF-2’ for details.
diff --git a/pkg/osx/daemon/_metadata.py b/pkg/osx/daemon/_metadata.py
index 88843df..24524bb 100644
--- a/pkg/osx/daemon/_metadata.py
+++ b/pkg/osx/daemon/_metadata.py
@@ -1,154 +1,7 @@
-# -*- coding: utf-8 -*-
-
-# daemon/_metadata.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# 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.
-
-""" Package metadata for the ‘python-daemon’ distribution. """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import json
-import re
-import collections
-import datetime
-
-import pkg_resources
-
-
-distribution_name = "python-daemon"
-version_info_filename = "version_info.json"
-
-
-def get_distribution_version_info(filename=version_info_filename):
- """ Get the version info from the installed distribution.
-
- :param filename: Base filename of the version info resource.
- :return: The version info as a mapping of fields. If the
- distribution is not available, the mapping is empty.
-
- The version info is stored as a metadata file in the
- distribution.
-
- """
- version_info = {
- 'release_date': "UNKNOWN",
- 'version': "UNKNOWN",
- 'maintainer': "UNKNOWN",
- }
-
- try:
- distribution = pkg_resources.get_distribution(distribution_name)
- except pkg_resources.DistributionNotFound:
- distribution = None
-
- if distribution is not None:
- if distribution.has_metadata(version_info_filename):
- content = distribution.get_metadata(version_info_filename)
- version_info = json.loads(content)
-
- return version_info
-
-version_info = get_distribution_version_info()
-
-version_installed = version_info['version']
-
-
-rfc822_person_regex = re.compile(
- "^(?P<name>[^<]+) <(?P<email>[^>]+)>$")
-
-ParsedPerson = collections.namedtuple('ParsedPerson', ['name', 'email'])
-
-
-def parse_person_field(value):
- """ Parse a person field into name and email address.
-
- :param value: The text value specifying a person.
- :return: A 2-tuple (name, email) for the person's details.
-
- If the `value` does not match a standard person with email
- address, the `email` item is ``None``.
-
- """
- result = (None, None)
-
- match = rfc822_person_regex.match(value)
- if len(value):
- if match is not None:
- result = ParsedPerson(
- name=match.group('name'),
- email=match.group('email'))
- else:
- result = ParsedPerson(name=value, email=None)
-
- return result
-
author_name = "Ben Finney"
author_email = "ben+python@benfinney.id.au"
-author = "{name} <{email}>".format(name=author_name, email=author_email)
-
-
-class YearRange:
- """ A range of years spanning a period. """
-
- def __init__(self, begin, end=None):
- self.begin = begin
- self.end = end
-
- def __unicode__(self):
- text = "{range.begin:04d}".format(range=self)
- if self.end is not None:
- if self.end > self.begin:
- text = "{range.begin:04d}–{range.end:04d}".format(range=self)
- return text
-
- __str__ = __unicode__
-
-
-def make_year_range(begin_year, end_date=None):
- """ Construct the year range given a start and possible end date.
-
- :param begin_date: The beginning year (text) for the range.
- :param end_date: The end date (text, ISO-8601 format) for the
- range, or a non-date token string.
- :return: The range of years as a `YearRange` instance.
-
- If the `end_date` is not a valid ISO-8601 date string, the
- range has ``None`` for the end year.
-
- """
- begin_year = int(begin_year)
-
- try:
- end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d")
- except (TypeError, ValueError):
- # Specified end_date value is not a valid date.
- end_year = None
- else:
- end_year = end_date.year
-
- year_range = YearRange(begin=begin_year, end=end_year)
-
- return year_range
-
-copyright_year_begin = "2001"
-build_date = version_info['release_date']
-copyright_year_range = make_year_range(copyright_year_begin, build_date)
-
+year_range = "2001-2017"
copyright = "Copyright © {year_range} {author} and others".format(
year_range=copyright_year_range, author=author)
license = "Apache-2"
url = "https://alioth.debian.org/projects/python-daemon/"
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/pkg/osx/daemon/pidfile.py b/pkg/osx/daemon/pidfile.py
deleted file mode 100644
index 68f7b2a..0000000
--- a/pkg/osx/daemon/pidfile.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# daemon/pidfile.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# 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.
-
-""" Lockfile behaviour implemented via Unix PID files.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-from lockfile.pidlockfile import PIDLockFile
-
-
-class TimeoutPIDLockFile(PIDLockFile, object):
- """ Lockfile with default timeout, implemented as a Unix PID file.
-
- This uses the ``PIDLockFile`` implementation, with the
- following changes:
-
- * The `acquire_timeout` parameter to the initialiser will be
- used as the default `timeout` parameter for the `acquire`
- method.
-
- """
-
- def __init__(self, path, acquire_timeout=None, *args, **kwargs):
- """ Set up the parameters of a TimeoutPIDLockFile.
-
- :param path: Filesystem path to the PID file.
- :param acquire_timeout: Value to use by default for the
- `acquire` call.
- :return: ``None``.
-
- """
- self.acquire_timeout = acquire_timeout
- super(TimeoutPIDLockFile, self).__init__(path, *args, **kwargs)
-
- def acquire(self, timeout=None, *args, **kwargs):
- """ Acquire the lock.
-
- :param timeout: Specifies the timeout; see below for valid
- values.
- :return: ``None``.
-
- The `timeout` defaults to the value set during
- initialisation with the `acquire_timeout` parameter. It is
- passed to `PIDLockFile.acquire`; see that method for
- details.
-
- """
- if timeout is None:
- timeout = self.acquire_timeout
- super(TimeoutPIDLockFile, self).acquire(timeout, *args, **kwargs)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
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 :