librectf-rust/judge/languages.py

199 lines
5.4 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]