PKX(HĠXvnsenter/__init__.py#!/usr/bin/env python """ nsenter - run program with namespaces of other processes """ import argparse import ctypes import errno import os import logging from pathlib import Path try: from contextlib import ExitStack except ImportError: from contextlib2 import ExitStack NAMESPACE_NAMES = frozenset(['mnt', 'ipc', 'net', 'pid', 'user', 'uts']) class Namespace(object): """A context manager for entering namespaces Args: pid: The PID for the owner of the namespace to enter, or an absolute path to a file which represents a namespace handle. ns_type: The type of namespace to enter must be one of mnt ipc net pid user uts. If pid is an absolute path, this much match the type of namespace it represents proc: The path to the /proc file system. If running in a container the host proc file system may be binded mounted in a different location Raises: IOError: A non existent PID was provided ValueError: An improper ns_type was provided OSError: Unable to enter or exit the namespace Example: with Namespace(916, 'net'): #do something in the namespace pass with Namespace('/var/run/netns/foo', 'net'): #do something in the namespace pass """ _log = logging.getLogger(__name__) _libc = ctypes.CDLL('libc.so.6', use_errno=True) def __init__(self, pid, ns_type, proc='/proc'): if ns_type not in NAMESPACE_NAMES: raise ValueError('ns_type must be one of {0}'.format( ', '.join(NAMESPACE_NAMES) )) self.pid = pid self.ns_type = ns_type self.proc = proc # if it's numeric, then it's a pid, else assume it's a path try: pid = int(pid) self.target_fd = self._nsfd(pid, ns_type).open() except ValueError: self.target_fd = Path(pid).open() self.target_fileno = self.target_fd.fileno() self.parent_fd = self._nsfd('self', ns_type).open() self.parent_fileno = self.parent_fd.fileno() __init__.__annotations__ = {'pid': str, 'ns_type': str} def _nsfd(self, pid, ns_type): """Utility method to build a pathlib.Path instance pointing at the requested namespace entry Args: pid: The PID ns_type: The namespace type to enter Returns: pathlib.Path pointing to the /proc namespace entry """ return Path(self.proc) / str(pid) / 'ns' / ns_type _nsfd.__annotations__ = {'process': str, 'ns_type': str, 'return': Path} def _close_files(self): """Utility method to close our open file handles""" try: self.target_fd.close() except: pass if self.parent_fd is not None: self.parent_fd.close() def __enter__(self): self._log.debug('Entering %s namespace %s', self.ns_type, self.pid) if self._libc.setns(self.target_fileno, 0) == -1: e = ctypes.get_errno() self._close_files() raise OSError(e, errno.errorcode[e]) def __exit__(self, type, value, tb): self._log.debug('Leaving %s namespace %s', self.ns_type, self.pid) if self._libc.setns(self.parent_fileno, 0) == -1: e = ctypes.get_errno() self._close_files() raise OSError(e, errno.errorcode[e]) self._close_files() def main(): # pragma: no cover """Command line interface to the Namespace context manager""" parser = argparse.ArgumentParser(prog='nsenter', description=__doc__) parser.add_argument('--target', '-t', required=True, metavar='PID', help='A target process to get contexts from') parser.add_argument('--proc', '-p', metavar='PROCFS', default='/proc', help='The target proc file system') group = parser.add_argument_group('Namespaces') for ns in NAMESPACE_NAMES: group.add_argument('--{0}'.format(ns), action='store_true', help='Enter the {0} namespace'.format(ns) ) parser.add_argument('--all', action='store_true', help='Enter all namespaces' ) parser.add_argument('command', nargs='*', default=['/bin/sh']) args = parser.parse_args() # make sure we have --all or at least one namespace if (True not in [getattr(args, ns) for ns in NAMESPACE_NAMES] and not args.all): parser.error('You must specify at least one namespace') try: with ExitStack() as stack: namespaces = [] for ns in NAMESPACE_NAMES: if getattr(args, ns) or args.all: namespaces.append(Namespace(args.target, ns, proc=args.proc)) for ns in namespaces: stack.enter_context(ns) os.execlp(args.command[0], *args.command) except IOError as exc: parser.error('Unable to access PID: {0}'.format(exc)) except OSError as exc: parser.error('Unable to enter {0} namespace: {1}'.format( ns.ns_type, exc )) if __name__ == '__main__': # pragma: no cover main() PKXY(H#G%nsenter-0.2.dist-info/DESCRIPTION.rst======= NSEnter ======= .. image:: https://travis-ci.org/zalando/python-nsenter.svg?branch=master :target: https://travis-ci.org/zalando/python-nsenter :alt: Travis CI build status This Python package allows entering Linux kernel namespaces (mount, IPC, net, PID, user and UTS) by doing the "setns" syscall. The command line interface tries to be similar to the nsenter_ C program. Requires Python 2.6 or higher See the introductory `blog post "Entering Kernel Namespaces from Python"`_. Install from PyPI:: sudo pip3 install nsenter Install from git source:: python3 setup.py install Example command line usage:: docker run -d --name=redis -t redis sudo nsenter --all --target=`docker inspect --format '{{ .State.Pid }}' redis` /bin/bash Example usage from Python: .. code:: python import subprocess from nsenter import Namespace with Namespace(mypid, 'net'): # output network interfaces as seen from within the mypid's net NS: subprocess.check_output(['ip', 'a']) # or enter an arbitrary namespace: with Namespace('/var/run/netns/foo', 'net'): # output network interfaces as seen from within the net NS "foo": subprocess.check_output(['ip', 'a']) .. _nsenter: http://man7.org/linux/man-pages/man1/nsenter.1.html .. _blog post "Entering Kernel Namespaces from Python": http://tech.zalando.com/posts/entering-kernel-namespaces-with-python.html PKXY(HJw$**&nsenter-0.2.dist-info/entry_points.txt[console_scripts] nsenter = nsenter:main PKXY(H-ĕ#nsenter-0.2.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Operating System :: POSIX :: Linux", "License :: OSI Approved :: Apache Software License"], "extensions": {"python.commands": {"wrap_console": {"nsenter": "nsenter:main"}}, "python.details": {"contacts": [{"email": "henning.jacobs@zalando.de", "name": "Henning Jacobs", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/zalando/python-nsenter"}}, "python.exports": {"console_scripts": {"nsenter": "nsenter:main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["docker", "container", "namespace", "kernel", "setns"], "license": "Apache License 2.0", "metadata_version": "2.0", "name": "nsenter", "run_requires": [{"requires": ["argparse", "contextlib2", "pathlib"]}], "summary": "Enter kernel namespaces from Python", "version": "0.2"}PKXY(H