158 lines
5 KiB
Python
158 lines
5 KiB
Python
|
import logging
|
||
|
import subprocess
|
||
|
import os
|
||
|
from abc import ABCMeta
|
||
|
from typing import List, Dict
|
||
|
|
||
|
import config
|
||
|
from models import JobVerdict
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
logger.setLevel(logging.INFO)
|
||
|
logging.info('Starting up')
|
||
|
|
||
|
|
||
|
class Language(metaclass=ABCMeta):
|
||
|
@classmethod
|
||
|
def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str:
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
@classmethod
|
||
|
def get_command(cls, workdir: str, executable_name: str) -> List[str]:
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_files(cls, workdir: str, executable_name: str):
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_file_prefixes(cls, workdir: str, executable_name: str):
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
|
||
|
class CXX(Language):
|
||
|
@classmethod
|
||
|
def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str:
|
||
|
source_file_path = os.path.join(workdir, 'source.cpp')
|
||
|
with open(source_file_path, 'wb') as source_file:
|
||
|
source_file.write(source_code.encode('utf-8'))
|
||
|
|
||
|
executable_file_path = os.path.join(workdir, executable_name)
|
||
|
try:
|
||
|
subprocess.check_call(['g++', '--std=c++1y', '-o', executable_file_path, source_file_path],
|
||
|
timeout=config.COMPILATION_TIME_LIMIT)
|
||
|
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||
|
return None
|
||
|
|
||
|
return executable_name
|
||
|
|
||
|
@classmethod
|
||
|
def get_command(cls, workdir: str, executable_name: str) -> List[str]:
|
||
|
return [os.path.join(workdir, executable_name)]
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_files(cls, workdir: str, executable_name: str):
|
||
|
return []
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_file_prefixes(cls, workdir: str, executable_name: str):
|
||
|
return []
|
||
|
|
||
|
|
||
|
class Python(Language):
|
||
|
language_name = 'python'
|
||
|
interpreter_name = 'python'
|
||
|
|
||
|
@classmethod
|
||
|
def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str:
|
||
|
executable_name += '.py'
|
||
|
executable_path = os.path.join(workdir, executable_name)
|
||
|
with open(executable_path, 'wb') as executable_file:
|
||
|
executable_file.write(source_code.encode('utf-8'))
|
||
|
|
||
|
"""try:
|
||
|
subprocess.check_call([cls.interpreter_name, '-m', 'py_compile', executable_name],
|
||
|
timeout=config.COMPILATION_TIME_LIMIT)
|
||
|
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||
|
return None"""
|
||
|
|
||
|
return executable_name
|
||
|
|
||
|
@classmethod
|
||
|
def get_command(cls, workdir: str, executable_name: str) -> List[str]:
|
||
|
return [os.path.join('/usr/bin', cls.interpreter_name), '-s', '-S', os.path.join(workdir, executable_name)]
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_files(cls, workdir: str, executable_name: str):
|
||
|
return [
|
||
|
'/etc/nsswitch.conf',
|
||
|
'/etc/passwd',
|
||
|
'/dev/urandom', # TODO: come up with random policy
|
||
|
'/tmp',
|
||
|
'/bin/Modules/Setup',
|
||
|
workdir,
|
||
|
os.path.join(workdir, executable_name),
|
||
|
]
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_file_prefixes(cls, workdir: str, executable_name: str):
|
||
|
return []
|
||
|
|
||
|
|
||
|
class Java(Language):
|
||
|
@classmethod
|
||
|
def compile(cls, source_code: str, workdir: str, executable_name: str, time_limit: float = config.COMPILATION_TIME_LIMIT) -> str:
|
||
|
source_file_path = os.path.join(workdir, 'Main.java')
|
||
|
with open(source_file_path, 'wb') as source_file:
|
||
|
source_file.write(source_code.encode('utf-8'))
|
||
|
|
||
|
executable_file_path = os.path.join(workdir, 'Main')
|
||
|
try:
|
||
|
subprocess.check_call(['javac', '-d', workdir, source_file_path],
|
||
|
timeout=config.COMPILATION_TIME_LIMIT)
|
||
|
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||
|
return None
|
||
|
|
||
|
return 'Main'
|
||
|
|
||
|
@classmethod
|
||
|
def get_command(cls, workdir: str, executable_name: str) -> List[str]:
|
||
|
return ['/usr/bin/java', '-XX:-UsePerfData', '-XX:+DisableAttachMechanism', '-Xmx256m', '-Xrs', '-cp',
|
||
|
workdir, executable_name]
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_files(cls, workdir: str, executable_name: str):
|
||
|
return [
|
||
|
'/etc/nsswitch.conf',
|
||
|
'/etc/passwd',
|
||
|
'/tmp',
|
||
|
workdir,
|
||
|
os.path.join(workdir, executable_name + '.class'),
|
||
|
]
|
||
|
|
||
|
@classmethod
|
||
|
def get_allowed_file_prefixes(cls, workdir: str, executable_name: str):
|
||
|
return [
|
||
|
'/etc/java-7-openjdk/',
|
||
|
'/tmp/.java_pid',
|
||
|
'/tmp/',
|
||
|
]
|
||
|
|
||
|
|
||
|
class Python2(Python):
|
||
|
language_name = 'python2'
|
||
|
interpreter_name = 'python2.7'
|
||
|
|
||
|
|
||
|
class Python3(Python):
|
||
|
language_name = 'python3'
|
||
|
interpreter_name = 'python3.5'
|
||
|
|
||
|
|
||
|
languages = {
|
||
|
'cxx': CXX,
|
||
|
'python2': Python2,
|
||
|
'python3': Python3,
|
||
|
'java': Java,
|
||
|
} # type: Dict[str, Language]
|