Skip to content

Commit

Permalink
Set CLOEXEC on subprocess pipe endpoints so they are not inherited by…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
bdarnell committed Jun 12, 2013
1 parent 44dbe6a commit 3cf9098
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 3 deletions.
14 changes: 11 additions & 3 deletions tornado/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions tornado/test/process_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')"],
Expand Down

0 comments on commit 3cf9098

Please sign in to comment.