1- #!/usr/bin/env python3
1+ #!/usr/bin/env python
22
33"""
44nsenter - run program with namespaces of other processes
1515except ImportError :
1616 from contextlib2 import ExitStack
1717
18- NAMESPACE_NAMES = frozenset (['mnt' ,'ipc' ,'net' ,'pid' ,'user' ,'uts' ])
18+ NAMESPACE_NAMES = frozenset (['mnt' , 'ipc' , 'net' , 'pid' , 'user' , 'uts' ])
1919
2020
2121class Namespace (object ):
@@ -25,6 +25,9 @@ class Namespace(object):
2525 pid: The PID for the owner of the namespace to enter
2626 ns_type: The type of namespace to enter must be one of
2727 mnt ipc net pid user uts
28+ proc: The path to the /proc file system. If running in a container
29+ the host proc file system may be binded mounted in a different
30+ location
2831
2932 Raises:
3033 IOError: A non existent PID was provided
@@ -40,35 +43,36 @@ class Namespace(object):
4043 _log = logging .getLogger (__name__ )
4144 _libc = ctypes .CDLL ('libc.so.6' , use_errno = True )
4245
43- def __init__ (self , pid , ns_type ):
46+ def __init__ (self , pid , ns_type , proc = '/proc' ):
4447 if ns_type not in NAMESPACE_NAMES :
4548 raise ValueError ('ns_type must be one of {0}' .format (
4649 ', ' .join (NAMESPACE_NAMES )
4750 ))
4851
4952 self .pid = pid
5053 self .ns_type = ns_type
54+ self .proc = proc
5155
5256 self .target_fd = self ._nsfd (pid , ns_type ).open ()
5357 self .target_fileno = self .target_fd .fileno ()
5458
5559 self .parent_fd = self ._nsfd ('self' , ns_type ).open ()
5660 self .parent_fileno = self .parent_fd .fileno ()
5761
58- __init__ .__annotations__ = {'pid' : str , 'ns_type' : str }
62+ __init__ .__annotations__ = {'pid' : str , 'ns_type' : str }
5963
6064 def _nsfd (self , pid , ns_type ):
61- """Utility method to build a pathlib.Path instance pointing at the
65+ """Utility method to build a pathlib.Path instance pointing at the
6266 requested namespace entry
6367
6468 Args:
65- pid: The PID
69+ pid: The PID
6670 ns_type: The namespace type to enter
6771
6872 Returns:
6973 pathlib.Path pointing to the /proc namespace entry
7074 """
71- return Path ('/ proc' ) / str (pid ) / 'ns' / ns_type
75+ return Path (self . proc ) / str (pid ) / 'ns' / ns_type
7276
7377 _nsfd .__annotations__ = {'process' : str , 'ns_type' : str , 'return' : Path }
7478
@@ -78,50 +82,55 @@ def _close_files(self):
7882 self .target_fd .close ()
7983 except :
8084 pass
81- self .parent_fd .close ()
85+ self .parent_fd .close ()
8286
8387 def __enter__ (self ):
8488 self ._log .debug ('Entering %s namespace %s' , self .ns_type , self .pid )
8589
8690 if self ._libc .setns (self .target_fileno , 0 ) == - 1 :
8791 e = ctypes .get_errno ()
8892 self ._close_files ()
89- raise OSError (e , errno .errorcode [e ])
93+ raise OSError (e , errno .errorcode [e ])
9094
9195 def __exit__ (self , type , value , tb ):
9296 self ._log .debug ('Leaving %s namespace %s' , self .ns_type , self .pid )
93-
97+
9498 if self ._libc .setns (self .parent_fileno , 0 ) == - 1 :
9599 e = ctypes .get_errno ()
96100 self ._close_files ()
97- raise OSError (e , errno .errorcode [e ])
101+ raise OSError (e , errno .errorcode [e ])
98102
99103 self ._close_files ()
100-
101- def main (): #pragma: no cover
102- """Command line interface to the Namespace Contet Manager"""
104+
105+
106+ def main (): # pragma: no cover
107+ """Command line interface to the Namespace context manager"""
103108
104109 parser = argparse .ArgumentParser (prog = 'nsenter' , description = __doc__ )
105-
110+
106111 parser .add_argument ('--target' , '-t' , required = True , metavar = 'PID' ,
107112 help = 'A target process to get contexts from' )
108113
109114 group = parser .add_argument_group ('Namespaces' )
110115
111116 for ns in NAMESPACE_NAMES :
112- group .add_argument ('--{0}' .format (ns ), action = 'store_true' ,
113- help = 'Enter the {0} namespace' .format (ns ))
114-
115- parser .add_argument ('--all' , action = 'store_true' ,
116- help = 'Enter all namespaces' )
117+ group .add_argument ('--{0}' .format (ns ),
118+ action = 'store_true' ,
119+ help = 'Enter the {0} namespace' .format (ns )
120+ )
121+
122+ parser .add_argument ('--all' ,
123+ action = 'store_true' ,
124+ help = 'Enter all namespaces'
125+ )
117126
118127 parser .add_argument ('command' , nargs = '*' , default = ['/bin/sh' ])
119128
120129 args = parser .parse_args ()
121130
122- #make sure we have --all or at least one namespace
131+ # make sure we have --all or at least one namespace
123132 if (True not in [getattr (args , ns ) for ns in NAMESPACE_NAMES ]
124- and not args .all ):
133+ and not args .all ):
125134 parser .error ('You must specify at least one namespace' )
126135
127136 try :
@@ -130,9 +139,9 @@ def main(): #pragma: no cover
130139 for ns in NAMESPACE_NAMES :
131140 if getattr (args , ns ) or args .all :
132141 namespaces .append (Namespace (args .target , ns ))
133-
142+
134143 for ns in namespaces :
135- stack .enter_context (ns )
144+ stack .enter_context (ns )
136145
137146 os .execlp (args .command [0 ], * args .command )
138147 except IOError as exc :
@@ -143,5 +152,5 @@ def main(): #pragma: no cover
143152 ))
144153
145154
146- if __name__ == '__main__' : # pragma: no cover
155+ if __name__ == '__main__' : # pragma: no cover
147156 main ()
0 commit comments