Merge branch 'dev'
* dev: (195 commits) 修改刷新时间 增加题目页面倒计时的 js 增加比赛倒计时的 api 增加判题帮助 修复判断验证码是否存在的时候,用户不存在导致的报错 记录用户输出 md5 增加反馈链接 add docker start tool rename mq 不用的语言使用不同的系统调用过滤 update java runtime security policy 增加 clone 地址范围限制,否则 Java 无法运行 fix mq run path error 修复语言判断 bug add kill proc 修改codeMirror中代码的样式 修复数据库已有用户problems_statu字段为空造成的问题 fix typo add c/c++ sys call filter --isolate-process true ... Conflicts: judge/judger/settings.py judge/judger_controller/settings.py template/src/oj/contest/contest_problem.html template/src/oj/contest/submissions_list.html
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
FROM ubuntu:14.04
|
||||
MAINTAINER virusdefender<qduliyang@outlook.com>
|
||||
RUN mkdir /var/install/
|
||||
WORKDIR /var/install/
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install software-properties-common python-software-properties
|
||||
RUN add-apt-repository -y ppa:webupd8team/java
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install python gcc g++ rake pkg-config git make autoconf automake libtool python-pip python2.7-mysqldb
|
||||
RUN echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
|
||||
RUN echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections
|
||||
RUN apt-get install -y oracle-java7-installer
|
||||
RUN apt-get -y install libseccomp-dev
|
||||
RUN git clone https://github.com/quark-zju/lrun.git
|
||||
RUN cd lrun && make install
|
||||
RUN mkdir -p /var/judger/run/ && mkdir /var/judger/test_case/ && mkdir /var/judger/code/
|
||||
RUN chmod -R 777 /var/judger/run/
|
||||
WORKDIR /var/judger/code/
|
||||
@@ -1,3 +0,0 @@
|
||||
/usr/bin/docker run -t -i --privileged -v /var/test_case/:/var/judger/test_case/ -v /var/code/:/var/judger/code/ judger /bin/bash
|
||||
|
||||
python judge/judger/run.py -solution_id 1 -max_cpu_time 1 -max_memory 1 -test_case_id 1
|
||||
@@ -1,4 +1,5 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
import json
|
||||
import commands
|
||||
import hashlib
|
||||
@@ -7,9 +8,9 @@ from multiprocessing import Pool
|
||||
from settings import max_running_number, lrun_gid, lrun_uid, judger_workspace
|
||||
from language import languages
|
||||
from result import result
|
||||
from compiler import compile_
|
||||
from judge_exceptions import JudgeClientError, CompileError
|
||||
from judge_exceptions import JudgeClientError
|
||||
from utils import parse_lrun_output
|
||||
from logger import logger
|
||||
|
||||
|
||||
# 下面这个函数作为代理访问实例变量,否则Python2会报错,是Python2的已知问题
|
||||
@@ -61,6 +62,8 @@ class JudgeClient(object):
|
||||
" --max-real-time " + str(self._max_real_time / 1000.0 * 2) + \
|
||||
" --max-memory " + str(self._max_memory * 1000 * 1000) + \
|
||||
" --network false" + \
|
||||
" --syscalls '" + self._language["syscalls"] + "'" + \
|
||||
" --max-nprocess 20" + \
|
||||
" --uid " + str(lrun_uid) + \
|
||||
" --gid " + str(lrun_gid)
|
||||
|
||||
@@ -82,6 +85,8 @@ class JudgeClient(object):
|
||||
# 倒序找到MEMORY的位置
|
||||
output_start = output.rfind("MEMORY")
|
||||
if output_start == -1:
|
||||
logger.error("Lrun result parse error")
|
||||
logger.error(output)
|
||||
raise JudgeClientError("Lrun result parse error")
|
||||
# 如果不是0,说明lrun输出前面有输出,也就是程序的stderr有内容
|
||||
if output_start != 0:
|
||||
@@ -92,26 +97,35 @@ class JudgeClient(object):
|
||||
return error, parse_lrun_output(output)
|
||||
|
||||
def _compare_output(self, test_case_id):
|
||||
test_case_md5 = self._test_case_info["test_cases"][str(test_case_id)]["output_md5"]
|
||||
test_case_config = self._test_case_info["test_cases"][str(test_case_id)]
|
||||
output_path = judger_workspace + str(test_case_id) + ".out"
|
||||
|
||||
try:
|
||||
f = open(output_path, "rb")
|
||||
except IOError:
|
||||
# 文件不存在等引发的异常 返回结果错误
|
||||
return False
|
||||
return "", False
|
||||
|
||||
# 计算输出文件的md5 和之前测试用例文件的md5进行比较
|
||||
md5 = hashlib.md5()
|
||||
while True:
|
||||
data = f.read(2 ** 8)
|
||||
if not data:
|
||||
break
|
||||
md5.update(data)
|
||||
if "striped_output_md5" not in test_case_config:
|
||||
# 计算输出文件的md5 和之前测试用例文件的md5进行比较
|
||||
# 兼容之前没有striped_output_md5的测试用例
|
||||
# 现在比较的是完整的文件
|
||||
md5 = hashlib.md5()
|
||||
while True:
|
||||
data = f.read(2 ** 8)
|
||||
if not data:
|
||||
break
|
||||
md5.update(data)
|
||||
output_md5 = md5.hexdigest()
|
||||
|
||||
# 对比文件是否一致
|
||||
# todo 去除最后的空行
|
||||
return md5.hexdigest() == test_case_md5
|
||||
return output_md5, output_md5 == test_case_config["output_md5"]
|
||||
else:
|
||||
# 这时候需要去除用户输出最后的空格和换行 再去比较md5
|
||||
md5 = hashlib.md5()
|
||||
# 比较和返回去除空格后的md5比较结果
|
||||
md5.update(f.read().rstrip())
|
||||
output_md5 = md5.hexdigest()
|
||||
return output_md5, output_md5 == test_case_config["striped_output_md5"]
|
||||
|
||||
def _judge_one(self, test_case_id):
|
||||
# 运行lrun程序 接收返回值
|
||||
@@ -123,26 +137,30 @@ class JudgeClient(object):
|
||||
|
||||
run_result["test_case_id"] = test_case_id
|
||||
|
||||
# 如果返回值非0 或者信号量不是0 或者程序的stderr有输出 代表非正常结束
|
||||
if run_result["exit_code"] or run_result["term_sig"] or run_result["siginaled"] or error:
|
||||
run_result["result"] = result["runtime_error"]
|
||||
return run_result
|
||||
|
||||
# 代表内存或者时间超过限制了
|
||||
# 代表内存或者时间超过限制了 程序被终止掉 要在runtime error 之前判断
|
||||
if run_result["exceed"]:
|
||||
if run_result["exceed"] == "memory":
|
||||
run_result["result"] = result["memory_limit_exceeded"]
|
||||
elif run_result["exceed"] in ["cpu_time", "real_time"]:
|
||||
run_result["result"] = result["time_limit_exceeded"]
|
||||
else:
|
||||
logger.error("Error exceeded type: " + run_result["exceed"])
|
||||
logger.error(output)
|
||||
raise JudgeClientError("Error exceeded type: " + run_result["exceed"])
|
||||
return run_result
|
||||
|
||||
# 如果返回值非0 或者信号量不是0 或者程序的stderr有输出 代表非正常结束
|
||||
if run_result["exit_code"] or run_result["term_sig"] or run_result["siginaled"] or error:
|
||||
run_result["result"] = result["runtime_error"]
|
||||
return run_result
|
||||
|
||||
# 下面就是代码正常运行了 需要判断代码的输出是否正确
|
||||
if self._compare_output(test_case_id):
|
||||
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
|
||||
|
||||
return run_result
|
||||
|
||||
@@ -160,8 +178,8 @@ class JudgeClient(object):
|
||||
try:
|
||||
results.append(item.get())
|
||||
except Exception as e:
|
||||
# todo logging
|
||||
print e
|
||||
logger.error("system error")
|
||||
logger.error(e)
|
||||
results.append({"result": result["system_error"]})
|
||||
return results
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import commands
|
||||
from settings import lrun_uid, lrun_gid
|
||||
from judge_exceptions import CompileError, JudgeClientError
|
||||
from utils import parse_lrun_output
|
||||
from logger import logger
|
||||
|
||||
|
||||
def compile_(language_item, src_path, exe_path):
|
||||
@@ -22,14 +23,20 @@ def compile_(language_item, src_path, exe_path):
|
||||
output_start = output.rfind("MEMORY")
|
||||
|
||||
if output_start == -1:
|
||||
logger.error("Compiler error")
|
||||
logger.error(output)
|
||||
raise JudgeClientError("Error running compiler in lrun")
|
||||
|
||||
# 返回值不为0 或者 stderr中lrun的输出之前有东西
|
||||
if status or output_start:
|
||||
# 返回值不为 0 或者 stderr 中 lrun 的输出之前有 erro r字符串
|
||||
# 判断 error 字符串的原因是链接的时候可能会有一些不推荐使用的函数的的警告,
|
||||
# 但是 -w 参数并不能关闭链接时的警告
|
||||
if status or "error" in output[0:output_start]:
|
||||
raise CompileError(output[0:output_start])
|
||||
|
||||
parse_result = parse_lrun_output(output)
|
||||
parse_result = parse_lrun_output(output[output_start:])
|
||||
|
||||
if parse_result["exit_code"] or parse_result["term_sig"] or parse_result["siginaled"] or parse_result["exceed"]:
|
||||
logger.error("Compiler error")
|
||||
logger.error(output)
|
||||
raise CompileError("Compile error")
|
||||
return exe_path
|
||||
|
||||
@@ -6,6 +6,7 @@ languages = {
|
||||
"name": "c",
|
||||
"src_name": "main.c",
|
||||
"code": 1,
|
||||
"syscalls": "!execve:k,flock:k,ptrace:k,sync:k,fdatasync:k,fsync:k,msync,sync_file_range:k,syncfs:k,unshare:k,setns:k,clone:k,query_module:k,sysinfo:k,syslog:k,sysfs:k",
|
||||
"compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main",
|
||||
"execute_command": "{exe_path}main"
|
||||
},
|
||||
@@ -13,6 +14,7 @@ languages = {
|
||||
"name": "cpp",
|
||||
"src_name": "main.cpp",
|
||||
"code": 2,
|
||||
"syscalls": "!execve:k,flock:k,ptrace:k,sync:k,fdatasync:k,fsync:k,msync,sync_file_range:k,syncfs:k,unshare:k,setns:k,clone:k,query_module:k,sysinfo:k,syslog:k,sysfs:k",
|
||||
"compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main",
|
||||
"execute_command": "{exe_path}main"
|
||||
},
|
||||
@@ -20,8 +22,9 @@ languages = {
|
||||
"name": "java",
|
||||
"src_name": "Main.java",
|
||||
"code": 3,
|
||||
"syscalls": "!execve:k,flock:k,ptrace:k,sync:k,fdatasync:k,fsync:k,msync,sync_file_range:k,syncfs:k,unshare:k,setns:k,clone[a&268435456==268435456]:k,query_module:k,sysinfo:k,syslog:k,sysfs:k",
|
||||
"compile_command": "javac {src_path} -d {exe_path}",
|
||||
"execute_command": "java -cp {exe_path} Main"
|
||||
"execute_command": "java -cp {exe_path} -Djava.security.manager -Djava.security.policy==policy Main"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
judge/judger/logger.py
Normal file
8
judge/judger/logger.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s',
|
||||
filename='log/judge.log')
|
||||
|
||||
logger = logging
|
||||
@@ -7,9 +7,8 @@ from client import JudgeClient
|
||||
from language import languages
|
||||
from compiler import compile_
|
||||
from result import result
|
||||
from settings import judger_workspace
|
||||
|
||||
from settings import submission_db
|
||||
from settings import judger_workspace, submission_db
|
||||
from logger import logger
|
||||
|
||||
|
||||
# 简单的解析命令行参数
|
||||
@@ -60,7 +59,6 @@ except Exception as e:
|
||||
conn.commit()
|
||||
exit()
|
||||
|
||||
print "Compile successfully"
|
||||
# 运行
|
||||
try:
|
||||
client = JudgeClient(language_code=language_code,
|
||||
@@ -80,16 +78,13 @@ try:
|
||||
judge_result["accepted_answer_time"] = l[-1]["cpu_time"]
|
||||
|
||||
except Exception as e:
|
||||
print e
|
||||
logger.error(e)
|
||||
conn = db_conn()
|
||||
cur = conn.cursor()
|
||||
cur.execute("update submission set result=%s, info=%s where id=%s", (result["system_error"], str(e), submission_id))
|
||||
conn.commit()
|
||||
exit()
|
||||
|
||||
print "Run successfully"
|
||||
print judge_result
|
||||
|
||||
conn = db_conn()
|
||||
cur = conn.cursor()
|
||||
cur.execute("update submission set result=%s, info=%s, accepted_answer_time=%s where id=%s",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
# 单个判题端最多同时运行的程序个数,因为判题端会同时运行多组测试数据,比如一共有5组测试数据
|
||||
# 如果MAX_RUNNING_NUMBER大于等于5,那么这5组数据就会同时进行评测,然后返回结果。
|
||||
# 如果MAX_RUNNING_NUMBER小于5,为3,那么就会同时运行前三组测试数据,然后再运行后两组数据
|
||||
@@ -14,12 +15,10 @@ lrun_gid = 1002
|
||||
# judger工作目录
|
||||
judger_workspace = "/var/judger/"
|
||||
|
||||
|
||||
# 这个是在docker 中访问数据库 ip 不一定和web服务器还有celery的一样
|
||||
submission_db = {
|
||||
"host": "10.172.22.50",#"192.168.42.1",
|
||||
"host": os.environ.get("MYSQL_PORT_3306_TCP_ADDR", "127.0.0.1"),
|
||||
"port": 3306,
|
||||
"db": "oj_submission",
|
||||
"user": "root",
|
||||
"password": "mypwd"
|
||||
"password": os.environ.get("MYSQL_ENV_MYSQL_ROOT_PASSWORD", "root")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
注意:
|
||||
此文件包含 celery 的部分配置,但是 celery 并不是运行在docker 中的,所以本配置文件中的 redis和 MySQL 的地址就应该是
|
||||
运行 redis 和 MySQL 的 docker 容器的地址了。怎么获取这个地址见帮助文档。测试用例的路径和源代码路径同理。
|
||||
"""
|
||||
import os
|
||||
# 这个redis 是 celery 使用的,包括存储队列信息还有部分统计信息
|
||||
redis_config = {
|
||||
"host": "127.0.0.1",
|
||||
"host": os.environ.get("REDIS_PORT_6379_TCP_ADDR"),
|
||||
"port": 6379,
|
||||
"db": 0
|
||||
}
|
||||
@@ -9,8 +15,7 @@ redis_config = {
|
||||
|
||||
# 判题的 docker 容器的配置参数
|
||||
docker_config = {
|
||||
|
||||
"image_name": " 819d3da18dc1",
|
||||
"image_name": "judger",
|
||||
"docker_path": "docker",
|
||||
"shell": True
|
||||
}
|
||||
@@ -19,12 +24,14 @@ docker_config = {
|
||||
# 测试用例的路径,是主机上的实际路径
|
||||
test_case_dir = "/root/test_case/"
|
||||
# 源代码路径,也就是 manage.py 所在的实际路径
|
||||
source_code_dir = "/var/mnt/source/OnlineJudge/"
|
||||
source_code_dir = "/root/qduoj/"
|
||||
# 日志文件夹路径
|
||||
log_dir = "/root/log/"
|
||||
|
||||
|
||||
# 存储提交信息的数据库,是 celery 使用的,与 oj.settings/local_settings 等区分,那是 web 服务器访问的地址
|
||||
submission_db = {
|
||||
"host": "127.0.0.1",
|
||||
"host": os.environ.get("submission_db_host"),
|
||||
"port": 3306,
|
||||
"db": "oj_submission",
|
||||
"user": "root",
|
||||
|
||||
@@ -5,32 +5,34 @@ import MySQLdb
|
||||
import subprocess
|
||||
from ..judger.result import result
|
||||
from ..judger_controller.celery import app
|
||||
from settings import docker_config, source_code_dir, test_case_dir, submission_db, redis_config
|
||||
from settings import docker_config, source_code_dir, test_case_dir, log_dir, submission_db, redis_config
|
||||
|
||||
|
||||
@app.task
|
||||
def judge(submission_id, time_limit, memory_limit, test_case_id):
|
||||
try:
|
||||
command = "%s run -t -i --privileged --rm=true " \
|
||||
command = "%s run --privileged --rm " \
|
||||
"--link mysql " \
|
||||
"-v %s:/var/judger/test_case/ " \
|
||||
"-v %s:/var/judger/code/ " \
|
||||
"-v %s:/var/judger/code/log/ " \
|
||||
"%s " \
|
||||
"python judge/judger/run.py " \
|
||||
"--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % \
|
||||
(docker_config["docker_path"],
|
||||
test_case_dir,
|
||||
source_code_dir,
|
||||
log_dir,
|
||||
docker_config["image_name"],
|
||||
submission_id, str(time_limit), str(memory_limit), test_case_id)
|
||||
subprocess.call(command, shell=docker_config["shell"])
|
||||
except Exception as e:
|
||||
print e
|
||||
conn = MySQLdb.connect(db=submission_db["db"],
|
||||
user=submission_db["user"],
|
||||
passwd=submission_db["password"],
|
||||
host=submission_db["host"],
|
||||
port=submission_db["port"],
|
||||
character="utf8")
|
||||
charset="utf8")
|
||||
|
||||
cur = conn.cursor()
|
||||
cur.execute("update submission set result=%s, info=%s where id=%s",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#include <stdio.h>
|
||||
int main()
|
||||
{
|
||||
int a = 0;
|
||||
int i = 0;
|
||||
for(i = 0; i < 9999999999;i++)
|
||||
{
|
||||
a += i;
|
||||
}
|
||||
printf("%d", a);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
int main()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
# include <stdio.h>
|
||||
int main()
|
||||
{
|
||||
int a, b;
|
||||
scanf("%d %d", &a, &b);
|
||||
printf("%d", a + b);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user