-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathruncode.py
More file actions
208 lines (161 loc) · 6.76 KB
/
runcode.py
File metadata and controls
208 lines (161 loc) · 6.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# Referenced:
# https://stackoverflow.com/a/8577226/11039508
# https://stackoverflow.com/a/2257449/11039508
import os
import tempfile
import subprocess
from sandbox import Sandbox
import string
import random
import time
import requests
# Handles running the code in the specified language
# Returns the output from running the code (stdout and stderr) with the exit status
class CodeDriver:
def removeFile(self, path):
os.remove(path)
# Runs a command-line command piping stdout and stderr
def handleSub(self, args, input_arg=None):
result = subprocess.run(args,
input=input_arg.encode('utf-8'),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
exit_status = result.returncode
output = result.stdout.decode("utf-8")
error = result.stderr.decode("utf-8")
return exit_status, output, error
# Handles runing code that requires two command-line calls
# Typically, used for code that requires being compiled as opposed to an interpreter
def handleCompiled(self, comp_args, exec_args, exec_path):
# Compiles the code
exit_status, output, error = self.handleSub(comp_args)
# Runs compiled file if the code comiled succesfully
if (exit_status == 0):
run_exit, run_out, run_err = self.handleSub(exec_args)
exit_status = run_exit
output += "\n" + run_out
error += "\n" + run_err
# Removes compiled file
self.removeFile(exec_path)
return exit_status, output, error
# Runs the file at `path` containing appropriately given a language `lang`
# Returns the exit status, stdout, and stderr
def runFile(self, lang, path):
# Runs the code
if (lang == 'python'):
# Runs Python code
return self.handleSub(['python3', path])
elif (lang == 'c'):
# Runs C code
exec_path = path + "-exec"
comp_args = ['gcc', '-x', 'c', path, '-o', exec_path]
exec_args = [exec_path]
return self.handleCompiled(comp_args, exec_args, exec_path)
elif (lang == 'c++'):
# Runs C code
exec_path = path + "-exec"
comp_args = ['g++', '-x', 'c++', path, '-o', exec_path]
exec_args = [exec_path]
return self.handleCompiled(comp_args, exec_args, exec_path)
elif (lang == 'java'):
# Runs Java code
exec_path = path + "-exec.java"
comp_args = ['cp', path, exec_path]
exec_args = ['java', exec_path]
return self.handleCompiled(comp_args, exec_args, exec_path)
elif (lang == "sml"):
# Runs SML/NJ Code
input_arg = 'use "' + path + '";\n'
exit_status, output, error = self.handleSub(['sml'], input_arg)
if (exit_status == 0):
# Removes unneeded interpreter output
output_lines = output.split('\n')
adj = -3 if (output_lines[-3] == "val it = () : unit") else -2
output = '\n'.join(output_lines[2:adj])
# Changes exit status if needed
if (adj == -2):
exit_status = 1
return exit_status, output, error
elif (lang == "js" or lang == "javascript"):
# Runs JavaScript Code
return self.handleSub(['node', path])
elif (lang == "bash"):
# Runs Bash Code
return self.handleSub(['bash', path])
else:
# Language Not Supported
exit_status = -1
output = ""
error = 'Language "' + lang + '" is currently not supported. For a list of supported languages run the `help code` command.'
return exit_status, output, error
# Runs the code provided verbatim in `code` in the language specified by `lang`
# Returns the exit status, stdout, and stderr
def run(self, lang, code):
# Creates temporary file to writes code to it
# Then runs the code appropriately
fd, path = tempfile.mkstemp()
try:
# Writes code to the temp file
with os.fdopen(fd, 'w') as tmp:
tmp.write(code)
exit_status, output, error = self.runFile(lang, path)
except:
exit_status = -1
output = ""
error = "CodeBot has encountered an unexpected error."
finally:
self.removeFile(path)
return exit_status, output, error
def genHere(input_arg):
new_here = lambda : ''.join(random.choices(string.ascii_uppercase + string.digits, k=32))
here = new_here()
while here in input_arg:
here = new_here()
return ' <<' + here + "\n" + input_arg + here
# Child class of CodeDriver
# Handles running the code in the specified language within a secure sandboxed environment
# Returns the output from running the code (stdout and stderr) with the exit status
class CodeDriverSecure(CodeDriver):
def removeFile(self, path):
self.sandbox.exec("rm " + path)
def handleSub(self, args, input_arg=None):
cmd = " ".join(args)
if (input_arg != None):
cmd += genHere(input_arg)
obj = self.sandbox.exec(cmd)
return obj['exit'], obj['out'], obj['err']
def run(self, lang, code, cmd=False):
# Creates sandbox
self.sandbox = Sandbox()
# Waits for sandbox server to be running by attempting HEAD requests
addr = 'http://localhost:' + str(self.sandbox.port)
while True:
try:
requests.head(addr)
# If request didn't fail, sandbox server up
break
except:
# Still need to wait
time.sleep(0.01)
# Checks if we need to run a bash command or a file of code
if (cmd):
# We just need to run a single bash command
ret = self.handleSub([code])
else:
# We need to run an entire file
# Creates file containing code to run in the sandbox
path = "/.codebot/code"
cmd = "cat > " + path + genHere(code)
temp = self.sandbox.exec(cmd)
if (temp['exit'] != 0):
# Error occured while creating code file
exit_status = -1
output = ""
error = "CodeBot has encountered an unexpected error."
ret = (exit_status, output, error)
else:
# Runs the code in the sandbox
ret = self.runFile(lang, path)
# Closes the sandbox
del self.sandbox
return ret