diff --git a/judge/client.py b/judge/client.py index 4d5cda87..9dc0129f 100644 --- a/judge/client.py +++ b/judge/client.py @@ -1,12 +1,13 @@ # coding=utf-8 import os import json -import commands import hashlib import judger +import spj_client + from multiprocessing import Pool -from settings import max_running_number, lrun_gid, lrun_uid, judger_workspace +from settings import max_running_number from language import languages from result import result from judge_exceptions import JudgeClientError @@ -20,7 +21,7 @@ def _run(instance, test_case_id): class JudgeClient(object): - def __init__(self, language_code, exe_path, max_cpu_time, max_memory, test_case_dir, judge_base_path): + def __init__(self, language_code, exe_path, max_cpu_time, max_memory, test_case_dir, judge_base_path, spj_path): """ :param language_code: 语言编号 :param exe_path: 可执行文件路径 @@ -53,11 +54,12 @@ class JudgeClient(object): # 测试用例配置项 self._test_case_info = self._load_test_case_info() self._judge_base_path = judge_base_path + self._spj_path = spj_path def _load_test_case_info(self): # 读取测试用例信息 转换为dict try: - f = open(self._test_case_dir + "info") + f = open(os.path.join(self._test_case_dir, "info")) return json.loads(f.read()) except IOError: raise JudgeClientError("Test case config file not found") @@ -96,11 +98,13 @@ class JudgeClient(object): return output_md5, output_md5 == test_case_config["striped_output_md5"] def _judge_one(self, test_case_id): + in_file = os.path.join(self._test_case_dir, str(test_case_id) + ".in") + out_file = os.path.join(self._judge_base_path, str(test_case_id) + ".out") run_result = judger.run(path=self.execute_command[0], max_cpu_time=self._max_cpu_time, max_memory=self._max_memory, - in_file=os.path.join(self._test_case_dir, str(test_case_id) + ".in"), - out_file=os.path.join(self._judge_base_path, str(test_case_id) + ".out"), + in_file=in_file, + out_file=out_file, args=self.execute_command[1:], env=["PATH=" + os.environ["PATH"]], use_sandbox=self._language["use_sandbox"], @@ -113,12 +117,27 @@ class JudgeClient(object): # 将judger返回的结果标志转换为本系统中使用的 if run_result["flag"] == 0: - output_md5, r = self._compare_output(test_case_id) - if r: - run_result["result"] = result["accepted"] + if self._spj_path is None: + output_md5, r = self._compare_output(test_case_id) + if r: + run_result["result"] = result["accepted"] + else: + run_result["result"] = result["wrong_answer"] + run_result["output_md5"] = output_md5 else: - run_result["result"] = result["wrong_answer"] - run_result["output_md5"] = output_md5 + spj_result = spj_client.spj(path=self._spj_path, max_cpu_time=3 * self._max_cpu_time, + max_memory=3 * self._max_memory, + in_path=in_file, + user_out_path=out_file) + if spj_result["spj_result"] == spj_client.AC: + run_result["result"] = result["accepted"] + elif spj_result["spj_result"] == spj_client.WA: + run_result["result"] = result["wrong_answer"] + else: + run_result["result"] = result["system_error"] + run_result["error"] = "SPJ Crashed, return: %d, signal: %d" % \ + (spj_result["spj_result"], spj_result["signal"]) + elif run_result["flag"] in [1, 2]: run_result["result"] = result["time_limit_exceeded"] elif run_result["flag"] == 3: diff --git a/judge/compiler.py b/judge/compiler.py index f4fc9551..0e952e5c 100644 --- a/judge/compiler.py +++ b/judge/compiler.py @@ -7,8 +7,9 @@ from logger import logger from settings import judger_workspace -def compile_(language_item, src_path, exe_path, judge_base_path): - compile_command = language_item["compile_command"].format(src_path=src_path, exe_path=exe_path).split(" ") +def compile_(language_item, src_path, exe_path, judge_base_path, compile_spj=False): + command_item = "spj_compile_command" if compile_spj else "compile_command" + compile_command = language_item[command_item].format(src_path=src_path, exe_path=exe_path).split(" ") compiler = compile_command[0] compile_args = compile_command[1:] compiler_output_file = os.path.join(judge_base_path, "compiler.out") diff --git a/judge/language.py b/judge/language.py index 044e8efe..3bd4b56f 100644 --- a/judge/language.py +++ b/judge/language.py @@ -7,6 +7,7 @@ languages = { "src_name": "main.c", "code": 1, "compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}/main", + "spj_compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}", "execute_command": "{exe_path}/main", "use_sandbox": True }, @@ -15,6 +16,7 @@ languages = { "src_name": "main.cpp", "code": 2, "compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}/main", + "spj_compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}", "execute_command": "{exe_path}/main", "use_sandbox": True }, diff --git a/judge/runner.py b/judge/runner.py index e023e87a..b7874928 100644 --- a/judge/runner.py +++ b/judge/runner.py @@ -12,7 +12,8 @@ from settings import judger_workspace class JudgeInstanceRunner(object): - def run(self, token, submission_id, language_code, code, time_limit, memory_limit, test_case_id): + def run(self, token, submission_id, language_code, code, time_limit, memory_limit, test_case_id, + spj, spj_language, spj_code, spj_version): language = languages[language_code] host_name = socket.gethostname() judge_base_path = os.path.join(judger_workspace, "run", submission_id) @@ -35,25 +36,50 @@ class JudgeInstanceRunner(object): # 编译 try: - exe_path = compile_(language, src_path, judge_base_path, judge_base_path) + exe_path = compile_(language_item=language, src_path=src_path, + exe_path=judge_base_path, judge_base_path=judge_base_path, compile_spj=False) except Exception as e: shutil.rmtree(judge_base_path, ignore_errors=True) return {"code": 1, "data": {"error": str(e), "server": host_name}} + test_case_dir = os.path.join(judger_workspace, "test_case", test_case_id) + + # SPJ相关 + if spj: + spj_path = os.path.join(test_case_dir, "spj-" + spj_version) + if "spj-" + spj_version not in os.listdir(test_case_dir): + spj_language_item = languages[spj_language] + spj_code_path = os.path.join(test_case_dir, "spj-" + spj_language_item["src_name"]) + + f = open(spj_code_path, "w") + f.write(spj_code.encode("utf8")) + f.close() + + try: + compile_(language_item=languages[spj_language], src_path=spj_code_path, + exe_path=spj_path, + judge_base_path=judge_base_path, compile_spj=True) + except Exception as e: + return {"code": 2, "data": {"error": "SPJ Compile error: " + str(e), "server": host_name}} + else: + spj_path = None + # 运行 try: client = JudgeClient(language_code=language_code, exe_path=exe_path, max_cpu_time=int(time_limit), max_memory=int(memory_limit) * 1024 * 1024, - test_case_dir=judger_workspace + "test_case/" + test_case_id + "/", - judge_base_path=judge_base_path) + test_case_dir=test_case_dir, + judge_base_path=judge_base_path, spj_path=spj_path) judge_result = {"result": result["accepted"], "info": client.run(), "accepted_answer_time": None, "server": host_name} for item in judge_result["info"]: if item["result"] != 0: judge_result["result"] = item["result"] + if item.get("error"): + judge_result["info"] = item["error"] break else: l = sorted(judge_result["info"], key=lambda k: k["cpu_time"]) diff --git a/judge/settings.py b/judge/settings.py index 5d9fa432..3dd1268e 100644 --- a/judge/settings.py +++ b/judge/settings.py @@ -1,16 +1,9 @@ # coding=utf-8 -import os # 单个判题端最多同时运行的程序个数,因为判题端会同时运行多组测试数据,比如一共有5组测试数据 # 如果MAX_RUNNING_NUMBER大于等于5,那么这5组数据就会同时进行评测,然后返回结果。 # 如果MAX_RUNNING_NUMBER小于5,为3,那么就会同时运行前三组测试数据,然后再运行后两组数据 # 这样可以避免同时运行的程序过多导致的cpu占用太高 max_running_number = 10 -# lrun运行用户的uid -lrun_uid = 1001 - -# lrun用户组gid -lrun_gid = 1002 - # judger工作目录 judger_workspace = "/var/judger/" diff --git a/judge/spj_client.py b/judge/spj_client.py new file mode 100644 index 00000000..35adb05a --- /dev/null +++ b/judge/spj_client.py @@ -0,0 +1,26 @@ +# coding=utf-8 +import os +import judger + +WA = 1 +AC = 0 +SPJ_ERROR = -1 + + +def file_exists(path): + return os.path.exists(path) + + +def spj(path, max_cpu_time, max_memory, in_path, user_out_path): + if file_exists(in_path) and file_exists(user_out_path): + result = judger.run(path=path, in_file="/dev/null", out_file="/dev/null", + max_cpu_time=max_cpu_time, max_memory=max_memory, + args=[in_path, user_out_path], env=["PATH=" + os.environ.get("PATH", "")], + use_sandbox=True, use_nobody=True) + if result["signal"] == 0 and result["exit_status"] in [AC, WA, SPJ_ERROR]: + result["spj_result"] = result["exit_status"] + else: + result["spj_result"] = SPJ_ERROR + return result + else: + raise ValueError("in_path or user_out_path does not exist") \ No newline at end of file diff --git a/judge_dispatcher/models.py b/judge_dispatcher/models.py index 3bac9474..15cfa2c8 100644 --- a/judge_dispatcher/models.py +++ b/judge_dispatcher/models.py @@ -41,6 +41,10 @@ class JudgeWaitingQueue(models.Model): memory_limit = models.IntegerField() test_case_id = models.CharField(max_length=40) create_time = models.DateTimeField(auto_now_add=True) + spj = models.BooleanField(default=False) + spj_language = models.IntegerField(blank=True, null=True) + spj_code = models.TextField(blank=True, null=True) + spj_version = models.CharField(max_length=32, blank=True, null=True) class Meta: db_table = "judge_waiting_queue" diff --git a/judge_dispatcher/tasks.py b/judge_dispatcher/tasks.py index 8bdf53fd..f9d016fb 100644 --- a/judge_dispatcher/tasks.py +++ b/judge_dispatcher/tasks.py @@ -20,12 +20,23 @@ logger = logging.getLogger("app_info") class JudgeDispatcher(object): - def __init__(self, submission, time_limit, memory_limit, test_case_id): - self.submission = submission + def _none_to_false(self, value): + # xml rpc不能使用None + if value is None: + return False + else: + return value + + def __init__(self, submission_id, time_limit, memory_limit, test_case_id, spj, spj_language, spj_code, spj_version): + self.submission = Submission.objects.get(id=submission_id) self.time_limit = time_limit self.memory_limit = memory_limit self.test_case_id = test_case_id - self.user = User.objects.get(id=submission.user_id) + self.user = User.objects.get(id=self.submission.user_id) + self.spj = spj + self.spj_language = spj_language + self.spj_code = spj_code + self.spj_version = spj_version def choose_judge_server(self): servers = JudgeServer.objects.filter(workload__lt=100, lock=False, status=True).order_by("-workload") @@ -41,16 +52,21 @@ class JudgeDispatcher(object): # 如果没有合适的判题服务器,就放入等待队列中等待判题 if not judge_server: JudgeWaitingQueue.objects.create(submission_id=self.submission.id, time_limit=self.time_limit, - memory_limit=self.memory_limit, test_case_id=self.test_case_id) + memory_limit=self.memory_limit, test_case_id=self.test_case_id, + spj=self.spj, spj_language=self.spj_language, spj_code=self.spj_code, + spj_version=self.spj_version) return judge_server.use_judge_instance() try: - s = TimeoutServerProxy("http://" + judge_server.ip + ":" + str(judge_server.port), timeout=20) + s = TimeoutServerProxy("http://" + judge_server.ip + ":" + str(judge_server.port), + timeout=30) data = s.run(judge_server.token, self.submission.id, self.submission.language, - self.submission.code, self.time_limit, self.memory_limit, self.test_case_id) + self.submission.code, self.time_limit, self.memory_limit, self.test_case_id, + self.spj, self._none_to_false(self.spj_language), + self._none_to_false(self.spj_code), self._none_to_false(self.spj_version)) # 编译错误 if data["code"] == 1: self.submission.result = result["compile_error"] @@ -71,7 +87,7 @@ class JudgeDispatcher(object): judge_server.release_judge_instance() self.submission.judge_end_time = int(time.time() * 1000) - self.submission.save() + self.submission.save(update_fields=["judge_start_time", "result", "info", "accepted_answer_time", "judge_end_time"]) if self.submission.contest_id: self.update_contest_problem_status() @@ -85,13 +101,15 @@ class JudgeDispatcher(object): from submission.tasks import _judge waiting_submission = waiting_submissions.first() - - submission = Submission.objects.get(id=waiting_submission.submission_id) waiting_submission.delete() - - _judge.delay(submission=submission, time_limit=waiting_submission.time_limit, + _judge.delay(submission_id=waiting_submission.submission_id, + time_limit=waiting_submission.time_limit, memory_limit=waiting_submission.memory_limit, - test_case_id=waiting_submission.test_case_id) + test_case_id=waiting_submission.test_case_id, + spj=waiting_submission.spj, + spj_language=waiting_submission.spj_language, + spj_code=waiting_submission.spj_code, + spj_version=waiting_submission.spj_version) def update_problem_status(self): problem = Problem.objects.get(id=self.submission.problem_id) diff --git a/static/src/js/build.js b/static/src/js/build.js index 21f10c84..1bd9953b 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -27,6 +27,7 @@ pager: "components/pager", editorComponent: "components/editorComponent", testCaseUploader: "components/testCaseUploader", + spj: "components/spj", // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ //富文本编辑器simditor -> editor diff --git a/submission/tasks.py b/submission/tasks.py index 4267841a..34ab63d1 100644 --- a/submission/tasks.py +++ b/submission/tasks.py @@ -5,5 +5,5 @@ from judge_dispatcher.tasks import JudgeDispatcher @shared_task -def _judge(submission, time_limit, memory_limit, test_case_id): - JudgeDispatcher(submission, time_limit, memory_limit, test_case_id).judge() \ No newline at end of file +def _judge(submission_id, time_limit, memory_limit, test_case_id, spj=False, spj_language=None, spj_code=None, spj_version=None): + JudgeDispatcher(submission_id, time_limit, memory_limit, test_case_id, spj, spj_language, spj_code, spj_version).judge() \ No newline at end of file diff --git a/submission/views.py b/submission/views.py index cf14c384..94d62739 100644 --- a/submission/views.py +++ b/submission/views.py @@ -53,7 +53,8 @@ def _submit_code(user, problem_id, language, code): problem_id=problem.id) try: - _judge.delay(submission, problem.time_limit, problem.memory_limit, problem.test_case_id) + _judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id, + problem.spj, problem.spj_language, problem.spj_code, problem.spj_version) except Exception as e: logger.error(e) return error_response(u"提交判题任务失败") @@ -147,7 +148,8 @@ class ContestSubmissionAPIView(APIView): code=data["code"], problem_id=problem.id) try: - _judge.delay(submission, problem.time_limit, problem.memory_limit, problem.test_case_id) + _judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id, + problem.spj. problem.spj_language, problem.spj_code, problem.spj_version) except Exception as e: logger.error(e) return error_response(u"提交判题任务失败") @@ -332,7 +334,8 @@ class SubmissionRejudgeAdminAPIView(APIView): except Problem.DoesNotExist: return error_response(u"题目不存在") try: - _judge.delay(submission, problem.time_limit, problem.memory_limit, problem.test_case_id) + _judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id, + problem.spj. problem.spj_language, problem.spj_code, problem.spj_version) except Exception as e: logger.error(e) return error_response(u"提交判题任务失败")