diff options
Diffstat (limited to 'snekbox.py')
-rw-r--r-- | snekbox.py | 160 |
1 files changed, 89 insertions, 71 deletions
@@ -1,83 +1,101 @@ -import sys -import io import json import multiprocessing +import subprocess import threading import time from logs import log from rmq import Rmq -rmq = Rmq() - -def execute(body): - msg = body.decode('utf-8') - log.info(f"incoming: {msg}") - - failed = False - - old_stdout = sys.stdout - old_stderr = sys.stderr - redirected_output = sys.stdout = io.StringIO() - redirected_error = sys.stderr = io.StringIO() - snek_msg = json.loads(msg) - snekid = snek_msg['snekid'] - snekcode = snek_msg['message'].strip() - - try: - exec(snekcode) - - except Exception as e: - failed = str(e) - - finally: - sys.stdout = old_stdout - sys.stderr = old_stderr - - if failed: - result = failed.strip() - log.debug(f"this was captured via exception: {result}") - - result_err = redirected_error.getvalue().strip() - result_ok = redirected_output.getvalue().strip() - - if result_err: - log.debug(f"this was captured via stderr: {result_err}") - result = result_err - if result_ok: - result = result_ok - - log.info(f"outgoing: {result}") - - rmq.publish(result, - queue=snekid, - routingkey=snekid, - exchange=snekid) - exit(0) - -def stopwatch(process): - log.debug(f"10 second timer started for process {process.pid}") - for _ in range(10): - time.sleep(1) - if not process.is_alive(): - log.debug(f"Clean exit on process {process.pid}") - exit(0) - - process.terminate() - log.debug(f"Terminated process {process.pid} forcefully") - -def message_handler(ch, method, properties, body, thread_ws=None): - p = multiprocessing.Process(target=execute, args=(body,)) - p.daemon = True - p.start() - - t = threading.Thread(target=stopwatch, args=(p,)) - t.daemon = True - t.start() - - ch.basic_ack(delivery_tag=method.delivery_tag) +class Snekbox(object): + env = { + 'PATH': '/snekbox/.venv/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + 'LANG': 'en_US.UTF-8', + 'PYTHON_VERSION': '3.6.5', + 'PYTHON_PIP_VERSION': '10.0.1', + 'PYTHONDONTWRITEBYTECODE': '1', + } + + def python3(self, cmd): + args = ["nsjail", "-Mo", + "--rlimit_as", "700", + "--chroot", "/", + "-E", "LANG=en_US.UTF-8", + "-R/usr", "-R/lib", "-R/lib64", + "--user", "nobody", + "--group", "nogroup", + "--time_limit", "2", + "--disable_proc", + "--iface_no_lo", + "--quiet", "--", "/usr/local/bin/python3.6", "-ISq", "-c", cmd] + + proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=self.env, + universal_newlines=True) + + stdout, stderr = proc.communicate() + if proc.returncode == 0: + output = stdout + elif proc.returncode == 1: + try: + output = stderr.split('\n')[-2] + except IndexError: + output = '' + elif proc.returncode == 109: + output = 'timed out or memory limit exceeded' + else: + output = 'unknown error' + return output + + def execute(self, body): + msg = body.decode('utf-8') + log.info(f"incoming: {msg}") + result = "" + snek_msg = json.loads(msg) + snekid = snek_msg['snekid'] + snekcode = snek_msg['message'].strip() + + result = self.python3(snekcode) + + log.info(f"outgoing: {result}") + + rmq.publish(result, + queue=snekid, + routingkey=snekid, + exchange=snekid) + exit(0) + + def stopwatch(self, process): + log.debug(f"10 second timer started for process {process.pid}") + for _ in range(10): + time.sleep(1) + if not process.is_alive(): + log.debug(f"Clean exit on process {process.pid}") + exit(0) + + process.terminate() + log.debug(f"Terminated process {process.pid} forcefully") + + def message_handler(self, ch, method, properties, body, thread_ws=None): + p = multiprocessing.Process(target=self.execute, args=(body,)) + p.daemon = True + p.start() + t = threading.Thread(target=self.stopwatch, args=(p,)) + t.daemon = True + t.start() + + ch.basic_ack(delivery_tag=method.delivery_tag) if __name__ == '__main__': - rmq.consume(callback=message_handler) + try: + rmq = Rmq() + snkbx = Snekbox() + rmq.consume(callback=snkbx.message_handler) + except KeyboardInterrupt: + print("Exited") + exit(0) |