diff options
| -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 4e8db628..a9156e7b 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 00000000..5e2e41b1 --- /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 88843df7..24524bb0 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 68f7b2ac..00000000 --- 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 de9025d3..00000000 --- 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 : | 
