Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Cleanup the subprocess code

  • Loading branch information...
commit fcabbb4a2c3f758c6b5506a8d05031870596ca55 1 parent 2d31400
@haypo authored
Showing with 54 additions and 97 deletions.
  1. +41 −67 sandbox/subprocess.py
  2. +13 −30 sandbox/subprocess_child.py
View
108 sandbox/subprocess.py
@@ -1,14 +1,13 @@
from __future__ import absolute_import
from sandbox import SandboxError, Timeout
+from sandbox.subprocess_child import call_child
+from signal import SIGALRM
import fcntl
import os
import pickle
import subprocess
import sys
-from signal import SIGALRM
-from sandbox.subprocess_child import child_call, USE_STDIN_PIPE, USE_STDOUT_PIPE
-if not USE_STDOUT_PIPE:
- import tempfile
+import tempfile
def set_cloexec_flag(fd):
try:
@@ -18,6 +17,33 @@ def set_cloexec_flag(fd):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags | cloexec_flag)
+def call_parent(pid, rpipe):
+ try:
+ status = os.waitpid(pid, 0)[1]
+ except:
+ os.close(rpipe)
+ raise
+ if status != 0:
+ if os.WIFSIGNALED(status):
+ signum = os.WTERMSIG(status)
+ if signum == SIGALRM:
+ raise Timeout()
+ text = "subprocess killed by signal %s" % signum
+ elif os.WIFEXITED(status):
+ exitcode = os.WEXITSTATUS(status)
+ text = "subprocess failed with exit code %s" % exitcode
+ else:
+ text = "subprocess failed"
+ raise SandboxError(text)
+ rpipe_file = os.fdopen(rpipe, 'rb')
+ try:
+ data = pickle.load(rpipe_file)
+ finally:
+ rpipe_file.close()
+ if 'error' in data:
+ raise data['error']
+ return data['result']
+
def call_fork(sandbox, func, args, kw):
rpipe, wpipe = os.pipe()
set_cloexec_flag(wpipe)
@@ -25,48 +51,22 @@ def call_fork(sandbox, func, args, kw):
if pid == 0:
os.close(rpipe)
try:
- child_call(wpipe, sandbox, func, args, kw)
+ call_child(wpipe, sandbox, func, args, kw)
finally:
# FIXME: handle error differently?
os._exit(1)
else:
os.close(wpipe)
- try:
- status = os.waitpid(pid, 0)[1]
- except:
- os.close(rpipe)
- raise
- if status != 0:
- if os.WIFSIGNALED(status):
- signum = os.WTERMSIG(status)
- if signum == SIGALRM:
- raise Timeout()
- text = "subprocess killed by signal %s" % signum
- elif os.WIFEXITED(status):
- exitcode = os.WEXITSTATUS(status)
- text = "subprocess failed with exit code %s" % exitcode
- else:
- text = "subprocess failed"
- raise SandboxError(text)
- rpipe_file = os.fdopen(rpipe, 'rb')
- try:
- data = pickle.load(rpipe_file)
- finally:
- rpipe_file.close()
- if 'error' in data:
- raise data['error']
- return data['result']
+ return call_parent(pid, rpipe)
def execute_subprocess(sandbox, code, globals, locals):
# prepare data
input_data = {
'code': code,
'config': sandbox.config,
+ 'locals': locals,
+ 'globals': globals,
}
- if locals is not None:
- input_data['locals'] = locals
- if globals is not None:
- input_data['globals'] = globals
# FIXME: use '-S'
args = (sys.executable, '-E', '-m', 'sandbox.subprocess_child')
@@ -74,36 +74,17 @@ def execute_subprocess(sandbox, code, globals, locals):
'close_fds': True,
'shell': False,
}
- if USE_STDIN_PIPE:
- kw['stdin'] = subprocess.PIPE
- else:
- args += (pickle.dumps(input_data),)
- if USE_STDOUT_PIPE:
- kw['stdout'] = subprocess.PIPE
- else:
- output_file = tempfile.NamedTemporaryFile()
- args += (output_file.name,)
+ args += (pickle.dumps(input_data),)
+ output_file = tempfile.NamedTemporaryFile()
+ args += (output_file.name,)
try:
# create the subprocess
process = subprocess.Popen(args, **kw)
# wait data
- if USE_STDOUT_PIPE:
- if USE_STDIN_PIPE:
- stdout, stderr = process.communicate(pickle.dumps(input_data))
- else:
- stdout, stderr = process.communicate()
- else:
- if USE_STDIN_PIPE:
- pickle.dump(process.stdin)
- process.stdin.flush()
exitcode = process.wait()
if exitcode:
- if USE_STDOUT_PIPE:
- sys.stdout.write(stdout)
- sys.stdout.flush()
-
if os.name != "nt" and exitcode < 0:
signum = -exitcode
if signum == SIGALRM:
@@ -113,23 +94,16 @@ def execute_subprocess(sandbox, code, globals, locals):
text = "subprocess failed with exit code %s" % exitcode
raise SandboxError(text)
- if USE_STDOUT_PIPE:
- output_data = pickle.loads(stdout)
- else:
- output_data = pickle.load(output_file)
+ output_data = pickle.load(output_file)
finally:
- if not USE_STDOUT_PIPE:
- output_file.close()
+ output_file.close()
- if 'stdout' in output_data:
- sys.stdout.write(output_data['stdout'])
- sys.stdout.flush()
if 'error' in output_data:
raise output_data['error']
- if 'locals' in input_data:
+ if input_data['locals'] is not None:
input_data['locals'].clear()
input_data['locals'].update(output_data['locals'])
- if 'globals' in input_data:
+ if input_data['globals'] is not None:
input_data['globals'].clear()
input_data['globals'].update(output_data['globals'])
return output_data['result']
View
43 sandbox/subprocess_child.py
@@ -3,17 +3,12 @@
import pickle
from sandbox import Sandbox
-USE_STDIN_PIPE = False
-USE_STDOUT_PIPE = False
-if USE_STDOUT_PIPE:
- from cStringIO import StringIO
-
try:
import resource
except ImportError:
resource = None
-def enable_process_limits(config):
+def set_process_limits(config):
if resource is not None:
# deny fork and thread
resource.setrlimit(resource.RLIMIT_NPROC, (0, 0))
@@ -26,8 +21,7 @@ def enable_process_limits(config):
if not config.has_feature("stdout") \
or not config.has_feature("stderr"):
devnull = os.open(os.devnull, os.O_WRONLY)
- if not USE_STDOUT_PIPE \
- and not config.has_feature("stdout"):
+ if not config.has_feature("stdout"):
stdout_fd = sys.__stdout__.fileno()
os.dup2(devnull, stdout_fd)
if not config.has_feature("stderr"):
@@ -46,24 +40,14 @@ def enable_process_limits(config):
signal.alarm(seconds)
def execute_child():
- if USE_STDOUT_PIPE:
- output = sys.stdout
- sys.stdout = StringIO()
- else:
- if USE_STDIN_PIPE:
- output_filename = sys.argv[1]
- else:
- output_filename = sys.argv[2]
- output = open(output_filename, "wb")
+ output_filename = sys.argv[2]
+ output = open(output_filename, "wb")
base_exception = BaseException
try:
- if USE_STDIN_PIPE:
- input_data = pickle.load(sys.stdin)
- else:
- input_data = sys.argv[1]
- input_data = pickle.loads(input_data)
+ input_data = sys.argv[1]
+ input_data = pickle.loads(input_data)
config = input_data['config']
- enable_process_limits(config)
+ set_process_limits(config)
sandbox = Sandbox(config)
code = input_data['code']
@@ -72,24 +56,21 @@ def execute_child():
result = sandbox._execute(code, globals, locals)
output_data = {'result': result}
- if 'globals' in input_data:
+ if input_data['globals'] is not None:
del globals['__builtins__']
output_data['globals'] = globals
if 'locals' in input_data:
output_data['locals'] = locals
except base_exception, err:
output_data = {'error': err}
- if USE_STDOUT_PIPE:
- output_data['stdout'] = sys.stdout.getvalue()
pickle.dump(output_data, output)
output.flush()
- if not USE_STDOUT_PIPE:
- output.close()
+ output.close()
-def child_call(wpipe, sandbox, func, args, kw):
+def call_child(wpipe, sandbox, func, args, kw):
config = sandbox.config
try:
- enable_process_limits(config)
+ set_process_limits(config)
result = sandbox._call(func, args, kw)
data = {'result': result}
except BaseException, err:
@@ -100,8 +81,10 @@ def child_call(wpipe, sandbox, func, args, kw):
output.close()
if config.has_feature("stdout"):
sys.stdout.flush()
+ sys.stdout.close()
if config.has_feature("stderr"):
sys.stderr.flush()
+ sys.stderr.close()
os._exit(0)
if __name__ == "__main__":
Please sign in to comment.
Something went wrong with that request. Please try again.