diff options
Diffstat (limited to 'pkg/osx')
-rw-r--r-- | pkg/osx/README.rst | 3 | ||||
-rw-r--r-- | pkg/osx/daemon/LICENSE | 15 | ||||
-rw-r--r-- | pkg/osx/daemon/_metadata.py | 149 | ||||
-rw-r--r-- | pkg/osx/daemon/pidfile.py | 67 | ||||
-rw-r--r-- | pkg/osx/daemon/runner.py | 324 |
5 files changed, 18 insertions, 540 deletions
diff --git a/pkg/osx/README.rst b/pkg/osx/README.rst index 4e8db62..a9156e7 100644 --- a/pkg/osx/README.rst +++ b/pkg/osx/README.rst @@ -4,7 +4,8 @@ Helper files needed for OSX * The bitmask-helper that is run as root can be found in the source tree, in ``src/leap/bitmask/vpn/helpers/osx``. * python ``daemon`` is a dependency for the bitmask-helper, here it is vendored. -* The plist file ``se.leap.bitmask-helper.plist``. +* The plist file ``se.leap.bitmask-helper.plist`` (this should be installed into + /Library/LaunchDaemons/se.leap.bitmask-helper.plist). * OpenVPN up/down scripts: ``openvpn/client.down.sh`` and ``openvpn/client.up.sh``. 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 : |