Commit b17d2af5 authored by Martín Ferrari's avatar Martín Ferrari

Subprocess class and tests, fix some other stuff

parent bfb3a065
...@@ -64,6 +64,7 @@ def _start_child(debug = False): ...@@ -64,6 +64,7 @@ def _start_child(debug = False):
s1.close() s1.close()
return (s0, pid) return (s0, pid)
# FIXME: clean up signal handers, atexit functions, etc.
try: try:
s0.close() s0.close()
srv = netns.protocol.Server(s1, debug) srv = netns.protocol.Server(s1, debug)
......
...@@ -76,17 +76,17 @@ def spawn(executable, argv = None, cwd = None, env = None, ...@@ -76,17 +76,17 @@ def spawn(executable, argv = None, cwd = None, env = None,
if cwd != None: if cwd != None:
os.chdir(cwd) os.chdir(cwd)
if not argv: if not argv:
argv = [ file ] argv = [ executable ]
if '/' in file: # Should not search in PATH if '/' in executable: # Should not search in PATH
if env != None: if env != None:
os.execve(file, argv, env) os.execve(executable, argv, env)
else: else:
os.execv(file, argv) os.execv(executable, argv)
else: # use PATH else: # use PATH
if env != None: if env != None:
os.execvpe(file, argv, env) os.execvpe(executable, argv, env)
else: else:
os.execvp(file, argv) os.execvp(executable, argv)
raise RuntimeError("Unreachable reached!") raise RuntimeError("Unreachable reached!")
except: except:
try: try:
...@@ -119,6 +119,7 @@ def spawn(executable, argv = None, cwd = None, env = None, ...@@ -119,6 +119,7 @@ def spawn(executable, argv = None, cwd = None, env = None,
os.waitpid(pid, 0) os.waitpid(pid, 0)
exc = pickle.loads(s) exc = pickle.loads(s)
# XXX: sys.excepthook # XXX: sys.excepthook
#print exc.child_traceback
raise exc raise exc
# Used to print extra info in nested exceptions # Used to print extra info in nested exceptions
...@@ -145,3 +146,44 @@ def wait(pid): ...@@ -145,3 +146,44 @@ def wait(pid):
"""Wait for process to die and return the exit code.""" """Wait for process to die and return the exit code."""
return os.waitpid(pid, 0)[1] return os.waitpid(pid, 0)[1]
class Subprocess(object):
# FIXME: this is the visible interface; documentation should move here.
"""OO-style interface to spawn(), but invoked through the controlling
process."""
# FIXME
default_user = None
def __init__(self, node, executable, argv = None, cwd = None, env = None,
stdin = None, stdout = None, stderr = None, user = None):
self._slave = node._slave
if user == None:
user = Subprocess.default_user
# confusingly enough, to go to the function at the top of this file,
# I need to call it thru the communications protocol: remember that
# happens in another process!
self._pid = self._slave.spawn(executable, argv = argv, cwd = cwd,
env = env, stdin = stdin, stdout = stdout, stderr = stderr,
user = user)
node._add_subprocess(self)
@property
def pid(self):
return self._pid
def poll(self):
r = self._slave.poll(self._pid)
if r != None:
del self._pid
self.return_value = r
return r
def wait(self):
r = self._slave.wait(self._pid)
del self._pid
self.return_value = r
return r
def signal(self, sig = signal.SIGTERM):
return self._slave.signal(self._pid, sig)
...@@ -54,37 +54,64 @@ class TestSubprocess(unittest.TestCase): ...@@ -54,37 +54,64 @@ class TestSubprocess(unittest.TestCase):
# XXX: unittest still cannot skip tests # XXX: unittest still cannot skip tests
#@unittest.skipUnless(os.getuid() == 0, "Test requires root privileges") #@unittest.skipUnless(os.getuid() == 0, "Test requires root privileges")
@test_util.skipUnless(os.getuid() == 0, "Test requires root privileges") @test_util.skipUnless(os.getuid() == 0, "Test requires root privileges")
def test_popen_chuser(self): def test_spawn_chuser(self):
user = 'nobody' user = 'nobody'
pid = netns.subprocess.spawn(user, '/bin/sleep', ['/bin/sleep', '100']) pid = netns.subprocess.spawn('/bin/sleep', ['/bin/sleep', '100'],
user = user)
self._check_ownership(user, pid) self._check_ownership(user, pid)
os.kill(pid, signal.SIGTERM) os.kill(pid, signal.SIGTERM)
self.assertEquals(netns.subprocess.wait(pid), signal.SIGTERM) self.assertEquals(netns.subprocess.wait(pid), signal.SIGTERM)
def test_popen_basic(self): def test_spawn_basic(self):
# User does not exist # User does not exist
self.assertRaises(ValueError, netns.subprocess.spawn, self.assertRaises(ValueError, netns.subprocess.spawn,
self.nouser, '/bin/sleep', ['/bin/sleep', '1000']) '/bin/sleep', ['/bin/sleep', '1000'], user = self.nouser)
self.assertRaises(ValueError, netns.subprocess.spawn, self.assertRaises(ValueError, netns.subprocess.spawn,
self.nouid, '/bin/sleep', ['/bin/sleep', '1000']) '/bin/sleep', ['/bin/sleep', '1000'], user = self.nouid)
# Invalid CWD: it is a file # Invalid CWD: it is a file
self.assertRaises(OSError, netns.subprocess.spawn, self.assertRaises(OSError, netns.subprocess.spawn,
None, '/bin/sleep', None, cwd = '/bin/sleep') '/bin/sleep', cwd = '/bin/sleep')
# Invalid CWD: does not exist # Invalid CWD: does not exist
self.assertRaises(OSError, netns.subprocess.spawn, self.assertRaises(OSError, netns.subprocess.spawn,
None, '/bin/sleep', None, cwd = self.nofile) '/bin/sleep', cwd = self.nofile)
# Exec failure # Exec failure
self.assertRaises(OSError, netns.subprocess.spawn, self.assertRaises(OSError, netns.subprocess.spawn, self.nofile)
None, self.nofile, None)
# Test that the environment is cleared: sleep should not be found # Test that the environment is cleared: sleep should not be found
# XXX: This should be a python bug: if I don't set PATH explicitly, it # XXX: This should be a python bug: if I don't set PATH explicitly, it
# uses a default search path # uses a default search path
self.assertRaises(OSError, netns.subprocess.spawn, self.assertRaises(OSError, netns.subprocess.spawn,
None, 'sleep', None, env = {'PATH': ''}) 'sleep', env = {'PATH': ''})
#p = netns.subprocess.spawn(None, '/bin/sleep', ['/bin/sleep', '1000'], #p = netns.subprocess.spawn(None, '/bin/sleep', ['/bin/sleep', '1000'],
# cwd = '/', env = []) # cwd = '/', env = [])
# FIXME: tests fds # FIXME: tests fds
@test_util.skipUnless(os.getuid() == 0, "Test requires root privileges")
def test_Subprocess_basic(self):
node = netns.Node()
# User does not exist
self.assertRaises(RuntimeError, netns.subprocess.Subprocess, node,
'/bin/sleep', ['/bin/sleep', '1000'], user = self.nouser)
self.assertRaises(RuntimeError, netns.subprocess.Subprocess, node,
'/bin/sleep', ['/bin/sleep', '1000'], user = self.nouid)
# Invalid CWD: it is a file
self.assertRaises(RuntimeError, netns.subprocess.Subprocess, node,
'/bin/sleep', cwd = '/bin/sleep')
# Invalid CWD: does not exist
self.assertRaises(RuntimeError, netns.subprocess.Subprocess, node,
'/bin/sleep', cwd = self.nofile)
# Exec failure
self.assertRaises(RuntimeError, netns.subprocess.Subprocess, node,
self.nofile)
# Test that the environment is cleared: sleep should not be found
# XXX: This should be a python bug: if I don't set PATH explicitly, it
# uses a default search path
self.assertRaises(RuntimeError, netns.subprocess.Subprocess, node,
'sleep', env = {'PATH': ''})
#p = netns.subprocess.Subprocess(None, '/bin/sleep', ['/bin/sleep', '1000'], cwd = '/', env = [])
# FIXME: tests fds
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment