From 3cf90980bc1f534229e3c63d241dcfedd38c8c4d Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Tue, 11 Jun 2013 23:32:40 -0400 Subject: [PATCH] Set CLOEXEC on subprocess pipe endpoints so they are not inherited by the child. This is what the subprocess module does when it creates pipes, and is necessary so that a close of the writing side of the stdin pipe will be recognized by the child process. --- tornado/process.py | 14 +++++++++++--- tornado/test/process_test.py | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tornado/process.py b/tornado/process.py index 438db66d2f..5f05b18282 100644 --- a/tornado/process.py +++ b/tornado/process.py @@ -33,6 +33,7 @@ from tornado import ioloop from tornado.iostream import PipeIOStream from tornado.log import gen_log +from tornado.platform.auto import set_close_exec from tornado import stack_context try: @@ -69,6 +70,13 @@ def _reseed_random(): random.seed(seed) +def _pipe_cloexec(): + r, w = os.pipe() + set_close_exec(r) + set_close_exec(w) + return r, w + + _task_id = None @@ -184,17 +192,17 @@ def __init__(self, *args, **kwargs): self.io_loop = kwargs.pop('io_loop', None) to_close = [] if kwargs.get('stdin') is Subprocess.STREAM: - in_r, in_w = os.pipe() + in_r, in_w = _pipe_cloexec() kwargs['stdin'] = in_r to_close.append(in_r) self.stdin = PipeIOStream(in_w, io_loop=self.io_loop) if kwargs.get('stdout') is Subprocess.STREAM: - out_r, out_w = os.pipe() + out_r, out_w = _pipe_cloexec() kwargs['stdout'] = out_w to_close.append(out_w) self.stdout = PipeIOStream(out_r, io_loop=self.io_loop) if kwargs.get('stderr') is Subprocess.STREAM: - err_r, err_w = os.pipe() + err_r, err_w = _pipe_cloexec() kwargs['stderr'] = err_w to_close.append(err_w) self.stderr = PipeIOStream(err_r, io_loop=self.io_loop) diff --git a/tornado/test/process_test.py b/tornado/test/process_test.py index 588488b95c..5299a12a47 100644 --- a/tornado/test/process_test.py +++ b/tornado/test/process_test.py @@ -154,6 +154,20 @@ def test_subprocess(self): data = self.wait() self.assertEqual(data, b"") + def test_close_stdin(self): + # Close the parent's stdin handle and see that the child recognizes it. + subproc = Subprocess([sys.executable, '-u', '-i'], + stdin=Subprocess.STREAM, + stdout=Subprocess.STREAM, stderr=subprocess.STDOUT, + io_loop=self.io_loop) + self.addCleanup(lambda: os.kill(subproc.pid, signal.SIGTERM)) + subproc.stdout.read_until(b'>>> ', self.stop) + self.wait() + subproc.stdin.close() + subproc.stdout.read_until_close(self.stop) + data = self.wait() + self.assertEqual(data, b"\n") + def test_stderr(self): subproc = Subprocess([sys.executable, '-u', '-c', r"import sys; sys.stderr.write('hello\n')"],